~/TechPurAI
~/tutorials/css-from-scratch/typography-font-stacks-and-type-scale
beginner·part 6 of 22·3 min read

Typography: real font stacks, @font-face, and a consistent type scale

Updated Aug 16, 2026CSS

Typography is often the single biggest visual factor in how a real page feels, before a single color or layout decision is made. This part sets up Bright Leaf Coffee's actual typography properly — real fallbacks, a real custom font, and a genuine, consistent scale.

Font stacks: real fallbacks, not a single point of failure

css
body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

h1, h2, h3 {
  font-family: "Georgia", "Times New Roman", serif;
}

A font-family declaration lists real fallbacks in preference order — if "Inter" isn't available (not yet loaded, or a font file that failed to fetch), the browser tries the next one, continuing down to the final generic keyword (sans-serif or serif), which is guaranteed to resolve to something real on every device. Never declaring a real fallback chain risks an unstyled, inconsistent-looking fallback to whatever arbitrary default font a specific browser happens to use.

Self-hosting a real custom font with @font-face

css
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

body {
  font-family: "Inter", sans-serif;
}

@font-face registers a real, custom font file for use in font-family declarations — font-weight: 100 900 here declares this as a real variable font file capable of rendering any weight in that range from a single file, rather than needing a separate file per weight. font-display: swap is a genuinely important real performance detail: it tells the browser to render text immediately in the fallback font, then swap to the custom font once it finishes loading — without it, some browsers default to hiding text entirely until the custom font loads, a real, needless delay to when a visitor can actually read anything.

Why it matters

This ties directly into the HTML series' own preload guidance — a real custom font declared with @font-face is exactly the kind of critical resource worth a <link rel="preload"> in the HTML <head>, since CSS alone doesn't discover the font file until the stylesheet itself has already been fetched and parsed.

A real, consistent type scale

css
:root {
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.75rem;
  --text-2xl: 2.5rem;
}

h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }
p  { font-size: var(--text-base); }
small { font-size: var(--text-sm); }

Rather than choosing an arbitrary font-size for every individual element as it comes up, a real, deliberate type scale — a small, fixed set of sizes referenced by name — keeps typography genuinely consistent across an entire site. This is the same real principle as part 5's design tokens, applied specifically to font sizes; a new heading added anywhere later in Bright Leaf Coffee's real site picks one of these five values rather than introducing a sixth, arbitrary one.

Real line-height and readable measure

css
body {
  line-height: 1.6;
}

.prose {
  max-width: 65ch;
}

line-height: 1.6 (a real, unitless multiplier of the element's own font-size) is a genuinely comfortable default for real body text — too tight, and lines of text feel cramped and harder to track visually; too loose, and paragraphs lose cohesion. max-width: 65ch constrains a real block of body text to roughly 65 characters per line using the ch unit (the width of the "0" character in the current font) — a real, well-established readability guideline, since lines significantly longer than that become genuinely harder to track from the end of one line to the start of the next.

Font weight and real, deliberate hierarchy

css
h1 { font-weight: 700; }
p  { font-weight: 400; }
strong { font-weight: 600; }

Real numeric font-weight values (400 for normal, 700 for bold, and values between for variable fonts like the @font-face example above) give more real control than the keywords normal/bold alone — worth using deliberately to establish genuine visual hierarchy, distinct from the semantic hierarchy the HTML series' own heading guidance already established structurally; CSS controls how that real hierarchy looks, not what it means.

Next: backgrounds and borders — real background images, gradients, border-radius, and box-shadow, styling Bright Leaf Coffee's actual product cards.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous5. Colors, units, and CSS custom properties: building real design tokensnext →7. Backgrounds, borders, and shadows: styling real product cards