Type something to search...
How to Build a Modular Type Scale for Your Website?

How to Build a Modular Type Scale for Your Website?

Open the stylesheet of a site that's been through a few years of edits and search for font-size. You'll often find 20 or 30 distinct values: 13px, 14px, 15px, 17px, 18px, 21px, 22px, 26px, 28px, and so on, each added by someone who needed something "a bit bigger." Headings don't relate to each other, components look subtly mismatched, and nobody can say which size a new card title should use. A modular type scale replaces that pile of one-off values with a small, related set of sizes that you choose from deliberately.

This article explains what a modular type scale is, how to choose a base size and ratio, how to calculate the steps, how to map them to headings and UI text, and how to implement the scale in plain CSS, Tailwind CSS v4, and a WordPress block theme. It also shows how to make the scale responsive.

What Is a Modular Type Scale?

A modular type scale is a sequence of font sizes where each size is the previous one multiplied by a fixed ratio. You start from a base size, usually your body text, and multiply up for headings and divide down for small text.

The formula for any step is:

size = base × ratio^step

With a base of 16px and a ratio of 1.25, the steps look like this:

StepCalculationSizerem
−216 × 1.25^−210.24px0.64rem
−116 × 1.25^−112.8px0.8rem
016 × 1.25^016px1rem
116 × 1.25^120px1.25rem
216 × 1.25^225px1.5625rem
316 × 1.25^331.25px1.9531rem
416 × 1.25^439.06px2.4414rem
516 × 1.25^548.83px3.0518rem

Because every size shares the same ratio, the differences between levels feel consistent. The idea comes from music, where many common scale ratios match musical intervals, and from traditional print typography, where type sizes were historically chosen from a fixed set.

The main benefits are practical:

  • Fewer decisions. Designers and developers pick from six to eight sizes instead of inventing new ones.
  • Clear hierarchy. Each step is noticeably different from the next, which makes typographic hierarchy easier to build.
  • Consistency across components. A card title, a modal heading, and a sidebar heading can all use the same step.
  • Easier maintenance. Changing the base size or ratio updates the whole system.

Choosing a Ratio

The ratio controls how dramatic the contrast between levels is. Common ratios are named after musical intervals:

RatioNameCharacterGood for
1.067Minor secondVery subtleDense dashboards with many levels
1.125Major secondSubtle, compactApps, admin UIs, mobile
1.2Minor thirdBalancedMobile content, documentation
1.25Major thirdClear, versatileBlogs, marketing sites, most content
1.333Perfect fourthStrong contrastEditorial sites, landing pages on desktop
1.414Augmented fourthDramaticLarge-screen marketing, few levels
1.5Perfect fifthVery dramaticPosters, hero-led pages
1.618Golden ratioExtremeDisplay typography with only 2–3 levels

Here's how the same 16px base grows with four of these ratios:

Step1.1251.21.251.333
−114.22px13.33px12.8px12px
016px16px16px16px
118px19.2px20px21.33px
220.25px23.04px25px28.43px
322.78px27.65px31.25px37.9px
425.63px33.18px39.06px50.52px
528.83px39.81px48.83px67.34px

Some guidelines for picking:

  1. More heading levels need a smaller ratio. A documentation site using H1 through H5 will run out of room with 1.5; the top step would be huge. With 1.2, five steps still fit.
  2. Small screens need a smaller ratio. A 50px H1 at a 1.333 ratio is too big on a 375px phone. That's why responsive scales often use 1.2 on mobile and 1.25 to 1.333 on desktop.
  3. Content density matters. Dashboards and apps want compact, subtle steps. Editorial and marketing pages can afford drama.
  4. The typeface matters. Fonts with a large x-height look bigger at the same size, so they may work with a smaller ratio. Display faces with strong personality can carry larger jumps.

If you're unsure, start with 1.25 for content sites and 1.2 for apps. Adjust after you see real content in it.

Choosing the Base Size

The base is step 0, and it's almost always your body text size. For content sites, 16–18px (1–1.125rem) is a sensible range, with 16px as the minimum. The post on the best font size for body text covers how to decide.

Always express the base in rem, not px, so the whole scale respects the user's browser font size preference.

Map Steps to Roles

A scale on its own is just numbers. The next step is assigning each number to a role:

RoleStepSize at 1.25 ratio
Fine print, legal−112.8px
Captions, metadata, labels−112.8px (or 14px)
Body text016px
Lead paragraph, H5, H6120px
H4, card titles225px
H3331.25px
H2439.06px
H1548.83px
Hero display661.04px

You don't have to use every step, and you can break the pure scale where it helps readability. Captions at 12.8px are on the small side, so many teams round step −1 up to 14px (0.875rem). That kind of practical adjustment is fine; the scale is a tool, not a law.

Also, decouple visual size from semantic level. An H2 in a sidebar might use step 2, not step 4. Use classes or tokens for size so that you can keep a correct heading outline without fighting the scale.

Implement the Scale in CSS

