Fonts and Web Fonts

Introduction

Fonts strongly influence readability, tone, and brand identity. A good font setup is not only about appearance, but also fallback behavior, loading speed, and user experience under slow networks. This chapter covers practical font-family strategy, system font stacks, web font loading, and performance-minded defaults.

Prerequisites

  • Basic CSS syntax and text styling
  • A page with an external stylesheet
  • Basic understanding of browser loading behavior

Font Families in CSS

Use font-family to define “try this font first; if missing, try the next one”:

css
body {
  /* Try left to right; sans-serif at the end is the generic fallback so text always shows */
  font-family: Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

Meaning: the browser walks this list—use Inter if available; otherwise try Segoe UI… and finally fall back to sans-serif (a generic sans-serif family).

How fallback works:

  1. Browser tries the first font
  2. If unavailable, it tries the next one
  3. It ends with a generic family (sans-serif, serif, monospace, etc.)

Always include a generic family at the end. Wrap font names that contain spaces in quotes, for example "Segoe UI".

Generic Font Families

Common generic families:

  • serif: serif typefaces (stroke ends have small “feet”; more print-like)
  • sans-serif: sans-serif typefaces (most common for modern UI)
  • monospace: fixed-width characters (great for code)
  • cursive: handwriting / script styles
  • fantasy: decorative typefaces
  • system-ui: prefer the operating system’s default UI font

For most modern UI work:

  • body/UI text often uses sans-serif or system-ui
  • code blocks use monospace

System Font Stack

A system stack prefers fonts already installed on the device, so you usually avoid extra downloads and get better performance:

css
body {
  /* system-ui / -apple-system: follow the OS UI font; then common platform fallbacks */
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

Benefits:

  • fast first render
  • native platform feel
  • fewer network dependencies

Trade-off:

  • typography differs slightly across operating systems (Windows / macOS / Android may not look identical)

Web Fonts with @font-face

When you need a specific brand typeface, use @font-face to tell the browser: “this font name maps to that font file.”

css
@font-face {
  font-family: "Inter";  /* custom name you will use later in font-family */
  src: url("/fonts/inter-regular.woff2") format("woff2"); /* file path + format */
  font-weight: 400;      /* this file is the regular weight */
  font-style: normal;    /* upright, not italic */
  font-display: swap;    /* show fallback text first; swap in the web font when ready */
}
 
body {
  /* prefer the registered Inter; otherwise fall back to system fonts */
  font-family: "Inter", system-ui, sans-serif;
}

Key fields:

  • font-family: custom font name used in styles
  • src: font file path and format
  • font-weight / font-style: which weight/style this file represents
  • font-display: what to show while loading

How to Read the src Line

For example:

css
src: url("/fonts/inter-regular.woff2") format("woff2");

Break it down:

PartMeaning
srcsource — where the font comes from
url("/fonts/inter-regular.woff2")file location: fonts/inter-regular.woff2 at the site root
format("woff2")tells the browser this is woff2; use it only if supported

The filename inter-regular is usually just a convention: the regular version of Inter. The real weight binding comes from font-weight: 400 next to it.

Think of it as two steps: register the font with @font-face, then use it with font-family.

Why woff2 Is Preferred

woff2 is the modern default format for web fonts:

  • better compression
  • broad support in modern browsers
  • smaller download size than many older formats

If legacy browser support is critical, teams may add additional formats, but many modern projects ship woff2 only.

font-display and Perceived Performance

font-display controls what users see before the font is fully loaded.

Common value:

css
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-regular.woff2") format("woff2");
  font-display: swap; /* show fallback text first; replace with the web font when ready */
}

Behavior:

  • browser shows fallback text immediately
  • swaps to the web font when ready

This usually avoids the “space reserved but no visible text” problem during loading.

FOIT and FOUT

Two common font-loading effects:

  • FOIT (Flash of Invisible Text): text stays hidden until the font loads
  • FOUT (Flash of Unstyled Text): fallback font appears first, then switches

Most products prefer a controlled FOUT (with font-display: swap) over FOIT, because readable text appears sooner.

Weight and Style Variants

If your UI uses multiple weights, provide a matching font file for each weight (do not declare only 400 and expect a perfect 600 to appear magically):

css
@font-face {
  font-family: "Inter";
  /* url(...) = font file path; format("woff2") = the file format is woff2 */
  src: url("/fonts/inter-600.woff2") format("woff2");
  font-weight: 600;   /* this file maps to weight 600 */
  font-style: normal;
  font-display: swap;
}

What this src line means:

  • download the font from /fonts/inter-600.woff2
  • inter-600 usually means Inter at weight 600 (semi-bold)
  • format("woff2") declares the format so the browser can decide whether to use it

Later, when you write font-weight: 600, the browser uses this file. If a requested weight is missing, browsers synthesize or substitute, which may look inconsistent.

Google Fonts and Hosted Fonts

You can load fonts from providers (for example Google Fonts) or self-host files.

Provider-hosted advantages:

  • easy setup
  • automatic delivery updates

Self-hosted advantages:

  • full control over caching and privacy
  • fewer third-party dependencies
  • easier performance tuning in some deployments

Choose based on product requirements and infrastructure constraints.

Font Loading and Performance Guidelines

  • limit font families (often 1-2 is enough)
  • limit number of weight variants
  • prefer woff2
  • use font-display: swap
  • avoid loading decorative fonts for large body content
  • test on slower networks

Typography quality is important, but payload size matters too.

Tip

Keep Typography Simple

A clean hierarchy with one primary family and a small weight scale often looks better than mixing many fonts.

Practical Example: UI + Code Font

A common combo is sans-serif/system fonts for UI text and monospace for code:

css
body {
  /* body copy, buttons, navigation, and other UI text */
  font-family: Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
}
 
code,
pre {
  /* code inside code/pre: monospace so characters align cleanly */
  font-family: "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
}

This keeps UI text readable and code rendering stable.

Common Font Mistakes

MistakeWhy it hurtsBetter approach
No fallback fontsBroken look if custom font failsAlways provide a fallback stack
Too many families/weightsSlower loading and visual noiseKeep the font system minimal
Missing font-displayRisk of invisible textUse font-display: swap
Using decorative font for body copyLower readabilityReserve decorative fonts for accents
Not testing real devicesHidden rendering issuesTest desktop + mobile + slow network

Mini Exercise

  1. Define a system font stack for body text
  2. Add a monospace stack for code and pre
  3. Add one self-hosted @font-face declaration (if font files are available)
  4. Set font-display: swap
  5. Test behavior with DevTools network throttling
  6. Compare readability before and after

FAQ

Should I always use custom web fonts?

No. System fonts are often a great default for performance and readability.

Is one font family enough for a full site?

Usually yes. Many high-quality products use one UI family plus one monospace family for code.

Why does text “jump” after load?

That is often fallback-to-web-font swapping. It can be reduced with better fallback metrics and careful font choices.

Yes, but self-hosting gives more control over performance, caching, and third-party dependency.

Do I need many font weights?

Not usually. A minimal scale (for example 400, 600, 700) is enough for most interfaces.