From 17747f1361e878323bd7881f65e62f0b8ae10671 Mon Sep 17 00:00:00 2001 From: Pedro Paulo Vezza Campos Date: Fri, 29 May 2026 08:33:15 -0700 Subject: [PATCH] Self-host fonts and inline global CSS to cut render-blocking Lighthouse flagged two render-blocking requests on the critical path: the cross-origin Google Fonts stylesheet (~750ms) and the first-party global stylesheet (~6KB, ~160ms). Self-host Inter and Playfair Display via Astro's Fonts API. They are now downloaded, subset to latin, and served from /_astro/fonts (bypassing the Worker). The component inlines @font-face CSS and preloads the font bytes instead of waiting on a cross-origin stylesheet, and generates metric-matched fallbacks that also reduce CLS. Inline the small global stylesheet (build.inlineStylesheets: "always") so it no longer emits a render-blocking ; pages are SSR'd per request, so there is no shared-cache benefit lost. Verified in rendered HTML: zero googleapis/gstatic references, zero external stylesheet links, font @font-face/preloads all point to same-origin /_astro/fonts/*.woff2. Co-Authored-By: Claude Opus 4.8 (1M context) --- astro.config.mjs | 30 +++++++++++++++++++++++++++++- src/layouts/Layout.astro | 12 ++++++------ src/styles/global.css | 6 ++++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/astro.config.mjs b/astro.config.mjs index 4e02846..03870d1 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,4 +1,4 @@ -import { defineConfig, sessionDrivers } from "astro/config"; +import { defineConfig, fontProviders, sessionDrivers } from "astro/config"; import cloudflare from "@astrojs/cloudflare"; import tailwindcss from "@tailwindcss/vite"; @@ -13,10 +13,38 @@ export default defineConfig({ // Cloudflare-KV session driver so no (id-less) SESSION KV binding is emitted // into the generated deploy config. session: { driver: sessionDrivers.memory() }, + // Self-host the two webfonts (downloaded + subset at build time, served as + // static /_astro assets by the platform). This removes the render-blocking + // cross-origin Google Fonts stylesheet from the critical path; the + // component instead inlines @font-face CSS and preloads the body font. + fonts: [ + { + provider: fontProviders.google(), + name: "Inter", + cssVariable: "--font-inter", + weights: ["400 800"], + styles: ["normal"], + subsets: ["latin"], + fallbacks: ["system-ui", "sans-serif"], + }, + { + provider: fontProviders.google(), + name: "Playfair Display", + cssVariable: "--font-playfair", + weights: ["700 900"], + styles: ["normal", "italic"], + subsets: ["latin"], + fallbacks: ["Georgia", "serif"], + }, + ], adapter: cloudflare({ // Plain with build-emitted assets — no runtime image service / IMAGES binding. imageService: "passthrough", }), + // Inline the (small, ~6KB) global stylesheet into each page's instead + // of emitting a render-blocking . Pages are SSR'd per request, so there + // is no shared-CSS-cache benefit to give up, and FCP/LCP improve. + build: { inlineStylesheets: "always" }, vite: { plugins: [tailwindcss()], resolve: { diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index a897513..cae8917 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,4 +1,5 @@ --- +import { Font } from "astro:assets"; import "../styles/global.css"; interface Props { @@ -101,12 +102,11 @@ const telemetryConfig = Astro.locals.telemetry?.clientConfig() ?? null; - - - + {/* Self-hosted, served from /_astro — no cross-origin render-blocking + stylesheet. Inter is the body font, so preload it; the serif display + face is above-the-fold too, so preload it as well. */} + + diff --git a/src/styles/global.css b/src/styles/global.css index fb14f49..e2ae44a 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -35,8 +35,10 @@ --color-paper: var(--paper); --color-ink: var(--ink); --color-electric: var(--electric); - --font-serif: "Playfair Display", Georgia, serif; - --font-sans: "Inter", system-ui, sans-serif; + /* Resolve to the self-hosted families registered via Astro's fonts config + (astro.config.mjs). Each var already carries optimized fallback metrics. */ + --font-serif: var(--font-playfair, "Playfair Display", Georgia, serif); + --font-sans: var(--font-inter, "Inter", system-ui, sans-serif); } :root {