Option 1: Precomputed custom properties

The simplest approach is to compute the values once and store them as custom properties:

:root {
  --font-size-sm: 0.8rem;      /* step -1 */
  --font-size-base: 1rem;      /* step 0 */
  --font-size-md: 1.25rem;     /* step 1 */
  --font-size-lg: 1.5625rem;   /* step 2 */
  --font-size-xl: 1.9531rem;   /* step 3 */
  --font-size-2xl: 2.4414rem;  /* step 4 */
  --font-size-3xl: 3.0518rem;  /* step 5 */
}

body { font-size: var(--font-size-base); line-height: 1.6; }
h1 { font-size: var(--font-size-3xl); line-height: 1.1; }
h2 { font-size: var(--font-size-2xl); line-height: 1.15; }
h3 { font-size: var(--font-size-xl); line-height: 1.2; }
h4 { font-size: var(--font-size-lg); line-height: 1.3; }
.lead { font-size: var(--font-size-md); }
small { font-size: var(--font-size-sm); }

Note the line heights: they get tighter as size increases. Large headings with body-text leading look loose and disconnected.

Option 2: Calculate in CSS with pow()

CSS now has exponential functions, so you can define only the base and ratio and let the browser compute each step. pow() is supported in all current major browsers.

:root {
  --type-base: 1rem;
  --type-ratio: 1.25;

  --step--1: calc(var(--type-base) * pow(var(--type-ratio), -1));
  --step-0: var(--type-base);
  --step-1: calc(var(--type-base) * pow(var(--type-ratio), 1));
  --step-2: calc(var(--type-base) * pow(var(--type-ratio), 2));
  --step-3: calc(var(--type-base) * pow(var(--type-ratio), 3));
  --step-4: calc(var(--type-base) * pow(var(--type-ratio), 4));
  --step-5: calc(var(--type-base) * pow(var(--type-ratio), 5));
}

@media (max-width: 40rem) {
  :root {
    --type-ratio: 1.2;
  }
}

The media query changes only the ratio, and every step updates automatically. That's a compact way to get a tighter scale on phones.

Option 3: Generate it in a build step

If your tokens live in JSON or TypeScript, generate the scale there:

type Scale = Record<string, string>;

export function modularScale(
  base = 1,
  ratio = 1.25,
  steps: Record<string, number> = { sm: -1, base: 0, md: 1, lg: 2, xl: 3, "2xl": 4, "3xl": 5 },
): Scale {
  return Object.fromEntries(
    Object.entries(steps).map(([name, step]) => [
      name,
      `${Number((base * Math.pow(ratio, step)).toFixed(4))}rem`,
    ]),
  );
}

// modularScale() -> { sm: "0.8rem", base: "1rem", md: "1.25rem", lg: "1.5625rem", ... }

This fits naturally into a token pipeline. See how to set up typography tokens in a design system for the wider setup.

Make the Scale Responsive with clamp()

A single ratio rarely suits every screen. A better approach is a fluid modular scale: a smaller ratio and base on mobile, a larger ratio and base on desktop, with each step interpolating between the two using clamp().

For example, a scale that runs from a 16px base with a 1.2 ratio at 320px to an 18px base with a 1.25 ratio at 1280px:

:root {
  --step--1: clamp(0.8333rem, 0.8111rem + 0.1111vw, 0.9rem);
  --step-0: clamp(1rem, 0.9583rem + 0.2083vw, 1.125rem);
  --step-1: clamp(1.2rem, 1.1312rem + 0.3438vw, 1.4062rem);
  --step-2: clamp(1.44rem, 1.3341rem + 0.5297vw, 1.7578rem);
  --step-3: clamp(1.728rem, 1.5716rem + 0.7821vw, 2.1973rem);
  --step-4: clamp(2.0736rem, 1.8493rem + 1.1216vw, 2.7466rem);
  --step-5: clamp(2.4883rem, 2.1734rem + 1.5748vw, 3.4332rem);
}

Headings grow faster than body text as the screen widens, so the hierarchy becomes more pronounced where there's room. The math behind these values, plus the accessibility limits to respect, is in fluid typography with CSS clamp(). Tools such as Utopia generate fluid scales like this from four inputs.

Implement the Scale in Tailwind CSS v4

Tailwind v4 is configured in CSS with @theme. Override the default --text-* variables with your scale, and the text-* utilities follow:

@import "tailwindcss";

@theme {
  --text-*: initial; /* remove Tailwind's default sizes */

  --text-sm: 0.8rem;
  --text-sm--line-height: 1.5;
  --text-base: 1rem;
  --text-base--line-height: 1.6;
  --text-md: 1.25rem;
  --text-md--line-height: 1.5;
  --text-lg: 1.5625rem;
  --text-lg--line-height: 1.3;
  --text-xl: 1.9531rem;
  --text-xl--line-height: 1.2;
  --text-2xl: 2.4414rem;
  --text-2xl--line-height: 1.15;
  --text-3xl: 3.0518rem;
  --text-3xl--line-height: 1.1;
}

