CSS Fonts Table

PieceWhat it doesField note
font-family stackFallback chainAlways end sans-serif/monospace - the generic IS the fallback
font-display: swapKill the invisible textFallback shows instantly, webfont swaps in - FOIT solved
size-adjustMatch fallback metricsFallback scaled to webfont width - swap stops shifting layout
Variable fontsOne file, all weightsfont-weight: 1-1000 continuous - no 400/700 file pairs
font-optical-sizingDisplay vs text cutAuto per-size axis - display cuts tighter at large sizes
line-height 1.5The frame (again)Unitless inherits as ratio - every font change stays proportional
font shorthand resetsThe silent wipefont: 16px sans resets weight/style/line-height - longhands in components
system-ui stackThe native lookNative font = zero download + perfect rendering - the default-first answer
Reference: the MDN CSS fonts reference. Web font loading has two failure modes and one fix each: invisible text (FOIT) dies with font-display: swap - the fallback shows instantly and the webfont swaps in - and the swap-induced LAYOUT SHIFT dies with size-adjust, which scales the fallback to the webfont's metrics. Variable fonts end the 400/700 file-pair era: one file, continuous weights. Bottom line: the stack always ends with a generic (sans-serif is the fallback, not a style choice), consider the system-ui stack first (zero download, perfect rendering), and remember the font shorthand resets weight and line-height - the backgrounds-table shorthand trap in a typographer's clothing. Related tools: text-wrap table (the lines these fonts form), backgrounds table (the other shorthand-reset trap), and performance table (timing the font swap).

Web font loading has two failure modes and one fix each: invisible text (FOIT - the fallback hidden while the webfont loads) dies with font-display: swap - the fallback shows instantly and the webfont swaps in - and the swap-induced LAYOUT SHIFT dies with size-adjust, which scales the fallback to the webfont's metrics.

Bottom line: the stack always ends with a generic (sans-serif is the fallback, not a style choice), consider the system-ui stack FIRST (zero download, perfect native rendering - the default-first answer), and remember the font shorthand resets weight, style and line-height: the backgrounds shorthand trap in a typographer's clothing.

The honest part: variable fonts end the 400/700 file-pair era - one file carries the continuous weight axis (font-weight: 350 works), often smaller than the two static files it replaces. Optical sizing (auto) then cuts display sizes tighter automatically, like metal type always did.

How to use

  1. Ship the swap pair: font-display: swap plus size-adjust on the @font-face fallback (ascent-override, size-adjust tuned to match metrics) - instant text, minimal shift.
  2. Try the zero-download stack first: font-family: system-ui, sans-serif - the native font renders perfectly and costs nothing; add a webfont only when brand demands it.
  3. One variable file: @font-face { src: url(Inter.woff2) } with font-weight: 100 900 declared - every weight from one cached file instead of pairs per weight.

Frequently asked questions

What is the difference between FOIT and FOUT, and which should I choose?

Two ways font loading can look. FOIT (flash of invisible text): the browser hides the text while the webfont loads - no layout shift, but blank content for up to the block period (bad on slow networks; unreadable content is worse than different-looking content). FOUT (flash of unstyled text): the fallback shows immediately, then swaps when the webfont arrives - content always visible, but metrics differ so the layout shifts at swap. font-display picks: block (FOIT-ish), swap (FOUT, the recommended default), optional (webfont only if cached - near-zero shift, sometimes fallback forever). Choose swap, then fix the shift with size-adjust rather than hiding text.

How does size-address reduce the layout shift that swap causes?

Metric matching. The shift at swap happens because fallback and webfont occupy different widths - every line re-wraps when the real font lands. size-adjust (on a fallback @font-face declaration) scales the fallback glyph metrics - size, ascent, descent - to approximate the webfont's dimensions, so the fallback layout is nearly identical and the swap becomes a texture change instead of a reflow. Tools compute the percentages automatically from font metadata. It is fallback tuning: the fallback never looks right, it just OCCUPIES the right space - which is exactly what swap needs from it.

Why are variable fonts often smaller than two static weights?

Shared outlines. Static fonts duplicate every glyph per weight - Regular and Bold each carry full glyph sets. A variable font stores one master outline per glyph plus a variation axis: the Bold deltas are small adjustments to the Regular outlines. The result: one file covering weight 100-900 frequently ships smaller than the two static files it replaces, with every intermediate weight available (350 for large text, 450 for small - weights static files never offered). The caveat: rendering cost is slightly higher, and design axes beyond weight (width, optical size) grow the file - pick the axes you use and subset the rest.

When is the system-ui stack the right final answer?

More often than brands expect. system-ui renders the OS native font: zero download (no FOIT, no FOUT, no shift - the metrics ARE the platform's), perfect hinting and rendering, and an interface that feels native on every device. Content sites, documentation, dashboards and tools gain speed and lose nothing but brand distinctiveness. The honest case for a webfont: brand identity where typography IS the brand, or display headlines with character. The hybrid is common and correct: system-ui for UI chrome and body text, one webfont for display headlines - the webfont downloads are then small, and their swap shift affects one element, not the page.

Related tools