Choosing a Modular Scale Ratio for UI Type

This guide is part of the Type Scale & Modular Grids area, itself part of Typography Fundamentals & System Architecture.

A modular scale multiplies a base font size by a constant ratio, once per step, to produce every heading and body size in a type system. The ratio is the single number that decides whether your h1 looks confidently larger than your h2 or barely different from it, and whether your six or seven size steps stay inside a sane pixel range or blow out to unusable extremes. Pick the ratio too small and headings collapse into body text with no visual hierarchy; pick it too large and by step five or six you are rendering 90px or 120px text that breaks every layout on a dashboard or admin panel. This guide is about choosing the ratio itself — the actual decision of "1.125 or 1.333?" — not about the CSS custom-property plumbing, which is covered separately in A Modular Type Scale with CSS Custom Properties.

Prerequisites

  • A base font size already fixed (typically 16px or 1rem for body text).
  • An inventory of every text role your UI actually needs: body, caption/meta, h6 through h1, and any display/hero size.
  • Familiarity with calc() and CSS custom properties; the code below builds on the pattern from the sibling custom-properties guide.
  • If your layout needs to shrink or grow the scale by viewport, read Fluid Type Scale with clamp() after finishing this page — ratio selection and fluid clamping are separate decisions that compose.

Why the ratio matters more than the base size

The base size sets where the scale starts; the ratio sets how fast it grows. Because each step is a multiplication, not an addition, the difference between two ratios is invisible at step 1 and enormous at step 6. A 1.125 (major second) and a 1.333 (perfect fourth) both look reasonable for a subtitle one step above body text — 18px vs 21px — but by the fifth step above a 16px base they diverge to 29px vs 67px. This compounding is exactly why "just pick something that looks fine for h2" is the wrong evaluation method: you have to look at the full range the scale will produce, all the way to your largest heading, before you commit.

Named ratios exist because a handful of values recur across type systems and have well-understood personalities:

Ratio Common name Character
1.125 Major second Tight, restrained, dense UI
1.2 Minor third Standard app/dashboard rhythm
1.25 Major third Balanced, common marketing default
1.333 Perfect fourth Confident, editorial contrast
1.5 Perfect fifth Aggressive, few steps needed
1.618 Golden ratio Maximum drama, display-only

None of these is objectively "correct" — the right choice depends on how many size steps you need and how much room your layout has for large text.

Baseline implementation

The standard way to express a modular scale in CSS is a single ratio custom property raised to successive powers via calc(), anchored to a rem-based root size so the whole scale responds to user font-size preferences.

:root {
  --scale-base: 1rem;
  --scale-ratio: 1.25; /* major third — the value you are choosing */

  --step--1: calc(var(--scale-base) / var(--scale-ratio));       /* caption */
  --step-0: var(--scale-base);                                    /* body */
  --step-1: calc(var(--scale-base) * var(--scale-ratio));         /* h4 */
  --step-2: calc(var(--step-1) * var(--scale-ratio));             /* h3 */
  --step-3: calc(var(--step-2) * var(--scale-ratio));             /* h2 */
  --step-4: calc(var(--step-3) * var(--scale-ratio));             /* h1 */
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }
small, .caption { font-size: var(--step--1); }

Line by line: --scale-ratio is the single knob this whole guide is about — every other value derives from it, so changing the type system's personality is a one-line edit. --step--1 divides instead of multiplying, giving a caption size smaller than body text using the same ratio rather than an arbitrary separate value. Each subsequent --step-N multiplies the previous step rather than raising the ratio to a literal power, which keeps the calc() chain simple and lets you insert or remove steps without recalculating exponents by hand. Because every step derives from --scale-base in rem, the whole scale still respects the user's browser font-size setting — nothing here fights user zoom or accessibility overrides.

At a 1.25 ratio and 16px base, that produces roughly: caption 12.8px, body 16px, h4 20px, h3 25px, h2 31.25px, h1 39px. Compare that against a 1.333 ratio on the same base: caption 12px, body 16px, h4 21px, h3 28px, h2 38px, h1 51px — noticeably bolder hierarchy from the same number of steps.

Step-5 size by ratio (16px base) Bar chart comparing computed font size at scale step five across five common modular ratios. Step-5 size by ratio (16px base) 1.125 29px 1.2 40px 1.25 49px 1.333 67px 1.414 90px px at step 5
Larger ratios compound faster over the same number of steps.

