diff --git a/astro.config.mjs b/astro.config.mjs index a2e204b..bbbbdba 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -106,6 +106,14 @@ export default defineConfig({ }, vite: { plugins: [tailwindcss()], + build: { + // Font files are always emitted as files (src/components/Fonts.astro). + // Vite would otherwise inline those under 4 KB, the smaller Unicode + // subsets of Geist, into the stylesheet as base64, and every page would + // download subsets it never uses before its first paint. Anything else + // keeps the default limit. + assetsInlineLimit: (file) => (/\.woff2?$/.test(file) ? false : undefined), + }, }, build: { inlineStylesheets: 'auto', diff --git a/src/components/Fonts.astro b/src/components/Fonts.astro new file mode 100644 index 0000000..7ca1dbe --- /dev/null +++ b/src/components/Fonts.astro @@ -0,0 +1,34 @@ +--- +// The site's fonts, Geist and Geist Mono, served from this site. They used to +// come from Google Fonts, whose stylesheet blocks the first paint until it has +// loaded; where Google cannot be reached, as in mainland China without a +// proxy, a page then stayed blank for about 30 seconds. +// +// The files are the Fontsource builds of the Google Fonts releases, bundled by +// Vite under hashed names. As on Google Fonts, each weight is split into +// subsets by unicode-range, so a browser downloads only the subsets a page +// uses (Latin, for these pages), and every face uses font-display: swap. +// Chinese text is set in the system's own font (--font-sans in global.css), so +// no CJK font is downloaded. Until Geist has loaded, text is set in a local +// font adjusted to Geist's metrics ("Geist Fallback" in global.css), so the +// swap does not move the layout. +// +// The Latin files of the weights on every page are preloaded: 400 for text, +// 500 for navigation and buttons, 600 for headings, and Geist Mono 400 for the +// labels above them. The Open Graph images are drawn from the same packages +// (src/lib/og.ts). +import "@fontsource/geist/400.css"; +import "@fontsource/geist/500.css"; +import "@fontsource/geist/600.css"; +import "@fontsource/geist/700.css"; +import "@fontsource/geist-mono/400.css"; +import "@fontsource/geist-mono/500.css"; +import geist400 from "@fontsource/geist/files/geist-latin-400-normal.woff2?url"; +import geist500 from "@fontsource/geist/files/geist-latin-500-normal.woff2?url"; +import geist600 from "@fontsource/geist/files/geist-latin-600-normal.woff2?url"; +import geistMono400 from "@fontsource/geist-mono/files/geist-mono-latin-400-normal.woff2?url"; + +const preload = [geist400, geist500, geist600, geistMono400]; +--- + +{preload.map((href) => )} diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 45bb836..b5f2f63 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -2,6 +2,7 @@ import "~/styles/global.css"; import { getLang, t } from "~/i18n"; import Seo from "~/components/Seo.astro"; +import Fonts from "~/components/Fonts.astro"; import { ogImageAlt, ogImagePath, type OgCard } from "~/lib/og"; import type { JsonLd } from "~/lib/structured-data"; @@ -47,14 +48,7 @@ const GA_ID = "G-R8LX8NCHCJ"; - - - +