← The Library
Typography Fundamentals

Font Size, Scale & Modular Type Systems

Modular scales from minor second to golden ratio, fluid type with clamp(), defining a full type scale with CSS custom properties — practical implementation guide.

9 min

Font Size, Scale & Modular Type Systems

Why Scale Matters

A collection of type sizes is not a type scale. A type scale is a system — a set of sizes with defined mathematical relationships that produce visual harmony and legible hierarchy. Without a scale, font size decisions are made ad hoc: "the heading feels a bit small, make it 3px bigger." With a scale, every size has a reason, every step has a relationship, and the hierarchy is coherent.

Modular scales are the most widely used approach: start with a base size (typically the body text size, commonly 16px or 1rem), then multiply by a fixed ratio to generate each step up or down. The ratio is typically drawn from musical intervals or classical geometric proportions.

The Common Ratios

Scale Name Ratio Character
Minor Second 1.067 Very tight, subtle
Major Second 1.125 Tight, workmanlike
Minor Third 1.200 Balanced, versatile
Major Third 1.250 Clear, comfortable
Perfect Fourth 1.333 Expressive, editorial
Augmented Fourth 1.414 Dramatic
Perfect Fifth 1.500 Bold, high-contrast
Golden Ratio 1.618 Extreme, display use

At 16px base, a Major Third (1.25) scale generates: 10.24 / 12.8 / 16 / 20 / 25 / 31.25 / 39.06 / 48.83px. This is a practical range that covers caption through hero text.

A Perfect Fourth (1.333) scale at 16px generates: 9.02 / 12.02 / 16 / 21.33 / 28.43 / 37.9 / 50.52px — more separation between steps, better for editorial contexts where hierarchy must read clearly at a glance.

The Golden Ratio (1.618) produces dramatic jumps: 6.11 / 9.89 / 16 / 25.88 / 41.89 / 67.77px. Useful for display-only type, impractical for multi-level editorial hierarchies.

Implementing a Scale with CSS Custom Properties

Define the scale as a set of custom properties at the :root level. This creates a single source of truth for all size decisions across your codebase:

/* Perfect Fourth (1.333) scale, 1rem base */
:root {
  --text-xs:   0.75rem;    /*  12px */
  --text-sm:   0.875rem;   /*  14px */
  --text-base: 1rem;       /*  16px */
  --text-md:   1.125rem;   /*  18px */
  --text-lg:   1.333rem;   /*  21.3px */
  --text-xl:   1.777rem;   /*  28.4px */
  --text-2xl:  2.369rem;   /*  37.9px */
  --text-3xl:  3.157rem;   /*  50.5px */
  --text-4xl:  4.209rem;   /*  67.3px */
}

/* Usage */
body        { font-size: var(--text-base); }
p           { font-size: var(--text-base); }
.caption    { font-size: var(--text-sm); }
h3          { font-size: var(--text-lg); }
h2          { font-size: var(--text-xl); }
h1          { font-size: var(--text-2xl); }
.hero-title { font-size: var(--text-3xl); }

Fluid Type with clamp()

A static scale solves hierarchy. It does not solve responsiveness. At a fixed 50px, a h1 that reads correctly on desktop may be overwhelming on a 375px mobile screen. The traditional solution was multiple breakpoints with separate font-size declarations for each. Fluid type with clamp() is better.

The clamp(min, preferred, max) function accepts three values: a minimum, a preferred value (typically a vw-based calculation), and a maximum. The browser calculates the preferred value continuously and applies the result within the min–max range:

/* Fluid h1: scales from 2rem (32px) at small screens to 4rem (64px) at large */
h1 {
  font-size: clamp(2rem, 4vw + 1rem, 4rem);
}

/* Complete fluid scale */
:root {
  --text-base: clamp(1rem,     0.5vw  + 0.875rem, 1.125rem);
  --text-lg:   clamp(1.125rem, 1vw    + 0.9rem,   1.5rem);
  --text-xl:   clamp(1.333rem, 2vw    + 0.9rem,   2rem);
  --text-2xl:  clamp(1.777rem, 3vw    + 1rem,     2.75rem);
  --text-3xl:  clamp(2.369rem, 4vw    + 1rem,     3.5rem);
  --text-4xl:  clamp(3rem,     5vw    + 1rem,     5rem);
}

The formula Xvw + Yrem is commonly used for the preferred value because it maintains a minimum floor (Yrem) at very small viewports while scaling proportionally from there. Tools like Fluid Type Scale Calculator can generate these values with a visual preview.

Optical Sizing and Variable Fonts

Many modern typefaces include an optical size axis (opsz), accessible through font-optical-sizing: auto or font-variation-settings. At display sizes, the typeface applies its display-optimized design (finer serifs, higher contrast, tighter spacing); at body sizes, it applies text-optimized design (sturdier serifs, more open spacing, less contrast). This is the digital equivalent of having separate text and display optical sizes in traditional typography.

/* Enable automatic optical sizing */
h1 {
  font-size: clamp(2rem, 5vw, 4rem);
  font-optical-sizing: auto;
  /* Or explicitly: */
  font-variation-settings: 'opsz' 72;
}

body {
  font-size: 1rem;
  font-optical-sizing: auto;
  /* Or explicitly: */
  font-variation-settings: 'opsz' 16;
}

Google Fonts typefaces with optical sizing include Fraunces, Source Serif 4, Roboto Flex, and several others. Use font-optical-sizing: auto and the browser will set the opsz value based on font-size.

Base Size Considerations

Most screen typography uses 16px as the base font size because it is the browser default and represents a comfortable reading size for most users. Resist the urge to set a smaller base — 14px or 13px is frequently used to "fit more content," but it diminishes readability for users with imperfect vision and violates the spirit of accessible design.

Use rem (root em) units for font sizes rather than px or em:

  • rem values respect user-configured browser base sizes (some users set their browser default to 18px or 20px for accessibility)
  • px ignores user browser font preferences
  • em cascades multiplicatively through nested elements, producing unexpected behavior
/* Correct: rem respects user base font preference */
p { font-size: 1rem; }

/* Wrong: px ignores user accessibility preferences */
p { font-size: 16px; }

Key Takeaways

  • A modular scale defines type sizes through a consistent ratio; Perfect Fourth (1.333) is a reliable starting point
  • Define your scale as CSS custom properties at :root for a maintainable single source of truth
  • clamp(min, preferred, max) creates fluid type that scales continuously between breakpoints
  • Font optical sizing (font-optical-sizing: auto) adapts a variable typeface's design to its rendered size
  • Use rem units, not px, so that your scale respects user browser font preferences

Further Reading