Solving for a ratio instead of guessing one

Rather than picking a name off the table above, you can derive the exact ratio your design needs by working backward from constraints you already have: your smallest size (usually a caption or footnote) and your largest (usually h1 or a hero headline), plus how many steps separate them.

// ratio = (largest / smallest) ^ (1 / numberOfSteps)
function solveRatio(smallestPx, largestPx, steps) {
  return Math.pow(largestPx / smallestPx, 1 / steps);
}

// Example: 12px caption up to 48px h1, across 6 steps
const ratio = solveRatio(12, 48, 6);
console.log(ratio.toFixed(3)); // 1.260 — round to the 1.25 major third

This is the defensive version of ratio selection: instead of eyeballing a scale and hoping the top and bottom land somewhere reasonable, you fix both ends first — driven by real design constraints like "the smallest legible caption on this product is 12px" and "the hero headline must not exceed 48px on a 375px viewport" — and let the ratio fall out of the math. The edge case this handles is a design system with an odd number of steps or an unusual size range that no named ratio fits exactly; round the computed ratio to two or three decimal places and use it directly as --scale-ratio rather than forcing it to the nearest named value, unless consistency with a second product's scale matters more than an exact fit.

For UI-heavy products, always cap the top of the derived scale with clamp() so a ratio solved for desktop doesn't produce oversized text on a narrow viewport — see Fluid Type Scale with clamp() for the exact syntax.

