Architecture
Typography Architecture — Synthesis
Overview
Typography decisions in a design system are upstream of most other visual decisions. The scale determines spatial cadence. The role taxonomy determines how hierarchy is communicated. Line height and measure affect how every component with text feels to use. Getting these right means fewer corrections cascading through the rest of the system.
This document covers the structural decisions that shape a type system — not specific font choices, which are a downstream concern, but the architecture that makes type choices work at scale.
The density axis comes first
Before selecting a scale or ratio, identify where your product sits on the density axis. This single decision shapes more of your typography architecture than any aesthetic preference.
Information-dense contexts (data tables, dashboards, admin tools, code editors, developer interfaces):
- More text elements compete for vertical space
- Users scan rather than read — hierarchy needs to be legible at a glance
- Generous line heights waste space that could show more data
- Scale steps are closer together — fewer jumps between the smallest and largest sizes used in a single view
Reading and editorial contexts (marketing pages, documentation, blog content, onboarding flows):
- Text is read, not scanned
- Visual impact of large type is part of the communication
- Generous spacing aids comprehension
- Scale covers a wider range with more dramatic steps
Mixed contexts (most products):
- App shells and navigation are dense; content areas are editorial
- The same scale must serve both modes, either through separate sub-scales or through using different ranges of a single scale
Most design systems serving mixed contexts either split into two explicit type sets (dense-UI and editorial) or define a long scale with enough steps to cover both ranges without compromise. Neither approach is wrong; the mistake is designing a single short scale that tries to work in both contexts and does neither well.
Scale construction: modular vs. hand-tuned
Modular scales
A modular scale is a geometric sequence — each step is the previous step multiplied by a fixed ratio. The mathematical structure ensures that all size relationships are consistent. The visual result is a set of sizes where the perceived jump between any two adjacent steps feels equivalent throughout the scale.
Choosing a ratio:
| Ratio | Character | Works well for |
|---|---|---|
| 1.125 (Major Second) | Small, tight steps | Dense UI scales; many usable intermediate sizes |
| 1.200 (Minor Third) | Moderate steps | Enterprise product UI; enough hierarchy without drama |
| 1.250 (Major Third) | Clear steps | General-purpose product scales; comfortable heading contrast |
| 1.333 (Perfect Fourth) | Pronounced | Scales covering a wide size range; strong heading contrast |
| 1.414 (Augmented Fourth) | Bold | Expressive layouts; display-heavy designs |
| 1.618 (Golden Ratio) | Very large | Marketing/poster work; typically too dramatic for multi-step UI scales |
The ratio is most consequential at the small end of the scale. A 1.333 ratio from a 14px base gives 19px, 25px, 33px for the next three steps — clean for headings. The same ratio from a 12px base gives 16px, 21px, 28px. The base and ratio together determine whether the scale steps fall on workable values.
Modular scales with ratios above 1.333 tend to produce too few usable intermediate steps for a product scale (which needs 7–12 named sizes from label to display). They work better as a 4–6 step display-only scale with a separate, tighter base scale for UI text.
Hand-tuned scales
A hand-tuned scale starts from a list of desired sizes and works backward to verify the relationships are acceptable. The classical typographic scale (6–7–8–9–10–11–12–14–16–18–21–24–30–36–48–60–72pt) is hand-tuned from centuries of printing practice and approximates a geometric ratio of ~1.14 with some deliberate deviations.
Use hand-tuning when:
- You have specific pixel values that must land on clean numbers (a modular scale from 14px at 1.25× gives 17.5px — not ideal)
- The scale needs to cover an unusually wide range (11px to 60px) and no single ratio produces good steps throughout
- You are adapting an existing brand scale that doesn't follow a clean ratio
Use a modular scale when:
- You want the mathematical regularity to enforce consistency as the scale grows
- The design tool or framework will generate intermediate values (e.g. tokens computed from base + ratio)
- You want to be able to explain the scale's structure to others without enumerating every value
The practical middle path
Most production design systems use a hybrid: a loosely modular structure as a guide, with deliberate deviations to land on clean values and preserve good line-height relationships. Define the scale by listing its values — don't describe it by ratio unless the ratio holds cleanly across the full range.
Role taxonomy: named roles vs. numeric steps
This is the second major structural decision, and it has more influence on how your system communicates than on how it looks.
Named roles (semantic taxonomy)
Named roles assign communicative meaning to each size: display, headline, title, body, label. The name tells a developer what the text is for, not how large it is.
What named roles enable:
- Communication: a developer reading
type-role-body-largeknows what it does. They can use it correctly without seeing the pixel value. - Role-based theming: you can change the body text size system-wide by updating the
bodytoken, without knowing which components use it - Cross-system legibility: named roles are familiar across design systems; a new team member can read the role names and understand the hierarchy
The tradeoff:
- Named roles couple the token to a semantic intent. If a designer wants to use
headline-smallfor a non-heading context because it's the right size, they're either misusing the name or revealing that the scale doesn't have a size for that use case - Adding a new step between existing named roles requires careful naming that fits the existing pattern
The 5-role pattern (display / headline / title / body / label) emerged from mobile typography practice and maps well to most product UIs. Each role may have 2–4 size variants (large / medium / small / xsmall) to cover the range within that role's purpose.
Numeric steps
Numeric steps (1 through N) assign no semantic meaning — they're a size ladder. Step 5 is larger than step 4; what it means in context is up to the component consuming it.
What numeric steps enable:
- Composability: a component can use any step for any purpose, and the system doesn't prescribe what that purpose is
- Easy extension: add step 10 without naming implications
- Developer ergonomics:
size="6"is predictable and composable; no need to remember semantic distinctions
The tradeoff:
- Numbers communicate nothing about purpose. A team with 5 engineers will make 5 different size choices for the same UI element because the scale doesn't constrain the choice
- Without semantic names, the system can't enforce that the same role looks consistent across all components
Decision framework
Use named roles when:
- You want the type system to actively constrain and communicate purpose
- Multiple teams are consuming the system and consistency across those teams matters
- The system serves a product context where semantic hierarchy (this is a heading, this is body text) is meaningful
Use numeric steps when:
- You are building a library for other developers to compose with, not a system that prescribes usage
- The consuming team will make their own component-level decisions about hierarchy
- You want maximum flexibility at the cost of system-level prescription
Mixed approach: Named roles at the token/design level, numeric implementation internally. Define type-body-default as an alias for the equivalent numeric step. Developers who know the system use named tokens; the underlying scale is still numeric and composable.
Legibility constraints (non-negotiable)
These are not design preferences — they are cognitive limits. Violating them reduces readability for all users.
Line height
Body text: Line height must be 120–145% of font size (unitless values 1.2–1.45 in CSS). Below 1.2 and lines feel cramped; above ~1.6 and the space between lines becomes larger than the line itself, disrupting reading flow.
Per-size calibration: The optimal line-height ratio is not constant across the scale. It decreases as font size increases:
- Display / hero (48px+): 1.05–1.1
- Large heading (28–36px): 1.1–1.2
- Small heading (18–24px): 1.2–1.35
- Body (14–18px): 1.4–1.6
- Small / label (11–13px): 1.3–1.5
This is why a correct type system specifies line-height per role, not once globally. A global line-height: 1.5 is too generous at display sizes and may be too tight at the smallest sizes if the product uses 11px labels.
Always use unitless values: CSS line-height should be a bare number, not a px or em value. Unitless values are inherited as a multiplier and recalculate correctly when child elements have different font sizes. A px value is inherited as a computed value — child elements with different font sizes inherit the wrong line height.
Line length (measure)
Optimal: 45–90 characters per line for body text. Outside this range, readability degrades.
- Below 45 characters: excessive line breaks disrupt reading flow, especially for multi-clause sentences
- Above 90 characters: the eye loses its return position, making line-finding effortful for sustained reading
This translates to a content column width of roughly 480–720px at 16px body text (depending on typeface). Components that constrain content width (cards, panels, inline edits) implicitly control measure. When designing these components, verify that the text at the intended width stays within the optimal range.
Line length and line height interact: longer lines require more line height. When you have no choice but to use a wider column, compensate with more generous line spacing.
Letter-spacing at scale
Letter-spacing should move in the same direction as reading demands:
- Small sizes (≤ 12px): slight positive tracking (+0.01–0.03em) opens the letterforms and aids legibility under compression
- Body sizes (14–18px): zero or minimal adjustment (0 to +0.005em); the font's default tracking is calibrated for this range
- Display sizes (24px+): negative tracking (–0.01em to –0.025em) compensates for optical spread — large letterforms appear loosely spaced at default tracking, and negative values restore apparent density
This is not an aesthetic preference. A 60px heading with default tracking will visually appear to have too much space between letters, even if the measurement is technically zero. Negative tracking at display sizes is the correct choice.
X-height and apparent size
Two fonts at the same CSS font-size may appear visually different sizes. The cause is x-height: the ratio of lowercase letter height to the total em square. A high-x-height font at 14px looks larger than a low-x-height font at 14px.
Implications:
- When switching typefaces, the specified sizes may need adjustment. An 18px body in Roboto (high x-height) will look larger than 18px in a low-x-height typeface; you may need to step up to 19–20px to match the visual weight.
- This is why some systems expose a
font-size-adjusttoken (or equivalent) that multiplies the step size for a specific typeface — not changing the scale, just correcting for x-height differences
Variable fonts: when the complexity is worth it
Variable fonts encode a continuous range of variation axes in a single font file, rather than discrete font weights and styles. The browser can render any point along each axis.
The common axes and when they matter:
| Axis | Tag | Effect | Worth the complexity when… |
|---|---|---|---|
| Weight | wght | Continuous weight from thin to black | You use more than 2–3 weights, or need micro-weight adjustments for display text |
| Width | wdth | Compressed to expanded | You need condensed variants for data display (fixed-width columns at small sizes) |
| Optical size | opsz | Adjusts design for small vs. large rendering | You're using a typeface with an optical size axis and the system covers a wide size range |
| Italic/Slant | ital/slnt | True italic vs. slanted roman | You want true italics without loading a separate italic file |
The real cost of variable fonts is not file size (variable fonts are often smaller than multiple static files) but cognitive complexity in the token system. If you expose the weight axis as a continuous range, developers must decide what value to use rather than picking from a finite set of named weights. Most systems expose variable font axes through a fixed set of named tokens (weight-regular: 400, weight-medium: 500, weight-bold: 700) rather than exposing the axis directly.
When variable fonts add clear value:
- The system uses 4+ weight variants (the file size savings are real)
- You need optical-size adjustment across a wide scale (11px to 60px) and the typeface has an
opszaxis - The product has a true condensed-layout mode where
wdthcompression is genuinely needed
When to skip them:
- The product uses two weights (regular + bold). Load two static files; simpler.
- The chosen typeface doesn't have variable axes that map to your system's needs
- The team using the system isn't equipped to reason about axis values
How typography interacts with the spacing system
Typography and spacing are not independent. Every decision in the type scale has a corresponding constraint in the spacing system.
Line height and vertical rhythm: The line height of body text typically becomes the base unit for vertical spacing within a text container. If body text has a 24px line height, vertical margins between paragraphs should be multiples or fractions of 24px — this creates visual rhythm. Systems with a strict 8px spacing grid work well with 16px/24px line heights (multiples of 8); they work poorly with 20px or 22px line heights.
Scale steps and component height: Interactive components (buttons, form fields, select controls) have a minimum height that should relate to the type scale. A button using label-large text at 14px/20px line height needs enough vertical padding to produce a comfortable click target (44px minimum). 14px + (2 × 15px padding) = 44px — the 15px padding is the spacing system value. When the type scale changes, the component height tokens that reference it may also need adjustment.
Spacing and type density: A dense type scale (small steps, small base) requires a proportionally tighter spacing scale. A system with 12px body text and an 8px base spacing unit feels correct; the same spacing unit with 18px body text feels compressed. The key ratio: base spacing should be roughly 50–75% of the body text line height. If body text has a 24px line height, the base spacing unit of 12–16px works well.
Type and layout columns: Measure (line length) constrains column width. If optimal measure for your body text is 45–75 characters, and body text is 16px in a regular-width sans-serif, the optimal column width is roughly 520–680px. Layout columns should be defined to land content containers within this range. Design systems that define content column widths separately from the type system often produce column widths that are too wide (90+ characters per line) or too narrow (35 characters per line) for the specified body size.
Decision summary
| Decision | Ask first | Then |
|---|---|---|
| Scale construction | Hand-tuned or modular? | Modular if consistency > specific values; hand-tuned if specific values > consistency |
| Ratio | Dense or editorial? | 1.125–1.200 for dense; 1.250–1.333 for mixed; 1.333+ for editorial-only |
| Role taxonomy | Library or system? | Numeric steps for composable libraries; named roles for prescriptive systems |
| Line height | Specified per role? | Always. One global value is wrong for both ends of the scale |
| Variable fonts | 4+ weights needed? | Yes: consider variable font. 2–3 weights: load static files |
| Spacing relationship | 8px grid? | Verify body line height is a multiple of the grid base; adjust either if not |