Topic 4.1
Font Families and Font Stacks
In one line
font-family takes a list of fonts in order of preference, called a font stack; the browser uses the first one it has and must always end with a generic family like sans-serif as the safety net.
Think of it like this
Ordering a drink at a café. You say: "A mango smoothie, or if you don't have that, orange juice, or if not, any cold drink." The waiter brings the first thing on your list that the café has. A font stack works the same way: you list fonts in order, and the browser uses the first one installed or loaded on that device. "Any cold drink" is the generic family, the answer that always works.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Font (typeface)
- A set of letter shapes drawn in one style, like Arial or Georgia.
- Glyph
- The drawing of one character in a font. The letter
ain Arial is one glyph. - Font stack
- The list of fonts in
font-family, tried in order until one is available. - Generic family
- A keyword like
seriforsans-serifthat always gives some font of that style. - Serif
- Small strokes ("feet") at the ends of letters, like in Times New Roman.
- Sans-serif
- Letters without those small strokes, like Arial. "Sans" is French for "without".
- Monospace
- A font where every character is the same width, used for code.
- Font fallback
- When a font lacks a character, the browser draws that character with the next font that has it.
- System font
- The font the operating system uses for its own menus and buttons.
Step by step
01Name one font
Start with one font name. If the visitor's device has Georgia installed, they see Georgia. If it doesn't, the browser uses its default font (usually a serif like Times), which may look nothing like what you wanted.
body {
font-family: Georgia;
}02Add fallbacks
Add similar fonts after it, from most to least preferred. Quote names with spaces. Each fallback should look as close to the first choice as possible, so the page doesn't change much whichever one is used.
body {
font-family: Georgia, "Times New Roman", Times;
}03End with a generic family
The last item is a generic keyword with no quotes. It guarantees a font of the right style even on a device that has none of the named fonts.
body {
font-family: Georgia, "Times New Roman", Times, serif;
}04Let the browser choose, character by character
For each character the browser walks the stack. Most letters come from the first font that exists. A character the font doesn't have (an emoji, a Hindi letter) falls through to the next font, and finally to the system's fallback fonts.
05Set it once, fix the form controls
Put the stack on body so everything inherits it, and tell form controls to inherit too. Code elements get a monospace stack of their own.
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
button, input, select, textarea {
font: inherit;
}
code, pre {
font-family: ui-monospace, Consolas, "Courier New", monospace;
}Try it yourself
- 1
Quote a generic by mistake
In the first example, change
.sans { font-family: sans-serif; }tofont-family: "sans-serif";. Predict: will the line still be sans-serif? Run it. It falls back to the browser default (usually serif), because the quoted text is treated as a font name that doesn't exist. - 2
Find which font was really used
Right-click a line in the preview → Inspect, then open the Computed tab and scroll to the bottom: the Rendered Fonts section (Chrome, Edge) lists the actual font files used and how many glyphs each drew. Try it on the fallback example: you'll see several fonts.
- 3
Remove the inherit fix
In the second example, delete the
.fixed { font: inherit; }rule and run. Predict the last console line first. The button now reports the browser's own control font instead of Georgia.
Code & diagrams
What you see depends on your device: each generic keyword maps to a font your system has. Compare the shapes of the letters, especially the small feet on the serif line.
Preview
Console
Nothing logged yet.The `h1` never set a font, yet it reports the body's stack: inheritance. The first button keeps the browser's own control font; the second one inherits.
Preview
Console
Nothing logged yet.Expected console output
h1: Georgia, "Times New Roman", serif
code: ui-monospace, Consolas, monospace
fixed button: Georgia, "Times New Roman", serifCourier New has Latin letters but no Hindi, Chinese or emoji glyphs. Those characters are drawn with other fonts from your system, all on the same line.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Forget the generic family
Write font-family: "Helvetica Neue", Helvetica; with no generic at the end, and open the page on a Windows PC that has neither font.
Break #2
Leave out the comma
Write font-family: Georgia serif; (missing comma).
Myth vs fact
Myth
Everyone sees the font I see on my computer.
Fact
The browser can only use fonts installed on the visitor's device or downloaded as web fonts. A Mac, a Windows PC and an Android phone have different fonts, which is why stacks and generic families exist.
Myth
Quotes around font names are always required.
Fact
Quotes are needed for names with spaces or unusual characters to be safe, and forbidden around generic keywords. Arial and "Arial" mean the same thing.
Myth
font-family picks one font for the whole element.
Fact
Fallback happens per character. One paragraph can use your main font for English and a system font for emoji or another script.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Font matching is defined in the CSS Fonts specification: for each character the browser runs the font matching algorithm over the stack, comparing family, then
font-stretch,font-styleandfont-weight, before falling back to the next family. This is also why a missing bold face gets synthesised (fake bold) rather than skipped. - ▸
font-family: monospaceon its own makes browsers shrink the default size from 16px to 13px (a legacy rule). Listing any other family first, such asui-monospace, monospace, avoids it. That's the origin of the strange-lookingfont-family: monospace, monospacehack in old resets. - ▸
Long font stacks don't cost performance: the browser only looks up local fonts. Downloads happen only for
@font-facefonts that are actually used (next topic). Fingerprinting scripts can probe which fonts are installed, so browsers like Safari limit which local fonts pages may see. - ▸
Different fallbacks have different sizes for the same
font-size(their x-height, the height of a lowercasex, differs).font-size-adjustscales a fallback so its x-height matches your main font, reducing layout shift when fonts swap.
Remember this
- 1
A font (or typeface) is a set of shapes for letters, numbers and symbols, all drawn in one style. Each shape is called a glyph. CSS chooses fonts with the
font-familyproperty:body { font-family: Georgia, "Times New Roman", serif; }. The value is a comma-separated list, and the browser tries each name from left to right. - 2
Font names that contain spaces or digits should be wrapped in quotes:
"Times New Roman","Segoe UI". Names that are a single plain word (Arial,Georgia) work with or without quotes. Generic family keywords must never be quoted:serifis the keyword, but"serif"means "a font whose name is serif", which almost certainly doesn't exist. - 3
There are five classic generic families:
serif(letters with small feet, like Times),sans-serif(no feet, like Arial or Helvetica),monospace(every character the same width, like code),cursive(handwriting style) andfantasy(decorative). Newer ones includesystem-ui(the operating system's own interface font),ui-sans-serif,ui-serif,ui-monospace,ui-rounded,emojiandmath. Generic families always resolve to *some* real font, which is why a stack should end with one. - 4
The browser picks a font per character, not once for the whole text. If the first font has no glyph for
你or🙂, the browser tries the next font in the stack, then system fallback fonts, for that one character. This font fallback is why mixed-language text still shows, and why one line can quietly contain several different fonts. - 5
A system font stack uses the font the device already uses for its own menus and buttons, so there is nothing to download and the page feels native:
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;.system-uiis supported in all modern browsers; the other names are older fallbacks for specific systems (Apple, Windows, Android). - 6
font-familyis inherited: set it once onbody(or:root) and every element inside uses it unless it sets its own. One big exception: form controls (button,input,select,textarea) get their own font from the browser's built-in stylesheet, so they don't follow your page font. The usual fix isbutton, input, select, textarea { font: inherit; }.
Explain it without notes
What is a font stack, and why must it end with a generic family?
Why does a single line of text sometimes show several different fonts?
When should font names be quoted in font-family, and when must they not be?
Why don't buttons and inputs use the font you set on body, and how do you fix it?
Practice
Write a font stack for headings that prefers Georgia, then Times New Roman, and always ends in a serif.
Make all code elements use a monospace stack that prefers ui-monospace, then Consolas, then Courier New, and make all buttons use the page font.
Trade-offs
- ↔
A system font stack costs zero bytes and loads instantly, but the page looks slightly different on every operating system; a web font looks identical everywhere but must be downloaded (next topic).
- ↔
Long, carefully chosen fallback lists give a closer match on more devices but are more to maintain; a short stack ending in a generic is usually enough.
Done when you can
I can write a font stack with fallbacks that ends in a generic family.
I know which font names need quotes and that generics must not be quoted.
I can name the main generic families and what each looks like.
I can explain per-character font fallback.
I can make form controls inherit the page font.
I can find the font actually used for some text in DevTools.