Setting --text-*: initial clears Tailwind's built-in sizes so the team can only pick from your scale. Leave that line out if you'd rather extend the defaults.

Implement the Scale in a WordPress Block Theme

In a block theme, define the scale as font size presets in theme.json. With version 3, you can use the built-in fluid typography support:

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "typography": {
      "fluid": true,
      "defaultFontSizes": false,
      "fontSizes": [
        { "name": "Small", "slug": "small", "size": "0.8rem", "fluid": false },
        { "name": "Base", "slug": "base", "size": "1rem", "fluid": false },
        { "name": "Medium", "slug": "medium", "size": "1.25rem", "fluid": { "min": "1.2rem", "max": "1.25rem" } },
        { "name": "Large", "slug": "large", "size": "1.5625rem", "fluid": { "min": "1.44rem", "max": "1.5625rem" } },
        { "name": "X-Large", "slug": "x-large", "size": "1.9531rem", "fluid": { "min": "1.728rem", "max": "1.9531rem" } },
        { "name": "XX-Large", "slug": "xx-large", "size": "2.4414rem", "fluid": { "min": "2.0736rem", "max": "2.4414rem" } }
      ]
    }
  },
  "styles": {
    "elements": {
      "h1": { "typography": { "fontSize": "var(--wp--preset--font-size--xx-large)" } },
      "h2": { "typography": { "fontSize": "var(--wp--preset--font-size--x-large)" } },
      "h3": { "typography": { "fontSize": "var(--wp--preset--font-size--large)" } }
    }
  }
}

The fluid objects let WordPress generate clamp() values from a mobile size (here, the 1.2 ratio) and a desktop size (the 1.25 ratio). Setting defaultFontSizes to false hides WordPress's own presets so editors only see your scale.

Test and Refine the Scale

A scale that looks right on a specimen sheet can still feel wrong with real content. Before you commit:

  1. Set real content, not lorem ipsum. Long headings, short headings, and headings with numbers all behave differently.
  2. Check every step at 320px. Does the H1 wrap into four or more lines? Reduce the ratio on mobile.
  3. Compare adjacent steps side by side. If H3 and H4 are hard to tell apart, increase the ratio or make one bolder.
  4. Check the smallest step. Captions and labels below 14px are hard for many readers. Round small steps up.
  5. Zoom to 200%. Because the scale is in rem, everything should double cleanly.
  6. Count the sizes in use. After a few months, search your CSS for font-size values outside the scale and refactor them.

Modular Type Scale FAQ

There is no single best ratio. A major third at 1.25 is a versatile default for blogs and marketing sites, while a minor third at 1.2 or a major second at 1.125 suits apps and mobile screens. Larger ratios like 1.333 or 1.5 work when you only need a few heading levels on large screens.

Most websites need six to eight sizes: one or two below the base for captions and labels, the base for body text, and four or five above it for headings and display text. More steps than that usually means some sizes are too similar to be useful.

Rounding to whole pixels or tidy rem values is fine and often helpful, especially for small sizes like captions. Browsers render fractional sizes without problems, so rounding is a matter of convenience and readability of your code rather than rendering quality.

Yes, and it is usually a good idea. A smaller ratio on mobile keeps headings from becoming too large on narrow screens, while a larger ratio on desktop creates stronger hierarchy. A fluid scale built with clamp interpolates smoothly between the two.

No. Many sites only style H1 through H4 distinctly and set H5 and H6 at the base or first step with bold weight. Also remember that semantic heading level and visual size are separate decisions, so use classes or tokens to apply sizes.

Yes. The pow function is part of the CSS Values and Units Level 4 exponential functions and is supported in all current major browsers. If you must support older browsers, precompute the values and store them as plain custom properties instead.

Conclusion

A modular type scale gives you a small set of related font sizes built from a base and a ratio. Pick a base of 16–18px in rem, choose a ratio that fits your content density and screen size, map the steps to roles, and allow yourself sensible exceptions like rounding small text up. The result is consistent hierarchy and far fewer arbitrary values.

Implement the scale wherever your design decisions live: custom properties or pow() in plain CSS, @theme variables in Tailwind v4, or font size presets in WordPress theme.json. Make it fluid with clamp() so it tightens on phones and opens up on desktops, then test it with real content at 320px and 200% zoom before you lock it in.

Here are some useful references for going deeper on modular type scales:

  1. Utopia: Fluid type scale calculator — generates fluid modular scales with clamp output from a base and two ratios.
  2. MDN Web Docs: pow() — syntax and support for the CSS exponential function.
  3. Tailwind CSS Docs: Theme variables — how to override and extend the text size namespace in Tailwind v4.
  4. WordPress Developer Resources: Theme.json typography settings — font size presets and fluid typography in block themes.
  5. Type Scale: typescale.com — a visual tool for previewing scales with different ratios and fonts.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading