Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
34 changes: 34 additions & 0 deletions src/components/Fonts.astro
Original file line number Diff line number Diff line change
@@ -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) => <link rel="preload" href={href} as="font" type="font/woff2" crossorigin />)}
10 changes: 2 additions & 8 deletions src/layouts/Base.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -47,14 +48,7 @@ const GA_ID = "G-R8LX8NCHCJ";
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href={lang === "zh-CN"
? "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&family=Noto+Sans+SC:wght@400;500;600;700&display=swap"
: "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap"}
/>
<Fonts />

<!--
Auto-redirect to the user's preferred locale.
Expand Down
61 changes: 59 additions & 2 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,65 @@
--color-pink-500: #d6a95a; --color-pink-600: #b98a3f; --color-pink-700: #946b31; --color-pink-800: #74542b; --color-pink-900: #5f4526; --color-pink-950: #352413;
--color-fuchsia-300: #e9cb8e; --color-fuchsia-400: #e5c07b; --color-fuchsia-500: #d6a95a;

--font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
/* Geist is self-hosted (src/components/Fonts.astro). Chinese text uses the
system's CJK font: PingFang on macOS and iOS, Microsoft YaHei on Windows,
Noto Sans CJK on Linux and Android. */
--font-sans: "Geist", "Geist Fallback", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--font-mono: "Geist Mono", "Geist Mono Fallback", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---------- Fallback faces ---------- */
/* Until Geist and Geist Mono have loaded, text is set in a local font scaled
to their metrics, so that swapping them in does not reflow the page. For
each weight, size-adjust is the ratio of the widths of the English text of
this site's pages set in Geist and in the local font (Arial for 400 and
500, Arial Bold for 600 and 700, Courier New for Geist Mono); the ascent,
descent and line gap are Geist's, divided by size-adjust. Arial, Liberation
Sans and Arimo share their metrics, as do Courier New, Liberation Mono and
Cousine, so one set of figures serves macOS, Windows and Linux. */
@font-face {
font-family: "Geist Fallback";
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans"), local("Arimo");
font-weight: 100 400;
size-adjust: 102.26%;
ascent-override: 98.28%;
descent-override: 28.85%;
line-gap-override: 0%;
}
@font-face {
font-family: "Geist Fallback";
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans"), local("Arimo");
font-weight: 500;
size-adjust: 104.69%;
ascent-override: 96%;
descent-override: 28.18%;
line-gap-override: 0%;
}
@font-face {
font-family: "Geist Fallback";
src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("LiberationSans-Bold"), local("Arimo Bold"), local("Arimo-Bold");
font-weight: 600;
size-adjust: 99.89%;
ascent-override: 100.61%;
descent-override: 29.53%;
line-gap-override: 0%;
}
@font-face {
font-family: "Geist Fallback";
src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("LiberationSans-Bold"), local("Arimo Bold"), local("Arimo-Bold");
font-weight: 700 900;
size-adjust: 102.13%;
ascent-override: 98.4%;
descent-override: 28.88%;
line-gap-override: 0%;
}
@font-face {
font-family: "Geist Mono Fallback";
src: local("Courier New"), local("CourierNewPSMT"), local("Liberation Mono"), local("LiberationMono"), local("Cousine");
size-adjust: 99.98%;
ascent-override: 100.52%;
descent-override: 29.5%;
line-gap-override: 0%;
}

/* ---------- Base ---------- */
Expand Down
Loading