Fluid Typography with CSS clamp() — Scale Fonts Without Media Queries
CSS clamp() creates fluid typography that scales smoothly between minimum and maximum font sizes without media queries. Here's how to write fluid type scales, calculate clamp()...
clamp(min, preferred, max) sets a value that scales linearly between a minimum and maximum based on a viewport dimension. Applied to font-size, it creates fluid typography that’s always readable without media query breakpoints.
Use the PX to REM Converter to convert pixel values for your type scale.
clamp() syntax
font-size: clamp(minimum, preferred, maximum);
- minimum: smallest possible font size
- preferred: a viewport-relative expression that scales with screen width
- maximum: largest possible font size
h1 {
font-size: clamp(1.75rem, 4vw, 3rem);
/* 28px scales 48px */
}
At viewport widths:
- Narrow (320px): 4vw = 12.8px → uses minimum 28px
- Medium (700px): 4vw = 28px → uses preferred ~28px
- Wide (1200px): 4vw = 48px → uses maximum 48px
Calculating the preferred expression
The preferred value should hit the minimum at the minimum viewport and the maximum at the maximum viewport:
Given:
min-size: 18px = 1.125rem
max-size: 28px = 1.75rem
min-viewport: 320px
max-viewport: 1200px
slope = (max-size - min-size) / (max-viewport - min-viewport)
= (28 - 18) / (1200 - 320)
= 10 / 880
= 0.01136
intercept = min-size - slope × min-viewport
= 18 - 0.01136 × 320
= 18 - 3.636
= 14.364px = 0.898rem
preferred = 0.01136 × 100vw + 0.898rem
= 1.136vw + 0.898rem
Result:
font-size: clamp(1.125rem, 1.136vw + 0.898rem, 1.75rem);
Complete fluid type scale
:root {
/* Mobile → Desktop scale */
--text-sm: clamp(0.875rem, 0.8vw + 0.7rem, 1rem);
--text-base: clamp(1rem, 1vw + 0.8rem, 1.25rem);
--text-lg: clamp(1.125rem, 1.25vw + 0.9rem, 1.5rem);
--text-xl: clamp(1.25rem, 1.5vw + 1rem, 1.75rem);
--text-2xl: clamp(1.5rem, 2vw + 1.1rem, 2.25rem);
--text-3xl: clamp(1.875rem, 3vw + 1.2rem, 3rem);
--text-4xl: clamp(2.25rem, 4vw + 1.4rem, 3.75rem);
--text-5xl: clamp(3rem, 6vw + 1.5rem, 5rem);
}
body { font-size: var(--text-base); }
h1 { font-size: var(--text-4xl); }
h2 { font-size: var(--text-3xl); }
h3 { font-size: var(--text-2xl); }
h4 { font-size: var(--text-xl); }
h5 { font-size: var(--text-lg); }
small{ font-size: var(--text-sm); }
Tools for generating clamp() values
Rather than calculating manually, use these tools:
Utopia (utopia.fyi) — generates complete fluid type and space scales. Input your min/max viewport and min/max font sizes.
CSS Clamp Calculator — enter three sizes and two viewport widths, get the clamp() expression.
JavaScript formula:
function fluidClamp({ minSize, maxSize, minWidth = 320, maxWidth = 1200, unit = 'rem', base = 16 }) {
const pxToRem = px => px / base;
const slope = (maxSize - minSize) / (maxWidth - minWidth);
const intercept = minSize - slope * minWidth;
const minRem = pxToRem(minSize);
const maxRem = pxToRem(maxSize);
const slopeVw = slope * 100;
const interceptRem = pxToRem(intercept);
return `clamp(${minRem}rem, ${slopeVw.toFixed(4)}vw + ${interceptRem.toFixed(4)}rem, ${maxRem}rem)`;
}
fluidClamp({ minSize: 18, maxSize: 28 });
// "clamp(1.125rem, 1.1364vw + 0.8977rem, 1.75rem)"
Fluid spacing with clamp()
The same technique applies to padding, margin, and gap:
:root {
--space-sm: clamp(0.5rem, 1vw + 0.25rem, 1rem);
--space-md: clamp(1rem, 2vw + 0.5rem, 2rem);
--space-lg: clamp(1.5rem, 3vw + 0.75rem, 3rem);
--space-xl: clamp(2rem, 4vw + 1rem, 4rem);
}
section {
padding: var(--space-xl) var(--space-md);
}
.card-grid {
gap: var(--space-md);
}
Line height for fluid type
/* Tight line height for headings, looser for body: */
h1 {
font-size: var(--text-4xl);
line-height: 1.1; /* tight */
letter-spacing: -0.02em;
}
p {
font-size: var(--text-base);
line-height: 1.7; /* looser for readability */
}
Testing fluid typography
// Check font sizes at various viewport widths:
const breakpoints = [320, 480, 768, 1024, 1440];
breakpoints.forEach(width => {
// Simulate viewport width:
window.innerWidth = width;
const el = document.querySelector('h1');
const size = window.getComputedStyle(el).fontSize;
console.log(`${width}px → ${size}`);
});
Related tools
- PX to REM Converter — convert pixels to rem
- rem, px, em Explained — CSS unit comparison
- REM-Based Typography — scalable font sizes
Related posts
- CSS Units Guide — px, em, rem, vw, vh, and When to Use Each — CSS has absolute units (px), relative to font-size (em, rem), viewport units (vw…
- CSS Design Tokens — Define Spacing, Colors, and Typography with Custom Properties — CSS design tokens store reusable values as custom properties (CSS variables). Le…
- Font Size Accessibility — Minimum Sizes, User Zoom, and WCAG Requirements — WCAG 2.1 doesn't mandate a specific minimum font size, but best practice is 16px…
- CSS Grid Layout — How to Build Two-Dimensional Layouts — CSS Grid creates two-dimensional layouts with explicit rows and columns. Here's …
- REM-Based Typography — Scalable Font Sizes with rem Units — Using rem for font sizes creates accessible, scalable typography. Text inherits …
Related tool
Convert px to rem, em, and percent — and vice versa. Configurable root font size. Bulk conversion mode for entire stylesheets.
Written by Mian Ali Khalid. Part of the Frontend & Design pillar.