Ratio comparison at two scale steps Table comparing five modular scale ratios by computed size at step three and step five, with a recommended use case for each. Ratio comparison at two scale steps Step 3 Step 5 Best for 1.125 (M2) 20px 29px Dense UI, data tables 1.2 (m3) 23px 40px App UI, dashboards 1.25 (M3) 25px 49px Marketing sites 1.333 (P4) 27px 67px Editorial, blogs 1.618 (golde… 34px 110px Display-heavy landing pages
Higher ratios reach large display sizes in fewer steps but risk overshoot.

Step-count trade-offs

A larger ratio needs fewer steps to cover the same size range, which sounds efficient until you notice it also means fewer distinct sizes are available for intermediate hierarchy — you run out of "medium" options. A 1.125 scale might need nine steps to go from 12px to 36px; a 1.5 scale reaches the same range in about three. If your design has many text roles (eyebrow, subtitle, card title, section heading, page title, hero), a smaller ratio with more steps gives you a size for each role without any two looking identical. If your design has few roles and wants dramatic contrast between them (a marketing landing page with body copy and one huge headline), a larger ratio with fewer steps is the better fit.

A practical rule: count your actual text roles first, then pick the smallest ratio that gives every adjacent pair of roles a visually distinct size (roughly a 10–15% difference is the minimum humans reliably perceive at typical UI sizes). Going larger than that minimum buys drama; going smaller wastes hierarchy.

Verification

Confirm your chosen ratio produces the sizes you expect, at every step, before shipping it:

  1. Open DevTools → Elements → select each heading element in turn and read the Computed panel's font-size value; it should match --scale-base * ratio^N for that element's step.
  2. In the DevTools Console, paste the solveRatio example above (or a small loop) and print every step's pixel value for your chosen ratio — compare against the rendered computed values to catch any calc() typo.
  3. Resize the viewport to your narrowest supported width and re-check the largest heading's computed size; if you've layered clamp() on top of the scale, verify the clamp minimum, not the raw ratio output, is what renders.
  4. Zoom the page to 200% (a WCAG 1.4.4 reflow check) and confirm every step still scales proportionally — since everything derives from rem, this should hold automatically, but confirm it rather than assuming it.

Common Pitfalls

  • Picking a ratio by eye on one screen size, never testing the top step. A 1.5 ratio looks fine for a subtitle but produces a 121px h1 at step 6 from a 16px base — always print every step's computed value before committing, not just the first one or two.
  • Mixing ratios across a product. Marketing pages built with 1.333 and app UI built with 1.2 in the same design system produce headings that look like they belong to two different products; standardize on one ratio (or two, deliberately: one for marketing, one for app UI) and document which is which.
  • Ignoring step count when comparing ratios. A "1.2 vs 1.333" comparison is meaningless without also fixing the number of steps — the same ratio produces very different top-end sizes across 4 steps vs 8 steps.
  • Forgetting the caption/small end of the scale. Teams tune the heading side of the ratio carefully and then hand-pick an arbitrary small-text size instead of dividing by the same ratio, which breaks the scale's internal consistency and produces a caption that doesn't relate mathematically to body text.
  • Not capping the scale for narrow viewports. A ratio solved for a 1440px desktop hero can produce headline text that overflows a 360px phone screen; pair the scale with clamp() (see the fluid-scale guide) rather than relying on the ratio alone to stay responsive.
Workflow to pick a scale ratio Five-step process for selecting a modular type scale ratio from real UI size constraints. Workflow to pick a scale ratio 1 Count UI levels h1–h6, body, caption 2 List target sizes smallest & largest px 3 Solve for ratio (max/min)^(1/steps) 4 Round to a named scale match m3, P4, etc. 5 Cap with clamp() stop runaway growth
Derive the ratio from your actual size range instead of guessing.

Ratio selection for variable and system-based type

If your product uses a variable font, the ratio decision interacts with the wght and opsz axes in a way static fonts don't have to worry about: a large ratio combined with automatic optical sizing means the top of your scale not only gets bigger but also gets structurally heavier and wider, since opsz typically increases with size. That compounding effect can make an aggressive 1.5 or 1.618 ratio look even more dramatic than the raw pixel numbers suggest, because the glyphs themselves are drawn differently at the largest step. Test your top-of-scale heading with the real variable font loaded, not a static weight, before finalizing the ratio — a scale that looks proportionate in a design mockup using a single static weight can look unbalanced once font-optical-sizing: auto is engaged in the browser.

The same interaction applies in reverse at the bottom of the scale: a caption size derived from dividing by a large ratio can land below the range where a font's opsz axis was designed to remain legible, producing thinner, more condensed glyphs than intended for small text. If your smallest step is below roughly 14px, verify the rendered caption against the font's designed optical-size range rather than assuming the ratio math alone guarantees legibility.

Documenting the ratio for a team

Whatever ratio you land on, record it alongside the reasoning, not just the number. A design-tokens file or a comment above the :root block should state the base size, the ratio, the step count, and the smallest/largest constraints that produced it, so a future contributor changing the base size doesn't have to reverse-engineer why 1.25 was chosen over 1.2. This matters most when the ratio was solved numerically rather than picked from the named table — an unusual value like 1.267 looks arbitrary without the size constraints that generated it, and a teammate is more likely to "round it to something normal" and quietly change the scale's character if the origin isn't documented.

Frequently Asked Questions

Is there a "best" modular scale ratio for every UI? No. The right ratio depends on how many distinct text roles your interface needs and how much visual drama you want between them. Dense, data-heavy UI (dashboards, tables, admin panels) tends toward 1.125–1.2 because it needs many closely-spaced sizes without any looking oversized. Marketing and editorial pages tend toward 1.25–1.333 because they have fewer roles and want strong contrast between body copy and headlines. Treat the table earlier in this guide as a starting point, then verify against your own size range using the solveRatio approach.

Should I use the same ratio for mobile and desktop? Usually yes for the ratio itself, but pair it with clamp() so the computed pixel values shrink on narrow viewports rather than swapping to a second, unrelated scale. Changing the ratio between breakpoints (rather than just the base size or clamp bounds) tends to produce a UI that feels like it has two different type systems, since the visual relationship between headings changes shape, not just size.

Can I use a non-standard ratio like 1.27 instead of a named scale? Yes — a ratio solved from your actual smallest/largest size constraints via (largest/smallest)^(1/steps) is often not a "named" value, and that is fine. Named ratios (1.2, 1.25, 1.333, and so on) are conventions for communicating and comparing scales, not a requirement; the math works identically for any ratio between roughly 1.05 and 1.8. Values outside that range tend to be too subtle to read as a hierarchy or too extreme to fit real layouts.

How many steps should a modular scale have? Most UI type systems need six to eight steps: one or two below body text (caption, footnote), body itself, and three to five above it (subheading through the largest display heading). Fewer than five total steps usually means some text roles are sharing a size that should be distinct; more than nine or ten steps usually means some adjacent sizes are close enough that the difference isn't perceptible, which is a sign to either merge steps or reduce the ratio so the gaps stay meaningful across a shorter range.

Related