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.
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
Renders as "Brand Sans"
Brand Sans missing, Helvetica present
Renders as Helvetica
only the generic remains
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.
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.
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.
Remember this
- End every
font-familystack with a generic family (sans-serif,serif,monospace) as a safety net. - Prefer
font-display: swapfor body text so users never stare at blank space while a font loads. - List
woff2first in@font-facesrc, and uselocal()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.