Font loading

Taming the flash of invisible text without slowing the page

Every `@font-face` declaration needs `font-display: swap`. Without it, the browser defaults to three seconds of invisible text. ## The performance pipeline ### 1. Preconnect to the font origin ### 2. Preload critical fonts Preload _only_ above-the-fold fonts. The `crossorigin` attribute is required even for same-origin fonts. Omit it and the browser downloads the font twice. ### 3. Choose a `font-display` strategy | Value | Behavior | Use case | | ---------- | --------------------------------------------------------------- | -------------------------------------- | | `auto` | Browser decides. Usually 3s invisible text (FOIT) | Never use explicitly | | `swap` | Show fallback immediately, swap when loaded (FOUT) | Default for most fonts | | `fallback` | Short block (~100ms), short swap (~3s), then permanent fallback | Non-critical fonts | | `optional` | Brief block; use cached font or permanently fall back | Slow networks, progressive enhancement | `swap` is the right default. ## Taming the flash (FOUT) `font-display: swap` trades invisible text for a visible flash when the webfont arrives. Tune the fallback to match the webfont's metrics with `size-adjust`, `ascent-override`, and `descent-override`: Fontaine and next/font automate this calculation. ## Common mistakes **No `font-display` at all.** Defaults to `auto`, three seconds of invisible text. **Preloading every weight.** Preload only above-the-fold weights. **Loading weights you don't use.** If the design uses 400 and 600, don't load 300, 500, 700, 800, 900. **Google Fonts without preconnect.** Preconnect to both `fonts.googleapis.com` and `fonts.gstatic.com`, or self-host. ## Font subsetting **`unicode-range` subsetting** tells the browser to download a font file only when the page uses characters in that range: Google Fonts does this automatically, splitting each font into language-specific slices. Self-hosting? Use **glyphhanger** or **subfont** to generate subsets matching the characters you use. Skip subsetting for variable fonts where it breaks axis interpolation, or sites with user-generated content in unpredictable scripts.