How to font-display specification reference
- Step 1Understand the three periods first — Block period: invisible-but-space-occupying text (FOIT) while waiting. Swap period: fallback shown, web font swapped in if it loads in this window. Failure period: web font abandoned for the session, fallback stays. Every value is a choice of how long block and swap last.
- Step 2Read the value you are auditing in the table — Use the per-value table below to find the block and swap durations for the value in your CSS. `block`: ~3s block, infinite swap. `swap`: 0 block, infinite swap. `fallback`/`optional`: ~100ms block, then a short or zero swap window. `auto`: UA-defined.
- Step 3Get a recommendation from the picker — If you want a value rather than just the reference, open the **Use case** select above, choose Brand-critical / Performance-critical / Balanced, and press **Generate**. The output restates all five values and prints the recommended one plus an `@font-face` skeleton.
- Step 4Confirm the descriptor is in the right place — `font-display` is only valid inside `@font-face`. Verify it is a descriptor in your face rule, not a property on `body`/`h1` (no effect) and not using `var()` (invalid in `@font-face`, silently drops to `auto`).
- Step 5Map the value to real rules — The reference is abstract; the value ships in concrete `@font-face` blocks. Generate those with the [font-face generator](/font-tools/font-face-generator) (its `font-display` dropdown covers all five) or apply Google Fonts' `display=` parameter via the [Google Fonts CSS generator](/font-tools/google-fonts-css-generator).
- Step 6Verify behaviour against the periods — Throttle in DevTools and watch: did the block period hide text as long as the table predicts? Did the swap happen within the swap window? If `auto` behaves differently across browsers, replace it with an explicit value — the spec lets UAs define `auto` freely.
font-display values by timeline phase
How each value sets the block and swap periods, per the CSS Fonts spec. Durations are the spec's guidance values; user agents may differ. 'Infinite swap' means the font is swapped in whenever it eventually loads.
| Value | Block period | Swap period | Net behaviour |
|---|---|---|---|
auto | UA-defined (≈block) | UA-defined | Whatever the browser default is — typically FOIT-leaning |
block | Short, ~3s (FOIT) | Infinite | Invisible up to ~3s, then swaps in even if very late |
swap | Zero / extremely short | Infinite | Fallback immediately (FOUT), always swaps when loaded |
fallback | Extremely short, ~100ms | Short, ~3s | Brief FOIT, fallback, swap only if loaded in the window |
optional | Extremely short, ~100ms | Zero / near-zero | Brief FOIT, fallback; web font only if already available |
Descriptor validity and misuse
Where font-display is valid and what happens when it is not. font-display is an @font-face descriptor, not a CSS property.
| Usage | Valid? | Result |
|---|---|---|
Inside @font-face | Yes | Sets the loading strategy for that face |
On a selector (body { font-display: swap }) | No | Ignored — font-display is not a property of elements |
With var() (font-display: var(--x)) | No | Invalid in @font-face; declaration dropped, reverts to auto |
Omitted from @font-face | Allowed | Defaults to auto (UA-defined behaviour) |
Unknown value (typo, e.g. swp) | No | Invalid declaration ignored; descriptor reverts to auto |
Cookbook
Reference snippets keyed to each value's spec behaviour. The picker emits one value; these show all five in context. Build real blocks with the font-face generator.
auto — defer to the browser default
Exampleauto leaves the block and swap periods to the user agent. On most engines this is FOIT-leaning. Because it is UA-defined, behaviour can differ between browsers and versions — which is why it is the value to avoid when you want predictability.
@font-face {
font-family: "YourFont";
src: url("/fonts/yourfont.woff2") format("woff2");
font-display: auto; /* UA-defined; typically ~block */
}block — long FOIT, infinite swap
Exampleblock sets a ~3s block period (invisible text) and an infinite swap period (the font swaps in whenever it loads). Defensible mainly for icon fonts where a fallback glyph is meaningless.
@font-face {
font-family: "YourIcons";
src: url("/fonts/icons.woff2") format("woff2");
font-display: block; /* ~3s invisible, then swap whenever loaded */
}swap — zero block, infinite swap (FOUT)
Exampleswap collapses the block period to near zero (fallback shown at once) and keeps an infinite swap period (always upgrade to the web font). This is the picker's Brand-critical and Balanced output.
@font-face {
font-family: "YourFont";
src: url("/fonts/yourfont.woff2") format("woff2");
font-display: swap; /* fallback now, always swap later */
}fallback — short block, short swap
Examplefallback uses a ~100ms block period and a ~3s swap period, after which the failure period keeps the fallback for the session. A late-loading font is ignored — useful where a post-3s reflow would be jarring. The picker does not emit this; set it by hand.
@font-face {
font-family: "YourFont";
src: url("/fonts/yourfont.woff2") format("woff2");
font-display: fallback; /* ~100ms FOIT, ~3s to swap, else fallback */
}optional — short block, near-zero swap
Exampleoptional uses a ~100ms block period and an essentially zero swap period: the web font is only used if already available. On a cold visit it usually stays on the fallback for the session. This is the picker's Performance-critical output.
@font-face {
font-family: "YourFont";
src: url("/fonts/yourfont.woff2") format("woff2");
font-display: optional; /* ~100ms FOIT, then fallback unless instant */
}Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
auto is user-agent defined, not specified
UA-definedThe spec deliberately leaves auto to the user agent's font-display timeline. In practice that has meant a block-like FOIT on most engines, but it is not guaranteed and has shifted across versions. Treat auto as 'unpredictable' — if you need the same behaviour everywhere, name an explicit value. This is the most important nuance the spec implies but does not spell out as a fixed duration.
Phase durations are guidance, not guarantees
Implementation-definedThe ~3s block and ~100ms windows are the spec's recommended values, described as 'short' and 'extremely short' rather than exact constants. User agents may use different durations, and some adapt to network conditions. Reason about relative behaviour (zero vs short vs infinite swap), not about exact milliseconds, and verify the real timing under throttling.
The failure period is per-session, not permanent
Session-scopedWhen a font misses its swap window under fallback/optional, the fallback is used for the rest of that page session — but the font typically downloads into the HTTP cache anyway. On the next navigation or revisit, the font is cached and optional will use it immediately. 'Abandoned' means 'not for this session', not 'never'.
font-display on a selector does nothing
Ignoredfont-display is an @font-face descriptor, not a CSS property. body { font-display: swap; } is silently ignored — there is no element-level font-display. Always place it inside the @font-face rule for the face you want to control. The picker's skeleton does this correctly.
var() inside @font-face is invalid
Declaration dropped@font-face does not support var() substitution, so font-display: var(--x); is an invalid declaration that is dropped, reverting the face to auto. To tokenise the value, resolve it at build time and emit the literal. This trips up people trying to drive font-display from a design-token variable.
Unknown / misspelled value reverts to auto
Invalid — ignoredA typo like font-display: swp; is an invalid value; CSS error handling drops the declaration and the face behaves as auto. Because auto is UA-defined FOIT-leaning, a single typo can silently turn a swap strategy into invisible-text behaviour. Lint for the five valid values to catch this.
block on real text trips the invisible-text audit
Audit warningLighthouse's 'Ensure text remains visible during webfont load' audit flags @font-face rules whose font-display risks FOIT — i.e. block and auto. The spec permits these values; the audit reflects the UX consensus that text should not be hidden. Use swap/fallback/optional for text; reserve block for icon faces.
The picker is a generator, not a validator
SupportedThis reference tool recommends and explains values; it does not parse your CSS, validate descriptor placement, or detect a misspelled value. It is generative and text-only — the result badge reads 0 bytes uploaded. Use Stylelint or a CSS linter to validate real rules; use this tool to learn the spec behaviour and get a recommended value.
Frequently asked questions
What are the three font-display timeline periods?
Block period: the initial window where unloaded text renders invisibly (FOIT) but holds its space. Swap period: the window where a fallback is shown and the web font is swapped in if it loads. Failure period: after the swap period, an unloaded font is treated as failed and the fallback is kept for the session. Every value is just a choice of how long the block and swap periods last.
What block and swap periods does each value use?
block: ~3s block, infinite swap. swap: ~0 block, infinite swap. fallback: ~100ms block, ~3s swap. optional: ~100ms block, ~0 swap. auto: user-agent defined (typically block-like). Durations are the spec's guidance values, not exact guarantees — engines may differ.
Where is font-display valid?
Only inside an @font-face rule, as a descriptor. It is not a CSS property, so setting it on body, h1, or any selector does nothing. It also does not accept var() inside @font-face — that is invalid and reverts the face to auto. The picker's skeleton places it correctly inside @font-face.
Why is auto's behaviour unpredictable?
The spec leaves auto to the user agent's own font-display timeline rather than fixing a behaviour. In practice that is a block-like FOIT on most engines today, but it is not guaranteed and has changed across versions. For consistent cross-browser behaviour, name swap, fallback, or optional explicitly.
What happens if I omit font-display from @font-face?
The face defaults to auto, i.e. the user-agent's default timeline (typically FOIT-leaning). Because that risks invisible text, it is best to set an explicit value on every @font-face. Lighthouse will flag omitted/auto rules under its invisible-text audit.
Is the web font gone forever if it misses the swap window?
No — only for that session. Under fallback/optional, missing the swap window means the fallback is used for the rest of the page session, but the font still downloads into the HTTP cache. On the next visit it is cached, so optional will use it immediately. 'Failure period' is per-session, not permanent.
What does the picker recommend, and how does that map to the spec?
Brand-critical → swap (zero block, infinite swap — always show the font). Performance-critical → optional (short block, near-zero swap — fallback unless instant). Balanced → swap. The picker never emits fallback, block, or auto; set those by hand if the spec behaviour you want is one of them.
Can I drive font-display from a CSS variable?
Not live — @font-face descriptors do not support var(), so font-display: var(--x); is invalid and the face reverts to auto. Resolve the token at build time and emit a literal value into the generated @font-face. See the build pipeline guide for the codegen pattern.
What happens if I misspell the value?
CSS drops invalid declarations, so font-display: swp; is ignored and the face behaves as auto — which is FOIT-leaning. A single typo can silently turn a swap strategy into invisible-text behaviour. Lint your CSS to allow only the five valid values (auto, block, swap, fallback, optional).
Why does Lighthouse warn about my block/auto font-display?
Lighthouse's 'Ensure text remains visible during webfont load' audit flags @font-face rules whose font-display risks a flash of invisible text — block and auto. The values are spec-valid; the audit encodes the UX consensus that text should not be hidden. Switch text faces to swap/fallback/optional to clear it.
Does this reference tool validate my CSS?
No. It is a generative explainer and value recommender — it does not parse your stylesheet, check descriptor placement, or catch typos. It runs entirely in the browser (0 bytes uploaded). Use Stylelint or a CSS validator for real rules, and use this tool to learn the per-phase behaviour and get a recommended value.
Where does font-display actually ship in real rules?
Inside @font-face blocks you write or generate. The font-face generator emits complete blocks with a font-display dropdown covering all five values (default swap), and the Google Fonts CSS generator applies the display= parameter to hosted fonts. This reference explains the value; those tools produce the rule.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.