In CSS, what does the font-display descriptor inside an @font-face rule control, and what is the difference between FOIT and FOUT?
answer
- what to paint while the file downloads
- invisible text versus fallback text
- one means blank, one means restyle
- descriptor lives inside @font-face
- swap opts into the visible change
basics
~20 sfont-display tells the browser what to paint while a web font is still downloading: nothing (FOIT, a flash of invisible text) or the fallback family (FOUT, a flash of unstyled text that is later replaced by the web font).
solid answer
~50 sA font declared with `@font-face` lives in a file that has to be downloaded, so between the moment layout is ready to paint text and the moment the file arrives there is a gap. `font-display` is the author's instruction for what to show during that gap. FOIT — flash of invisible text — means the browser reserves the space but paints no glyphs until the font arrives; the reader sees blank area. FOUT — flash of unstyled text — means the browser paints immediately in the next family from the `font-family` list and repaints in the web font later; content is readable from first paint, but the restyle is visible and usually nudges line breaks. `font-display: swap` opts into FOUT; the default `auto` behaves like `block` in practice, which gives FOIT for the first moments. The descriptor goes inside each `@font-face` block, not on the selector that uses the family.
code
css · 11 lines@font-face {
font-family: "Inter";
src: url("/fonts/inter-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
body {
font-family: "Inter", system-ui, sans-serif;
}go deeper
Know that FOIT means invisible text and FOUT means fallback text that later changes, and that font-display picks between them inside the @font-face block.
Be ready to explain that the descriptor is per face, that the default auto behaves like block, and that swap trades a visible restyle for readable text at first paint.
Show you treat the descriptor as damage control, not a fix: say what actually delays the font, and justify the value you picked from how different the fallback looks and how much text uses the face.
Own the policy across a design system — one documented default per font role, icon fonts handled differently from body text, and a way to stop new @font-face blocks shipping without the descriptor.
## Why the browser needs a policy at all When a stylesheet declares a family with `@font-face`, the glyph outlines live in a separate file that must be fetched over the network. Layout can be ready to paint a paragraph long before that file lands — on a fast connection the gap is tens of milliseconds, on a congested mobile connection it can be several seconds. The browser has to put *something* on the screen during that gap, and `font-display` is how an author says which compromise they prefer. ## FOIT: flash of invisible text With FOIT the browser lays the text out but paints no glyphs. The box is the right size, the surrounding layout is settled, and the reader sees an empty region where the words should be. Nothing is broken — the text simply has not been drawn yet. Browsers do not do this forever: after a timeout they give up and paint a fallback face so the page is not permanently unreadable. FOIT trades *readability* for *visual stability*: the reader waits, but never sees the text change shape. ## FOUT: flash of unstyled text With FOUT the browser paints straight away using the next family in the `font-family` list, or the generic default if none matches, and then repaints in the web font once it arrives. The content is readable from the first paint. The cost is the visible swap: glyph shapes change mid-read, and because two faces almost never share identical metrics — width per character, x-height, line height — the text can reflow when the swap happens. FOUT trades visual stability for readability. ## Where the descriptor goes `font-display` is a *descriptor* inside `@font-face`, not a property you can set on a selector: ```css @font-face { font-family: "Inter"; src: url("/fonts/inter-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; } body { font-family: "Inter", system-ui, sans-serif; } ``` It applies per face, so a family shipped as four separate weight files needs the descriptor in all four `@font-face` blocks. Writing `font-display: swap` in a normal rule such as `body { … }` does nothing at all — a common review finding. ## The five values at a glance - `auto` — leave it to the browser. In practice mainstream browsers treat this like `block`, so an undeclared `font-display` gives you FOIT. - `block` — hide the text briefly, then swap whenever the font arrives. - `swap` — paint the fallback almost immediately and swap whenever the font arrives. The straightforward FOUT choice. - `fallback` — paint the fallback almost immediately, but only accept the swap within a short window; a very late font is ignored for this page load. - `optional` — paint the fallback almost immediately and never swap on this load; the file is still cached for later navigations. ## What font-display does not do It does not make the font arrive faster and it does not change *when* the request starts — the browser still fetches the file only after style resolution finds an element that actually uses the family. It has no effect on families already installed on the device, because there is nothing to download. And it does not remove the restyle that FOUT causes: `swap` is a decision to *show* the swap rather than hide it. If the real problem is that the font is discovered late, changing the descriptor only changes which bad frame the user sees. ## Which one is usually right For body copy, most teams pick FOUT: unreadable text is a worse failure than restyled text, and a reader who has already started the sentence is not blocked. For an icon font the logic inverts — a fallback family has no matching glyphs, so `swap` renders letters or tofu boxes where icons should be, and briefly showing nothing is the lesser evil. The judgment depends on how different the fallback looks and how much of the page the face covers.
- If an @font-face rule declares no font-display at all, what happens?The value defaults to `auto`, which hands the decision to the browser. In practice mainstream browsers treat `auto` like `block`, so you get a short period of invisible text followed by the font swapping in whenever it arrives. If you want readable text immediately you have to say `swap` explicitly.
- Does font-display change when the font file is requested?No. The request is triggered by style resolution finding an element that uses the family, and `font-display` only governs what is painted while that request is in flight. A late-discovered font is still late; the descriptor just chooses whether the reader stares at blank space or at the fallback during the wait.
- Why do most teams prefer FOUT over FOIT for body copy?Because invisible text is indistinguishable from a broken page — the reader has nothing to do but wait. FOUT lets them start reading at first paint and pays for it with a restyle they can read through. The calculation flips for icon fonts, where the fallback carries no useful glyphs at all.
saying these in an interview costs you the question
- Thinks font-display controls when the font starts downloading
- Says font-display: swap removes the visible font change
- Believes FOIT means the font failed to load
- Puts font-display in the element rule instead of @font-face
- Assumes swap is correct for icon fonts too