Frontend Development4 min read

Web Fonts & Typography

Optimize web fonts: self-hosting, font subsetting, font-display strategies, variable fonts, and achieving consistent cross-browser typography.

typographyfontsperformancecss

Web fonts significantly impact performance and visual consistency. Best practice is self-hosting fonts (avoiding third-party DNS lookups), subsetting to include only needed characters, using `font-display: swap` to prevent FOIT, and leveraging variable fonts to reduce HTTP requests.

Self-host all fonts - third-party font services add latency and privacy concerns. Subset fonts to the character sets you use. Use variable fonts (one file, infinite weights). Preload critical fonts. Use `font-display: swap` as a default.

Workflow

  1. Self-host fonts correctlySelf-hosting eliminates third-party DNS lookups and gives you control over caching.
  2. Subset fonts to reduce file sizeFull font files contain all Unicode characters; you likely only need Latin characters, saving 80%+ on file size.
  3. Use variable fontsA single variable font file replaces multiple static weight/style files, reducing requests and size.
  4. Implement font-display strategyWithout `font-display`, browsers may show invisible text (FOIT) while the font loads.
  5. Establish a type scaleConsistent typography creates visual hierarchy and reduces decision fatigue.

Output Format

Self-hosted WOFF2, preloaded in HTML head, font subsetted for character range, variable font with weight range, `font-display: swap` + size-adjusted fallback, type scale as CSS custom properties.

Examples

Font face: `@font-face { font-family: "Geist"; src: url("/fonts/geist-var.woff2") format("woff2-variations"); font-weight: 100 900; font-display: swap; } @font-face { font-family: "Geist-fallback"; src: local("Arial"); size-adjust: 105%; ascent-override: 90%; descent-override: 22%; }`
Fluid type scale: `html { font-size: clamp(15px, 0.75vw + 13px, 18px); } h1 { font-size: clamp(2rem, 4vw + 1rem, 4rem); }`
Unicode-range subset: `@font-face { font-family: "NotoSans"; src: url("/fonts/noto-latin.woff2") format("woff2"); unicode-range: U+0000-00FF; } @font-face { font-family: "NotoSans"; src: url("/fonts/noto-cjk.woff2") format("woff2"); unicode-range: U+4E00-9FFF; }`

Edge Cases & Gotchas

  • iOS Safari and font-display: iOS Safari has partial support for `font-display: optional`; test on real iOS devices
  • Emoji fonts: emoji rendering depends on OS emoji font (Apple Color Emoji, Noto Color Emoji); cannot subset or replace without significant complexity
  • GDPR and Google Fonts: embedding Google Fonts URLs (fonts.googleapis.com) in HTML triggers cross-site requests - self-host to avoid GDPR compliance issues in the EU

When to Break These Rules

  • For marketing landing pages where brand font appearance is critical and speed matters less, `font-display: block` with a very short timeout ensures the brand font is always shown, avoiding fallback flashes.

Reference Files

  • Font-display: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display
  • Fontsource: https://fontsource.org/
  • Google Webfonts Helper: https://gwfh.mranftl.com/
  • Variable fonts: https://web.dev/articles/variable-fonts

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.