01 / Visual language
The system you actually read.
Interfaces are mostly text — strip the chrome from any product screen and what’s left is type. A type system locks roles, sizes, leading, weight, and measure so a thousand screens read like the work of one designer instead of a thousand committee decisions.
01 / Three roles
One typeface, three jobs.
Every product type system has three roles: display for moments, text for reading, mono for code. Most products can ship all three from one typeface family — the variable axes carry the weight. Pairing two families is a design choice, not a requirement.
Display
Headlines, hero copy, marketing moments. Set with negative tracking, tight leading.
- Hero h1
- Page titles ≥ 40px
- Editorial pull-quotes
Text
Body copy, UI labels, paragraphs. Set with neutral tracking, comfortable leading.
- Paragraph body
- Form labels & inputs
- List items, table cells
Mono
Code blocks, tokens, metadata. Tabular numerals for tables.
- Code snippets
- Token names · CSS vars
- Tabular numbers
ABCDEFGHIJKLM NOPQRSTUVWXYZ
abcdefghijklm nopqrstuvwxyz
0123456789
& @ # % · — “ ” ?
02 / The scale
Eleven tokens. one anchor.
Every size is defined against the 16px body anchor. Steps sit close together through UI sizes, where 2px matters, and spread out at display sizes, where it doesn’t. Eleven tokens cover micro text through marketing display — more is decoration, fewer leaves gaps the eye notices.
Eleven steps from 12px to 72px, rendered at actual size.
One scale, read three ways.
The lead paragraph sits a step above body — larger, lighter in colour, roomy leading to pull the reader in.
Body copy holds the anchor at 16px with 1.6 leading and a 65-character measure. Every other size in the layout is a ratio away from this one number. Weight carries emphasis inside the run — semibold for the things that matter — without breaking the reading rhythm.
--text-base · 16px / 1.6 · 400
Caption text drops to 14px, muted, for metadata and asides.
03 / Line-height curve
Tight at the top, loose at the bottom.
Leading isn’t a constant — it curves inverse to size. Display sizes need leading near 1.0 or they look bloated; body sizes need 1.5–1.65 or they feel claustrophobic. Tracking follows the same logic, just gentler.
72px
lh 1
tr -0.02em
Tight at the top.
48px
lh 1.1
tr -0.015em
Slightly loose for h1.
24px
lh 1.3
tr -0.005em
Open enough for subheads to breathe.
16px
lh 1.6
tr 0
Body copy lives here. Long paragraphs need the air to scan and skim without rereading the same line twice.
12px
lh 1.5
tr 0.02em
Footnotes get a touch wider tracking.
The curve. 72px → 1.0 · 48px → 1.1 · 24px → 1.3 · 16px → 1.6 · 12px → 1.5. Memorize the endpoints; interpolate between them.
04 / Weight ladder
Four weights cover every job.
Regular for body, medium for UI emphasis, semibold for headings, bold for the occasional shout. Shipping six weights — light, regular, medium, semibold, bold, extrabold — is the moment a system stops being shippable.
If a layout needs both bold and extrabold to read correctly, the size scale isn’t doing its job. Push the contrast into the size axis first, weight axis second.
05 / Measure
Forty-five to seventy-five characters.
Measure is line length in characters. The readable band runs 45–75 characters — wider and the eye loses the next line, narrower and the rhythm of reading breaks. This is the oldest rule in typography. CSS handles it in one declaration: max-width: 65ch.
Too narrow · < 45ch
Eye returns to the next line too often. Reading rhythm breaks.
Readable · 45–75ch
The eye sweeps the line and finds the next one without effort. This is the band every editorial designer has used since Gutenberg.
Too wide · > 75ch
The return sweep loses the next line. The reader rereads the same line, or skips one entirely, or — most commonly — gives up and scans for headings instead of reading.
06 / Fluid type
One declaration, three breakpoints retired.
clamp() replaces the breakpoint cascade for type. One value scales continuously between a min and a max — no media queries, no jump at 768px, no Pixel 7 fighting a Pixel 8 Pro. Apply it to display sizes; leave body type alone.
375px
32px
Headline scales fluidly.
768px
48px
Headline scales fluidly.
1280px
64px
Headline scales fluidly.
font-size: clamp(2rem, 4vw + 1rem, 4rem);Where to use clamp. Display sizes (32–72px) benefit most. Body copy at 16px should stay constant — fluid body text crosses the readable band at the wrong viewports.
07 / Anti-patterns
Four ways a type system becomes a type problem.
These four account for most of the typography debt every team eventually pays down.
Too many sizes
Avoid
18 type tokens across the productPrefer
11 tokens covering xs → 7xlEvery extra size is another decision a designer makes wrong on a Tuesday. Eleven steps cover every real product need — anything beyond is decoration.
Constant line-height
Avoid
line-height: 1.5 everywherePrefer
Tight at display sizes · loose at body sizesLeading is inversely related to size. 1.5 balloons at 64px and feels cramped at 12px. The line-height curves down as the type gets bigger.
Measure too wide
Avoid
Paragraphs at 120 characters per linePrefer
Body copy at 65–75ch · max-width: 65chPast 75 characters, the eye loses the next line on the return sweep. Keep body measure inside 45–75 characters.
Loose semantic naming
Avoid
--font-h2 · --font-buttonPrefer
--text-3xl · --text-smComponent-bound tokens lock you into the markup. Size tokens are reusable across roles — a marketing card can use --text-3xl without pretending to be an h2.
Lock the scale before designing components — every component snaps to it.
Match weight to importance, not size. Don't bold every heading.
Set measure with max-width: 65ch. Long-form just works.
Type sits on a scale. spacing is the same scale, applied differently.
Once the type scale is locked, the spacing scale is what holds the page together — the vertical rhythm between paragraphs, the gap between cards, the cushion inside a button. Same modular thinking, applied to layout.