Skip to content
Back to Design Hub

Typography System & Type Scale

Modular type scales, line-height ratios, and responsive clamp() sizing for readable text everywhere.

Design TokensIntermediate

Why

Inconsistent type sizes (14px here, 13px there, 15px in modals) make a UI feel uncared-for and hurt readability. A modular scale from one ratio gives harmonious sizes that scale fluidly across viewports.

Tokens Studio (formerly Figma Tokens) has 500k+ installs, and the W3C Design Tokens Format Module is nearing candidate recommendation. Tokens enable dark mode, multi-brand theming, and platform parity from a single source.

When

Define the type scale before designing screens. If you have 8+ font sizes in use, audit and collapse them onto a scale — most products need only 5-7 sizes.

How

1. Pick a base — 16px (1rem) body, the browser default and most readable 2. Pick a ratio — 1.25 (major third) for dense UIs, 1.333 (perfect fourth) for editorial 3. Generate scale — base × ratio^n for -2..+4, round to rem with 2 decimals 4. Line-height — 1.5 for body, 1.25 for headings, 1.1 for display 5. Fluid sizing — clamp(min, preferred vw, max) so headings grow with viewport 6. Font loading — preload woff2, font-display: swap, subset to latin + cjk 7. Tokens — --font-size-{-2..4}, --line-height-{body|heading|display}

Example

// typography-tokens.css — modular scale (ratio 1.25, base 1rem) :root { --font-size--2: 0.64rem; /* 10px — caption */ --font-size--1: 0.80rem; /* 13px — small */ --font-size-0: 1.00rem; /* 16px — body */ --font-size-1: 1.25rem; /* 20px — h4 */ --font-size-2: 1.56rem; /* 25px — h3 */ --font-size-3: 1.95rem; /* 31px — h2 */ --font-size-4: 2.44rem; /* 39px — h1 */ --line-height-body: 1.5; --line-height-heading: 1.25; } /* Fluid h1 — grows from 28px to 39px between 640px and 1024px viewport */ h1 { font-size: clamp(1.75rem, 2vw + 1rem, var(--font-size-4)); line-height: var(--line-height-heading); } /* font loading — preload + swap to avoid FOIT */ /* <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin> */ @font-face { font-family: "Inter"; src: url("/fonts/inter.woff2") format("woff2"); font-display: swap; }

Common Problems

  • ⚠Too many sizes — 14 sizes in use, half differ by 1px, impossible to keep consistent
  • ⚠Fixed px sizes — doesn't scale with user's browser font-size setting, breaks a11y
  • ⚠FOIT on font load — invisible text for 3s while the web font downloads

Best Practices

  • ✓Use rem not px — respects user font-size setting, required for WCAG 1.4.4 resize
  • ✓Cap the scale at 7 sizes — if you need an 8th, your hierarchy is wrong, redesign
  • ✓Always font-display: swap — show fallback text immediately, swap when font arrives

FAQ