Type Scale and Anchor Point
Every project you’ll work on has a font size problem. Usually not one problem, but a dozen of them, each added independently, each making sense to whoever added it at the time. Paragraphs at 16px. Headings somewhere between 18px and 32px with no clear system behind the choices. A subtitle style that grew out of a one-off design request and never got named. A font-size: 13px buried in a component that makes a section slightly harder to read.
The fix is not complicated. It’s just uncommonly taught.
This chapter is about building a type scale: a predetermined set of font sizes with a consistent mathematical relationship between them. You set the sizes once, in one place, and everything else refers to them by name. The result is a codebase where “how big should that heading be?” has an answer that doesn’t require opening DevTools.
The anchor point
Every type scale starts with a base size. This is your anchor: the size that body text, paragraph content, and most UI labels will use. From it, everything else is derived.
For most web interfaces, that anchor is 16px.
That’s not arbitrary. 16px is the browser’s built-in default for body text, and it’s been that since the early days of the web. It’s also close to optimal for reading on a screen at arm’s length. Anything below 16px for sustained body copy demands more from the reader. The effort is small enough that most people won’t articulate it, but it accumulates over a page.
Some projects anchor at 18px. If you’re building something reading-focused, a long-form editorial site or a documentation library where sustained reading is the primary activity, 18px gives more breathing room. The text feels more comfortable over time.
Dense interfaces sometimes anchor lower, at 14px. Data tables, developer tools, admin dashboards where information density is the explicit design priority. That’s a deliberate trade-off, not a default.
Start at 16px unless you have a specific reason not to.
The ratio
Once you have an anchor, you need a ratio. The ratio is a number you multiply (and divide) to generate the rest of the scale.
Here’s the math: take your anchor (16px) and a ratio (1.25). Multiply up for sizes larger than body text, divide down for sizes smaller.
| Step | Result |
|---|---|
| base | 16px |
| × 1.25 | 20px |
| × 1.25 | 25px |
| × 1.25 | 31.25px |
| × 1.25 | 39px |
| × 1.25 | 48.8px |
| ÷ 1.25 (from base) | 12.8px |
That gives you a mathematically consistent set of sizes: 12.8, 16, 20, 25, 31, 39, 48. You don’t have to use all of them. You probably won’t.
The ratio determines how dramatic the jumps between sizes are. Common choices:
1.125 (Major Second): Small jumps. Works well for body-heavy interfaces where the type hierarchy needs to be present but not loud.
1.2 (Minor Third): Moderate jumps. Works across most web interfaces. A safe default if you’re not sure.
1.25 (Major Third): Noticeable jumps. Good when you want heading sizes to feel clearly distinct from body text.
1.333 (Perfect Fourth): Large jumps. Works well for editorial and marketing sites where the hierarchy should be expressive.
The names come from musical intervals. You don’t need to understand why. What matters is the number. Pick a ratio that fits the visual weight you’re going for. You can adjust it later if the first choice feels wrong.
Working in rem, not px
The sizes above are in px to make the math legible. In your actual CSS, use rem.
rem stands for “root em.” It’s a multiple of whatever font size is set on the <html> element. By default in every browser, that’s 16px. So 1rem = 16px. 1.5rem = 24px. 0.875rem = 14px.
Here’s why it matters: users can change their browser’s default font size. Someone who finds 16px hard to read at their screen distance might set their default to 20px. If you’ve set all your type in px, nothing responds to that setting. Your body text stays at 16px regardless of what they asked for. If you’ve used rem, your body text becomes 20px and everything scales proportionally.
This is a basic accessibility concern, and it’s easy to get right.
To convert your scale, divide each px value by 16:
| px | rem |
|---|---|
| 12.8px | 0.8rem |
| 16px | 1rem |
| 20px | 1.25rem |
| 25px | 1.5625rem |
| 31.25px | 1.953rem |
| 39px | 2.4375rem |
| 48.8px | 3.05rem |
One note on the smallest value: 0.8rem (12.8px) is below the accessibility floor for anything functional. Don’t use it for labels, captions, or any text a user needs to read to complete a task. It’s appropriate for purely decorative metadata, and even then, proceed carefully.
Setting the scale with CSS custom properties
Name your tokens semantically, not numerically. A property called --text-xl survives a redesign. --font-size-31px does not.
Here’s a complete scale as CSS custom properties, anchored at 16px with a 1.25 (Major Third) ratio:
:root {
--text-xs: 0.8rem; /* 12.8px — decorative metadata only */
--text-sm: 0.875rem; /* 14px — captions, secondary text */
--text-base: 1rem; /* 16px — body anchor */
--text-md: 1.25rem; /* 20px — lead text, prominent labels */
--text-lg: 1.5625rem; /* 25px — small headings (h4 range) */
--text-xl: 1.953rem; /* 31.25px — mid headings (h3 range) */
--text-2xl: 2.4375rem; /* 39px — large headings (h2 range) */
--text-3xl: 3rem; /* 48px — display, h1 */
}Use them in your stylesheets:
body {
font-size: var(--text-base);
}
h1 {
font-size: var(--text-3xl);
}
h2 {
font-size: var(--text-2xl);
}
.caption {
font-size: var(--text-sm);
}One scale, applied everywhere. When the design calls for larger headings, you change one number.
On rounding
The modular scale math produces values like 1.953rem and 1.5625rem. There’s a temptation to round these to 2rem and 1.5rem for cleanliness.
You can do this. The jumps between sizes will be slightly uneven, but for most projects the difference is invisible. If the scale is the foundation of a complex design system where tokens cascade across dozens of components, rounding deserves more thought. If you’re building a personal site, round to one decimal and move on.
What matters more: don’t abandon the scale. Setting h3 to 28px because “it looked right” is where the problem starts again.
Tools
If you want to see your scale before writing CSS, two tools make this quick:
Typescale: Pick a base size and ratio, see the full set of values, copy out the CSS. Good for exploring ratios quickly.
Modular Scale: More options and more control.
Neither is required. The math above is everything.
What comes next
A type scale gives every font size in your project a name and a source. It’s not a visible decision. Nobody looks at a heading and thinks “nice modular scale.” But it’s the kind of decision that makes the next year of working on the codebase quieter.
In Chapter 02, you’ll take the scale you’ve just built and deal with the next problem: how wide the text should be, and why this matters as much as the size.