Font Rendering: FOUT vs FOIT, font-display, and Metric Overrides
- Last updated
- Prerequisites:
- CSS @font-face rules
- Browser Critical Rendering Path
- browser
- fonts
- fout
- foit
- font-display
- performance
- cls
Read at your depth
The practical view
Flash of Invisible Text (FOIT) happens when the browser hides text while downloading a web font (default `font-display: block` with a 3-second invisible timeout). Flash of Unstyled Text (FOUT) happens when the browser immediately renders a fallback system font and swaps to the custom font once loaded (`font-display: swap`). Use `font-display: optional` for the fastest performance without layout shifts.
The same idea in other frameworks
`next/font` generating zero-CLS fallback `@font-face` metric overrides.Angular inline critical fonts with `font-display: swap`.Nuxt `@nuxt/fonts` generating matching fallback metric rules.SvelteKit with preload link tags and fallback `size-adjust` styles.Legacy vs modern
Default FOIT Blocking vs Metric-Overridden FOUT Swap
Default fonts leave text invisible for seconds; metric overrides allow instant reading with zero layout shift on swap.
@font-face { font-family: 'Brand'; src: url('font.woff2'); } /* ❌ Invisible text for 3s */font-display: swap; size-adjust: 102%; ascent-override: 95%; // ✅ NEWInterview gotchas
Context
Interviewers test your precision regarding Core Web Vitals optimization and low-network conditions.
The mechanical answer
`font-display: swap` gives a 0ms block period and an infinite swap period: the fallback font renders immediately and will *always* swap when the web font downloads (even if it takes 10 seconds). `font-display: optional` gives a tiny 100ms block period and a **0ms swap period**: if the web font is already in cache, it uses it; if not, it uses the fallback font for the entire life of the page and caches the web font for subsequent page loads, guaranteeing **zero layout shift**.
Trap
`font-display: optional` can result in the web font not displaying on the first visit if the network roundtrip exceeds ~100ms.
Context
This tests knowledge of CSS font metric calculations.
The mechanical answer
Fallback fonts (e.g. Arial) and custom web fonts (e.g. Inter) possess different bounding boxes and character advance widths. When `font-display: swap` swaps the font, the line height and paragraph wrapping change, shifting elements below it (causing CLS). `size-adjust` scales the fallback font's glyph glyphs proportionally, and `ascent-override` / `descent-override` match the exact vertical line heights, making the fallback occupy the exact same pixel dimensions as the custom font.
Trap
Metric overrides must be applied to the *fallback* `@font-face` definition (using `src: local(...)`), not to the custom font definition itself.