Breakpoint typography jumps: 32px on a phone, then suddenly 48px at 768px. Fluid typography grows smoothly with the viewport instead, so every screen width gets a size that fits — with less CSS.
The building block: clamp()
font-size: clamp(2rem, 1.5rem + 2.5vw, 3rem);
clamp(minimum, preferred, maximum) never goes below the minimum or above the maximum, and in between follows the preferred value. The preferred part is a straight line: a fixed rem amount plus a share of the viewport width.
The math, once
Pick a size at a small viewport (say 32px at 360px) and at a large one (48px at 1280px). The slope is (48 − 32) / (1280 − 360) ≈ 0.0174, which is 1.74vw. The intercept is 32 − 0.0174 × 360 ≈ 25.7px ≈ 1.61rem. So:
font-size: clamp(2rem, 1.61rem + 1.74vw, 3rem);
Scale the ratio, not just the size
Headings need more contrast with body text on a big screen than on a phone. A good pattern: use a tighter ratio on mobile (like 1.2) and a bolder one on desktop (like 1.333). Small steps barely change, while display sizes grow a lot.
Keep it accessible
- Always include a rem part in the preferred value. Pure vw values ignore the user’s font-size setting, and zooming stops working (WCAG 1.4.4).
- Use rem for the minimum and maximum, never px, for the same reason.
- Tighten line-height as size grows: around 1.5 for body, 1.1–1.2 for large headings.
- Test at 200% zoom: text should still grow.
Turn it into tokens
Store each step as a custom property (--text-sm, --text-base, --text-xl…) or as design tokens, so designers and developers talk about the same sizes. The generator exports both.