diff --git a/content/features/upcoming features.md b/content/features/upcoming features.md
index d7acd2a..676bb71 100644
--- a/content/features/upcoming features.md	
+++ b/content/features/upcoming features.md	
@@ -4,6 +4,7 @@ draft: true
 
 ## high priority
 
+- local fonts
 - images in same folder are broken on shortest path mode
 - https://help.obsidian.md/Editing+and+formatting/Tags#Nested+tags nested tags?? and big tag listing
 - watch mode for config/source code
diff --git a/package-lock.json b/package-lock.json
index bd6566f..7bb72bc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
         "hast-util-to-string": "^2.0.0",
         "is-absolute-url": "^4.0.1",
         "js-yaml": "^4.1.0",
+        "lightningcss": "^1.21.5",
         "mdast-util-find-and-replace": "^2.2.2",
         "mdast-util-to-string": "^3.2.0",
         "micromorph": "^0.4.5",
@@ -2378,6 +2379,17 @@
         "node": ">=6"
       }
     },
+    "node_modules/detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/diff": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
@@ -3384,6 +3396,183 @@
         "node": ">=6"
       }
     },
+    "node_modules/lightningcss": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.21.5.tgz",
+      "integrity": "sha512-/pEUPeih2EwIx9n4T82aOG6CInN83tl/mWlw6B5gWLf36UplQi1L+5p3FUHsdt4fXVfOkkh9KIaM3owoq7ss8A==",
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "lightningcss-darwin-arm64": "1.21.5",
+        "lightningcss-darwin-x64": "1.21.5",
+        "lightningcss-linux-arm-gnueabihf": "1.21.5",
+        "lightningcss-linux-arm64-gnu": "1.21.5",
+        "lightningcss-linux-arm64-musl": "1.21.5",
+        "lightningcss-linux-x64-gnu": "1.21.5",
+        "lightningcss-linux-x64-musl": "1.21.5",
+        "lightningcss-win32-x64-msvc": "1.21.5"
+      }
+    },
+    "node_modules/lightningcss-darwin-arm64": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.21.5.tgz",
+      "integrity": "sha512-z05hyLX85WY0UfhkFUOrWEFqD69lpVAmgl3aDzMKlIZJGygbhbegqb4PV8qfUrKKNBauut/qVNPKZglhTaDDxA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-darwin-x64": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.21.5.tgz",
+      "integrity": "sha512-MSJhmej/U9MrdPxDk7+FWhO8+UqVoZUHG4VvKT5RQ4RJtqtANTiWiI97LvoVNMtdMnHaKs1Pkji6wHUFxjJsHQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm-gnueabihf": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.21.5.tgz",
+      "integrity": "sha512-xN6+5/JsMrbZHL1lPl+MiNJ3Xza12ueBKPepiyDCFQzlhFRTj7D0LG+cfNTzPBTO8KcYQynLpl1iBB8LGp3Xtw==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-gnu": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.21.5.tgz",
+      "integrity": "sha512-KfzFNhC4XTbmG3ma/xcTs/IhCwieW89XALIusKmnV0N618ZDXEB0XjWOYQRCXeK9mfqPdbTBpurEHV/XZtkniQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-musl": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.21.5.tgz",
+      "integrity": "sha512-bc0GytQO5Mn9QM6szaZ+31fQHNdidgpM1sSCwzPItz8hg3wOvKl8039rU0veMJV3ZgC9z0ypNRceLrSHeRHmXw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-gnu": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.21.5.tgz",
+      "integrity": "sha512-JwMbgypPQgc2kW2av3OwzZ8cbrEuIiDiXPJdXRE6aVxu67yHauJawQLqJKTGUhiAhy6iLDG8Wg0a3/ziL+m+Kw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-musl": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.21.5.tgz",
+      "integrity": "sha512-Ib8b6IQ/OR/VrPU6YBgy4T3QnuHY7DUa95O+nz+cwrTkMSN6fuHcTcIaz4t8TJ6HI5pl3uxUOZjmtls2pyQWow==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-win32-x64-msvc": {
+      "version": "1.21.5",
+      "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.21.5.tgz",
+      "integrity": "sha512-A8cSi8lUpBeVmoF+DqqW7cd0FemDbCuKr490IXdjyeI+KL8adpSKUs8tcqO0OXPh1EoDqK7JNkD/dELmd4Iz5g==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
     "node_modules/longest-streak": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
diff --git a/package.json b/package.json
index 8ece9eb..cfd1d15 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
     "hast-util-to-string": "^2.0.0",
     "is-absolute-url": "^4.0.1",
     "js-yaml": "^4.1.0",
+    "lightningcss": "^1.21.5",
     "mdast-util-find-and-replace": "^2.2.2",
     "mdast-util-to-string": "^3.2.0",
     "micromorph": "^0.4.5",
diff --git a/quartz.config.ts b/quartz.config.ts
index 5378a8e..211fa93 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -95,7 +95,7 @@ const config: QuartzConfig = {
     filters: [Plugin.RemoveDrafts()],
     emitters: [
       Plugin.AliasRedirects(),
-      Plugin.ComponentResources(),
+      Plugin.ComponentResources({ fontOrigin: "googleFonts" }),
       Plugin.ContentPage({
         ...sharedPageComponents,
         ...contentPageLayout,
diff --git a/quartz/bootstrap-cli.mjs b/quartz/bootstrap-cli.mjs
index 1f853a2..bc5df4e 100755
--- a/quartz/bootstrap-cli.mjs
+++ b/quartz/bootstrap-cli.mjs
@@ -11,6 +11,7 @@ import { intro, isCancel, outro, select, text } from "@clack/prompts"
 import { rimraf } from "rimraf"
 import prettyBytes from "pretty-bytes"
 import { spawnSync } from "child_process"
+import { transform } from "lightningcss"
 
 const UPSTREAM_NAME = "upstream"
 const QUARTZ_SOURCE_BRANCH = "v4-alpha"
@@ -302,6 +303,7 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started.
         plugins: [
           sassPlugin({
             type: "css-text",
+            cssImports: true,
           }),
           {
             name: "inline-script-loader",
diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts
index bc1d4ab..72a8841 100644
--- a/quartz/plugins/emitters/componentResources.ts
+++ b/quartz/plugins/emitters/componentResources.ts
@@ -1,5 +1,5 @@
 import { FilePath, ServerSlug } from "../../path"
-import { PluginTypes, QuartzEmitterPlugin } from "../types"
+import { QuartzEmitterPlugin } from "../types"
 
 // @ts-ignore
 import spaRouterScript from "../../components/scripts/spa.inline"
@@ -13,6 +13,7 @@ import { BuildCtx } from "../../ctx"
 import { StaticResources } from "../../resources"
 import { QuartzComponent } from "../../components/types"
 import { googleFontHref, joinStyles } from "../../theme"
+import { transform } from "lightningcss"
 
 type ComponentResources = {
   css: string[]
@@ -67,7 +68,6 @@ function addGlobalPageResources(
 ) {
   const cfg = ctx.cfg.configuration
   const reloadScript = ctx.argv.serve
-  staticResources.css.push(googleFontHref(cfg.theme))
 
   // popovers
   if (cfg.enablePopovers) {
@@ -120,35 +120,61 @@ function addGlobalPageResources(
   }
 }
 
-export const ComponentResources: QuartzEmitterPlugin = () => ({
-  name: "ComponentResources",
-  getQuartzComponents() {
-    return []
-  },
-  async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
-    // component specific scripts and styles
-    const componentResources = getComponentResources(ctx)
-    // important that this goes *after* component scripts
-    // as the "nav" event gets triggered here and we should make sure
-    // that everyone else had the chance to register a listener for it
-    addGlobalPageResources(ctx, resources, componentResources)
-    const fps = await Promise.all([
-      emit({
-        slug: "index" as ServerSlug,
-        ext: ".css",
-        content: joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css),
-      }),
-      emit({
-        slug: "prescript" as ServerSlug,
-        ext: ".js",
-        content: joinScripts(componentResources.beforeDOMLoaded),
-      }),
-      emit({
-        slug: "postscript" as ServerSlug,
-        ext: ".js",
-        content: joinScripts(componentResources.afterDOMLoaded),
-      }),
-    ])
-    return fps
-  },
-})
+interface Options {
+  fontOrigin: "googleFonts" | "local"
+}
+
+const defaultOptions: Options = {
+  fontOrigin: "googleFonts",
+}
+
+export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<Options>) => {
+  const { fontOrigin } = { ...defaultOptions, ...opts }
+  return {
+    name: "ComponentResources",
+    getQuartzComponents() {
+      return []
+    },
+    async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
+      // component specific scripts and styles
+      const componentResources = getComponentResources(ctx)
+      // important that this goes *after* component scripts
+      // as the "nav" event gets triggered here and we should make sure
+      // that everyone else had the chance to register a listener for it
+
+      if (fontOrigin === "googleFonts") {
+        resources.css.push(googleFontHref(ctx.cfg.configuration.theme))
+      } else if (fontOrigin === "local") {
+        // let the user do it themselves in css
+      }
+
+      addGlobalPageResources(ctx, resources, componentResources)
+
+      const stylesheet = joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css)
+      const prescript = joinScripts(componentResources.beforeDOMLoaded)
+      const postscript = joinScripts(componentResources.afterDOMLoaded)
+      const fps = await Promise.all([
+        emit({
+          slug: "index" as ServerSlug,
+          ext: ".css",
+          content: transform({
+            filename: "index.css",
+            code: Buffer.from(stylesheet),
+            minify: true
+          }).code.toString(),
+        }),
+        emit({
+          slug: "prescript" as ServerSlug,
+          ext: ".js",
+          content: prescript,
+        }),
+        emit({
+          slug: "postscript" as ServerSlug,
+          ext: ".js",
+          content: postscript,
+        }),
+      ])
+      return fps
+    },
+  }
+}
diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss
index ee39b47..fd94a1a 100644
--- a/quartz/styles/base.scss
+++ b/quartz/styles/base.scss
@@ -1,6 +1,6 @@
+@use "./custom.scss";
 @use "./syntax.scss";
 @use "./callouts.scss";
-@use "./custom.scss";
 @use "./variables.scss" as *;
 
 html {
diff --git a/quartz/theme.ts b/quartz/theme.ts
index 2860e2c..b01bfdc 100644
--- a/quartz/theme.ts
+++ b/quartz/theme.ts
@@ -24,13 +24,17 @@ export interface Theme {
 const DEFAULT_SANS_SERIF =
   '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif'
 const DEFAULT_MONO = "ui-monospace, SFMono-Regular, SF Mono, Menlo, monospace"
+
 export function googleFontHref(theme: Theme) {
   const { code, header, body } = theme.typography
   return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap`
 }
 
 export function joinStyles(theme: Theme, ...stylesheet: string[]) {
-  return `:root {
+  return `
+${stylesheet.join("\n\n")}
+
+:root {
   --light: ${theme.colors.lightMode.light};
   --lightgray: ${theme.colors.lightMode.lightgray};
   --gray: ${theme.colors.lightMode.gray};
@@ -55,6 +59,6 @@ export function joinStyles(theme: Theme, ...stylesheet: string[]) {
   --tertiary: ${theme.colors.darkMode.tertiary};
   --highlight: ${theme.colors.darkMode.highlight};
 }
+`
 
-${stylesheet.join("\n\n")}`
 }