ToyTools Guide

How to Build a Modular Type Scale in CSS

How a base size and a ratio become caption-to-display steps in rem, and when a step is too small or too large for a phone.

6 min read Updated Oct 2026

How do you build a modular type scale?

Enter a base size and pick a ratio. The page returns eight steps, from caption up to display, each in pixels and in rem. Body is the size you typed. Every step above body multiplies by the ratio. Every step below divides by it. Copy CSS takes the :root block.

The variables are --type-caption, --type-small, --type-body, --type-lead, --type-h3, --type-h2, --type-h1, and --type-display. A 16px base makes --type-body equal 1rem, because rem is computed against a 16px root. The pixel column is the size you will see at that default root.

The sample word on each row is Ag, set at the real pixel size, inside a box that scrolls. A 177px display step does not stretch the whole page. You still see the number. The arithmetic stays in the browser. Nothing is uploaded.

Open the Type Scale Generator →

Which ratio should a font scale use?

Start with 1.25, the major third. From a 16px body the steps stay close enough for an interface, and display lands under 50px. Use 1.125, the major second, when captions and headings should barely move apart. Use 1.2, the minor third, when you want a little more air than 1.125 and less jump than 1.25.

1.333 is the perfect fourth, rounded from 4/3. 1.5 is the perfect fifth. Both are musical names for a multiplier, not a claim about music on the page. They suit posters and marketing pages more than a settings screen. The golden ratio, 1.618, is the largest preset. Major third versus golden ratio comes down to the page: 1.25 for an interface, 1.618 when display type should feel like a poster.

Five of the six presets are musical intervals, and 1.25 maps to the major third. The golden ratio is the exception, which comes from geometry, not music. The names help you remember the number. They do not change the math. Two teams can both say "major third" and mean 1.25. If a spec writes 1.250, that is the same preset. There is no custom field, because an unnamed ratio is hard to repeat later. Pick one of the six and keep the name next to the CSS.

For example, start from a 16px body. Input: base 16 and ratio 1.25, the major third. Output: body is 16px and 1rem, h1 is about 39.06px, and caption is about 10.24px, under the 12px floor, so the note names it.

When is a step too small or too large?

Caption under 12px is too small to read as text on a phone. Display over 64px overflows a phone before the sentence does. The note names those ends and stays hidden when both sit inside the band. The steps are still printed either way. The note is a warning, not a block.

A 16px base and a 1.125 ratio is the quiet case. Caption stays at or above 12px, and display stays at or below 64px, so you see sizes and CSS and no warning. Switch to 1.25 and caption falls to about 10.24px. The note names caption and the 12px floor. Display at that ratio is still a normal heading.

The golden ratio on a 16px base fails both tests. Caption is near 6px. Display is near 177px. The note names both. That scale can still be what you want for a hero on a wide screen. It is a poor default for a phone UI. Raise the base if you need the small steps to clear 12px, or drop display and use h2 for the largest phone heading.

Why are the CSS variables in rem?

Rem follows the root font size, so a reader who enlarges text gets a larger scale. That is the difference in px sizes versus rem sizes: pixels would freeze the ramp at the size you copied. The page assumes the root is 16px, which is the browser default and the default on the px to rem converter. --type-body is then base divided by 16.

If html sets font-size to 18px, every rem on this list renders larger than the pixel column. The pixel column did not lie. It described a 16px root. Convert a single step on the px to rem page when your root is different, or divide by your root yourself. This scale will not guess a root you have not typed.

Line height is not in the block. A useful companion is a unitless line height near 1.4 for body and closer to 1.1 for display. Put that next to the variables in your stylesheet. The generator keeps the file to sizes, because a line-height preset would pretend to know the font.

How should the step names be used?

Caption and small are for labels, meta text, and legal lines. Body is the paragraph. Lead is the sentence under a title. Then h3, h2, h1, and display. Use the name that matches the job, not the HTML tag you happened to pick. A page can have one h1 and still set it in --type-h2 if h1 is too loud.

The powers are fixed. Caption is body divided by the ratio twice. Display is body multiplied five times. There is no ninth step. If you need a size between h2 and h1, you want a smaller ratio, not a new name. Adding a one-off pixel value breaks the scale the next time someone changes the base.

Keep the token names when you change the ratio. A component that reads --type-h2 should not be edited because you tried 1.333 instead of 1.25. The value moves. The name stays. That is the point of a variable. The copied block is the whole contract.

What does the sample row show?

Each row sets the word Ag at that step's pixel size. Ag shows an ascender and a descender, so you can see the body of the letter and not only a capital height. The font is whatever the page is using. This tool does not load a family, a weight, or a tracking value.

The sample sits in a box with a maximum height. A display step larger than that box scrolls inside the row. The meta line under it always shows the label, the pixels, and the rem, so you do not have to scroll the sample to know the number. Copy CSS does not include the word Ag. It includes only the variables.

On a phone the rows stack in one column. The base field and the ratio menu come first, so the controls sit in the first screen. The steps follow. Wide windows put the controls and the steps side by side. Nothing on the page needs a horizontal scroll of the whole window.

How do you choose a scale for a real page?

Write down the smallest text you will actually ship and the largest heading you will put on a phone. Set the base to the paragraph size. Move the ratio until caption clears 12px and display stays near the largest heading you can accept. If display is the thing that breaks, keep the ratio and use a lower step for that heading.

Check both ends after you change the base. A larger base lifts caption over the floor and also inflates display. A smaller base does the reverse. The note is the checklist. When it disappears, both ends are inside the band. When it names one end, fix that end before you paste the CSS into a production stylesheet.

Then paste the block into :root and point body at --type-body. Point the title at the step you chose. Leave the unused variables in the file. They cost nothing, and the next screen may need caption. Revisit the ratio when the font changes, because a wide face at 16px feels larger than a narrow one at the same number.