Web Typography 101: Why Type Is 90% of Web Design
Strip almost any website down to its essentials and what's left is text. Headlines, paragraphs, labels, buttons — the web is overwhelmingly reading. That's why the old designer adage holds: web design is 90% typography. Get the type right and a page looks professional before you've added a single image; get it wrong and no amount of gradients will save it.
The intimidating part is that typography feels like taste. It isn't — it's mostly a small set of learnable rules. Here they are.
How Do You Pair Fonts Without Making a Mess?
Font pairing fails in two opposite ways: fonts that are too similar (two geometric sans-serifs that look like a rendering bug) or too loud together (a display slab next to a swashy script). The rules that prevent both:
- Pair by contrast, not similarity. The classic combination is a serif for headings + sans-serif for body (or the reverse). The roles are instantly distinguishable, which is the whole point of using two fonts.
- Maximum two families. One for headings, one for body. Need a third voice? Use a weight or style variation of what you have. Every extra family costs load time and coherence.
- Match x-heights. The x-height — the height of a lowercase "x" — governs how "big" a font feels at a given size. Pair a tall-x-height sans with a tiny-x-height serif and one will always look wrong next to the other. Set both to 16px side by side; if one visually dwarfs the other, keep looking.
- Superfamilies are the cheat code. Families designed as sets — a serif, sans, and mono cut from the same skeleton (think Source Serif/Sans/Code, or IBM Plex) — pair perfectly by construction.
Classic Pairings That Always Work
| Heading | Body | Personality |
|---|---|---|
| Playfair Display (serif) | Source Sans 3 | Editorial, elegant |
| Montserrat (geometric sans) | Merriweather (serif) | Modern, friendly |
| IBM Plex Sans | IBM Plex Serif | Technical superfamily |
| Space Grotesk | Inter | Startup, product UI |
| Lora (serif) | Open Sans | Warm, blog-friendly |
Rather than installing fifteen fonts to compare, preview combinations side by side in a Font Pairing tool — real headings over real body text, swapped instantly.
Type Scales: Stop Choosing Sizes at Random
If your CSS contains font-size: 17px, 19px, 22px, and 27px, sizes were chosen by vibes. A type scale replaces vibes with a ratio: start from a base size (usually 16px) and multiply repeatedly by a constant.
| Ratio | Name | Feel |
|---|---|---|
| 1.2 | Minor third | Calm, dense — good for apps and dashboards |
| 1.25 | Major third | Balanced default for most sites |
| 1.333 | Perfect fourth | Assertive — good for marketing and editorial |
| 1.5 | Perfect fifth | Dramatic — landing pages, big heroes |
With a 1.25 ratio and a 16px base: 16 → 20 → 25 → 31.25 → 39.06. Five sizes, all mathematically related, all visibly distinct. Generate the whole ramp — with preview and CSS output — using a Type Scale Generator.
Fluid Type with clamp()
Fixed pixel sizes force a compromise between phone and desktop. clamp(min, preferred, max) ends that: the middle value scales with the viewport, pinned between sane bounds.
:root {
/* base: 16px on mobile → 18px on desktop */
--step-0: clamp(1rem, 0.93rem + 0.33vw, 1.125rem);
--step-1: clamp(1.25rem, 1.14rem + 0.54vw, 1.45rem);
--step-2: clamp(1.56rem, 1.39rem + 0.85vw, 1.88rem);
--step-3: clamp(1.95rem, 1.69rem + 1.29vw, 2.44rem);
--step-4: clamp(2.44rem, 2.05rem + 1.93vw, 3.16rem);
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
body { font-size: var(--step-0); }
Headlines that filled a desktop screen no longer wrap into five lines on a phone — no media queries required.
Line-Height and Measure: The Unsung Half
Two settings do more for readability than any font choice:
- Line-height: body text wants roughly 1.5–1.6; headings want less (1.1–1.3) because large text with tall line-height falls apart visually. Use unitless values (
line-height: 1.5), not pixels. - Measure (line length): the comfortable range is 45–75 characters per line, with ~66 as the sweet spot. Longer, and the eye loses its place returning to the left edge. One property enforces it:
.prose {
max-width: 65ch; /* ~65 characters at the current font size */
line-height: 1.6;
}
The ch unit scales with your font, so the constraint survives font-size changes for free.
Variable Fonts: One File, Every Weight
Traditionally, using four weights of a font meant loading four files. A variable font packs the entire design space — every weight, and often width and slant too — into one file, and lets CSS interpolate anywhere along each axis:
@font-face {
font-family: "Inter Var";
src: url("/fonts/InterVariable.woff2") format("woff2");
font-weight: 100 900; /* the whole range, one file */
font-display: swap;
}
.hero-title {
font-variation-settings: "wght" 640, "opsz" 32;
}
Why this matters:
- Performance. One ~100–300KB WOFF2 file typically replaces 4–6 static files — fewer requests, less total transfer, and one cache entry.
- Precision. Weight isn't limited to 400/700 anymore. If 600 is too heavy and 500 too light, use 560. Animating weight on hover (
transition: font-variation-settings) is a subtle, classy effect no static font can do. - Beyond weight. Many variable fonts expose optical size (
opsz), width (wdth), or custom axes. Optical size in particular quietly improves rendering: letterforms tuned for small body text differ from display cuts.
Explore a font's axes interactively — drag sliders, see the glyphs respond, copy the font-variation-settings — with a Variable Font Tester.
Loading Fonts Without the Flash
Two rules cover most of web font performance:
- Ship WOFF2 only. It's supported everywhere that matters and compresses ~30% better than WOFF. If your source is a TTF or OTF from a foundry, convert it with a Font Converter before deploying.
- Always set
font-display: swap. Without it, some browsers hide text while the font loads (the dreaded invisible-text flash). Withswap, users read fallback text immediately and the web font swaps in when ready.
Add <link rel="preload" as="font" type="font/woff2" crossorigin> for your primary body font, and self-host rather than hotlinking — it's faster and simpler for GDPR compliance.
One Last Check: Contrast
Typography and accessibility meet at contrast: thin weights (200–300) at small sizes effectively lose contrast because there's less ink on the screen, even when the color technically passes. If you use light weights, bump the size or the color contrast to compensate — and verify your text/background pairs in a WCAG Contrast Checker before shipping.
Put It Together in Five Minutes
A working typographic system is four decisions: a pairing, a scale, a measure, and a loading strategy. Start with the first — open the Font Pairing tool, find a heading/body duo you like, then build your ramp in the Type Scale Generator. Free, in the browser, no account required.