Fonts & Web Font Loading

How font-family fallback chains work, how @font-face loads custom fonts, and how font-display stops the blank-text flash.

CSS7 min readConcept 9 of 43

What a font stack is

font-family takes a comma-separated list, and the browser uses the first family it can find. If the first is missing, it tries the next, and so on. The last entry should always be a generic family like sans-serif, serif, or monospace, because those are guaranteed to exist on every device.

A real stack looks like font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;. Quotes are only needed when a name has spaces. The browser does not guess: it walks the list top to bottom and stops at the first hit.

Custom fonts that are not on the user's system are loaded with @font-face. You name the family, give a src (the file URL, with format() hints), and optionally pin font-weight and font-style so the right face is picked for bold or italic text.

Why the fallback chain matters

If you write font-family: "MyBrandFont"; with no fallback and that font fails to load, the browser drops back to its default, which is usually a serif. Your whole interface suddenly looks wrong, and on slow connections the text may be invisible for seconds.

A good chain degrades gracefully: the brand font, then a similar widely-available font, then a generic family. The page always reads well, even before the web font arrives.

Loading custom fonts also costs bandwidth and time. One variable font file can replace six static weights, and font-display lets you show readable text instantly instead of hiding it while the font downloads.

How to load a web font

Declare the face with @font-face, list the modern woff2 format first, and use local() so a user who already has the font installed does not download it again:

@font-face { font-family: "Brand"; src: local("Brand"), url(brand.woff2) format("woff2"); font-weight: 400; }

Then set font-display to choose the load behavior. swap shows fallback text almost instantly and swaps in the web font when ready, which is best for body text. block hides text briefly (the FOIT flash), which suits icon fonts where a fallback glyph would look wrong.

For a variable font, one file holds the whole wght axis. Drive it with font-variation-settings: 'wght' 450; (or plain font-weight: 450) to pick any weight, not just 400 and 700.

Try it

Watch a font-family chain resolve, see the four font-display strategies as timelines, and sweep a variable font weight axis live.

font-family fallback chain

The browser walks the comma-separated list top to bottom and uses the first family it can find. Each row below shows the same stack resolving differently.

Brand Sans installed

Brand Sansused
Helveticaskipped
Arialskipped
sans-serifskipped

Renders as "Brand Sans"

Brand Sans missing, Helvetica present

Brand Sansskipped
Helveticaused
Arialskipped
sans-serifskipped

Renders as Helvetica

only the generic remains

Brand Sansskipped
Helveticaskipped
Arialskipped
sans-serifused

Renders as sans-serif

font-display strategies

Each bar is a timeline from page load to the web font arriving. The colored segments show whether the text is blank, showing a fallback, or showing the web font.

blockHides text briefly (FOIT). Good for icon fonts.
blank
fallback
web font
swapShows fallback instantly, swaps when ready (FOUT). Best for body.
blank
fallback
web font
fallbackShort swap window, then locks in whichever is ready.
blank
fallback
web font
optionalSwaps only if already cached; may never swap on slow networks.
blank
fallback
web font
`swap` keeps body text readable from the first paint. `block` fits icon fonts where a wrong fallback glyph looks broken.

variable font weight axis

One variable font file holds every weight on the `wght` axis. These are static stops, then a live auto-sweep from 100 to 900.

Aa
100
Aa
300
Aa
400
Aa
500
Aa
700
Aa
900
Aa

auto-sweeping `font-weight` (reduced motion: stays at 100)

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1Which font-display value shows fallback text almost immediately instead of hiding it while the web font loads?
  2. 2What does local("Brand") do inside an @font-face src?
  3. 3Why should every font-family stack end with a generic family like sans-serif?

Remember this

  • End every font-family stack with a generic family (sans-serif, serif, monospace) as a safety net.
  • Prefer font-display: swap for body text so users never stare at blank space while a font loads.
  • List woff2 first in @font-face src, and use local() to reuse an installed copy before downloading.
  • One variable font file replaces a folder of static weights; drive any value with font-variation-settings: 'wght' 450.

Done with this concept?

Mark it complete to track your progress. No login needed.