Skip to content
WANCODE
BM

Clamp-based Fluid Typography

Font sizes that grow smoothly with the screen between a minimum and a maximum — without breaking browser zoom.

Live preview Open in Playground
fluid-type.css
/*
  Fluid type scale: each step grows smoothly from a 360px viewport
  to a 1280px viewport, then stops.
  Formula: clamp(MIN, INTERCEPT + SLOPE, MAX)
*/
:root {
  --step--1: clamp(0.8rem, 0.76rem + 0.16vw, 0.89rem);
  --step-0: clamp(1rem, 0.9rem + 0.43vw, 1.25rem);
  --step-1: clamp(1.25rem, 1.05rem + 0.87vw, 1.75rem);
  --step-2: clamp(1.56rem, 1.22rem + 1.52vw, 2.44rem);
  --step-3: clamp(1.95rem, 1.38rem + 2.55vw, 3.42rem);
}

body {
  font-size: var(--step-0);
  line-height: 1.6;
}

h1 { font-size: var(--step-3); line-height: 1.1; }
h2 { font-size: var(--step-2); line-height: 1.2; }
h3 { font-size: var(--step-1); line-height: 1.3; }
small { font-size: var(--step--1); }

clamp(MIN, PREFERRED, MAX) returns the preferred value, but never less than MIN and never more than MAX. If the preferred value depends on the viewport width (vw), the font size grows with the screen and stops at both ends.

Where the numbers come from

Take --step-0: we want 16px at a 360px-wide screen and 20px at 1280px. That's a straight line:

  1. Slope = (20 − 16) ÷ (1280 − 360) = 0.00435 → as a viewport unit that's 0.43vw (1vw is 1% of the width).
  2. Intercept = 16 − 0.00435 × 360 ≈ 14.4px ≈ 0.9rem (assuming the default 16px root size).
  3. Result: clamp(1rem, 0.9rem + 0.43vw, 1.25rem). Check it: at 360px, 14.4 + 1.56 ≈ 16px; at 1280px, 14.4 + 5.6 = 20px.

Don't use vw alone

font-size: 3vw ignores the user's font-size setting and barely changes when they zoom, which can fail WCAG 1.4.4 (Resize Text). Mixing a rem part into the preferred value — and using rem for min/max — keeps zoom and user preferences working.

The other steps use the same method with a ratio of 1.25 between steps at the small end and about 1.4 at the large end, so headings grow faster than body text.

Usage

CSS
/* Use the steps anywhere, not just on headings */
.hero-title {
  font-size: var(--step-3);
}

.card p {
  font-size: var(--step--1);
}