A font-family declaration is a list, and the browser walks it. Most people write their chosen face, then sans-serif, and never think about it again. That second entry is what every visitor reads while the real font downloads, and how different it is decides whether the page jumps when the swap happens.
What the browser actually does
It works left to right, per character. If the first family is not available — still downloading, failed, or missing that particular glyph — it moves to the next. So the list is not only a fallback for failure; it is also what fills in for characters your font does not contain.
The system stack, and why it beats a single name
Writing Arial as your fallback means Windows users get Arial and everyone else gets whatever their browser substitutes. The system stack names the native interface font on each platform instead, so every visitor gets something well-made and locally installed: system-ui first, then the platform-specific names, then a generic.
It renders instantly on every device because nothing is downloaded, and it looks native rather than borrowed. For a great many projects it is a legitimate final answer rather than a fallback.
Matching the metrics, which is the part that stops the jump
When the real font arrives, every line re-measures. If the fallback was narrower, text reflows and the page shifts — and Cumulative Layout Shift is a ranking signal, so this is not only cosmetic.
The fix is an @font-face rule that points at a local system font and adjusts it: size-adjust scales it, ascent-override and descent-override set the line box. Tune those until the fallback occupies almost exactly the space the real font will, then name that adjusted family as your fallback. The swap becomes nearly invisible.
The generic at the end is not decoration
Always end with sans-serif, serif or monospace. It is what the browser uses when nothing else in the list has a glyph — which happens more than people expect the moment a user types a name with an accent your font does not carry. Every CSS snippet generated on this site ends with the right generic for that font’s category.
And the setting that decides what happens meanwhile
font-display: swap shows the fallback immediately and swaps when the font arrives. optional gives the font a very short window and abandons it for that page view if it misses, so nothing ever shifts. With a well-matched fallback, swap is fine. Without one, optional is often the better trade. The page speed post has the rest.