Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~20 min· topic 1 of 6

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 a in 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 serif or sans-serif that 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.

style.csswhole filecss
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.

style.csswhole filecss
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.

style.csswhole filecss
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.

Let the browser choose, character by characterdiagram
Rendering diagram…

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.

style.csswhole filecss
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. 1

    Quote a generic by mistake

    In the first example, change .sans { font-family: sans-serif; } to font-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. 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. 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

The generic families side by side New tab

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

Press Run to see the page.

Console

Nothing logged yet.
A stack is inherited, form controls are not New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

h1: Georgia, "Times New Roman", serif
code: ui-monospace, Consolas, monospace
fixed button: Georgia, "Times New Roman", serif
Per-character fallback New tab

Courier 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

Press Run to see the page.

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.

terminal
$ the page shows
── what you'll see ──
Text in the browser's default serif font (often Times New Roman), not a clean sans-serif.

Break #2

Leave out the comma

Write font-family: Georgia serif; (missing comma).

terminal
$ the page shows
── what you'll see ──
The browser default font, as if the rule did nothing.

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-style and font-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: monospace on its own makes browsers shrink the default size from 16px to 13px (a legacy rule). Listing any other family first, such as ui-monospace, monospace, avoids it. That's the origin of the strange-looking font-family: monospace, monospace hack in old resets.

  • ▸

    Long font stacks don't cost performance: the browser only looks up local fonts. Downloads happen only for @font-face fonts 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 lowercase x, differs). font-size-adjust scales a fallback so its x-height matches your main font, reducing layout shift when fonts swap.

Remember this

  1. 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-family property: 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. 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: serif is the keyword, but "serif" means "a font whose name is serif", which almost certainly doesn't exist.

  3. 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) and fantasy (decorative). Newer ones include system-ui (the operating system's own interface font), ui-sans-serif, ui-serif, ui-monospace, ui-rounded, emoji and math. Generic families always resolve to *some* real font, which is why a stack should end with one.

  4. 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. 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-ui is supported in all modern browsers; the other names are older fallbacks for specific systems (Apple, Windows, Android).

  6. 6

    font-family is inherited: set it once on body (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 is button, input, select, textarea { font: inherit; }.

Explain it without notes

01

What is a font stack, and why must it end with a generic family?

02

Why does a single line of text sometimes show several different fonts?

03

When should font names be quoted in font-family, and when must they not be?

04

Why don't buttons and inputs use the font you set on body, and how do you fix it?

Practice

01

Write a font stack for headings that prefers Georgia, then Times New Roman, and always ends in a serif.

02

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.