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.
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:
remvalues respect user-configured browser base sizes (some users set their browser default to 18px or 20px for accessibility)pxignores user browser font preferencesemcascades 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
:rootfor 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
remunits, notpx, so that your scale respects user browser font preferences