Published 2026-02-28 · 3 min read
By Akash Yadav · Frontend Engineer
CSS clamp() Complete Guide for Engineers
clamp(min, preferred, max) is the workhorse of modern fluid interfaces. Learn the mental model, linear preferred values, and testing workflow.
Mental model
clamp() always picks the middle value after guardrails are applied. That sentence is worth re-reading. The preferred value is evaluated first, then the minimum and maximum act as hard stops:
- If
preferred<min→ returnmin - If
preferred>max→ returnmax - Otherwise → return
preferred
This means you can let the preferred value track some external measurement (viewport width, container width, calculation) while guaranteeing that the result never breaks readability or layout constraints.
/* Font size that scales between 1rem and 3rem */
/* Never below 1rem (16px), never above 3rem (48px) */
h1 {
font-size: clamp(1rem, 0.5rem + 2.5vw, 3rem);
}
Use the clamp calculator to prototype ideas quickly without calculating coefficients by hand.
Syntax breakdown
property: clamp(MINIMUM, PREFERRED, MAXIMUM);
| Argument | Role | Common type |
|---|---|---|
MINIMUM | Hard floor — never return less than this | rem or px |
PREFERRED | Fluid middle — usually calc() with vw/cqw | calc(Xrem + Yvw) |
MAXIMUM | Hard ceiling — never return more than this | rem or px |
The preferred value almost always uses a linear combination of a fixed offset (rem) and a fluid multiplier (vw or cqw):
/* preferred = fixed offset + (fluid multiplier × viewport/container width) */
preferred: calc(intercept_rem + coefficient_vw * 1vw)
The key insight: at the minimum viewport, preferred should equal min; at the maximum viewport, preferred should equal max. Everything between is a straight line.
Linear preferred values
The cleanest way to derive the preferred value is to define two anchor points — (viewport_min, size_min) and (viewport_max, size_max) — and calculate the linear interpolation:
slope = (size_max - size_min) / (viewport_max - viewport_min)
intercept = size_min - slope * viewport_min
preferred = calc(intercept_rem + slope_vw * 1vw)
For example, scaling a heading from 1.5rem at 320px to 3rem at 1280px:
slope = (3rem - 1.5rem) / (1280px - 320px) = 1.5rem / 960px ≈ 0.15625 per px
= 0.15625 * 100 ≈ 15.625 per 100px = 15.625vw? No — need to convert:
slope_vw = (1.5 / 960) * 100 = 0.15625rem/px × 100px/vw = 15.625 ...
Wait — let's do this cleanly in CSS units:
slope_as_vw = (3 - 1.5) / (12.8 - 3.2) = 1.5 / 9.6 ≈ 0.15625rem per rem-viewport
= 1.5rem / 9.6rem × 100vw = 15.625 is wrong
The fluid typography generator handles this calculation automatically. You input two anchor points and it outputs the correct clamp() string:
/* Output from fluid generator: 1.5rem at 320px → 3rem at 1280px */
font-size: clamp(1.5rem, 0.9375rem + 1.875vw, 3rem);
Fluid spacing with clamp
clamp() is not limited to font sizes. Any property that benefits from scaling with viewport width is a candidate:
:root {
/* Section padding: 1.5rem on mobile, 4rem on desktop */
--section-padding: clamp(1.5rem, 1rem + 2.5vw, 4rem);
/* Card gap: 1rem on mobile, 2rem on desktop */
--card-gap: clamp(1rem, 0.75rem + 1.25vw, 2rem);
/* Heading margin: 2rem on mobile, 3.5rem on desktop */
--heading-margin: clamp(2rem, 1.5rem + 2.5vw, 3.5rem);
}
.section {
padding-block: var(--section-padding);
}
.card-grid {
gap: var(--card-gap);
}
A spacing system built on clamp() tokens eliminates entire categories of breakpoint overrides for spacing adjustments.
Common patterns and recipes
Fluid body copy:
body {
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
/* Subtle: 16px → 18px between 320px and 1200px viewports */
}
Fluid display heading:
.hero-title {
font-size: clamp(2rem, 1rem + 5vw, 5rem);
line-height: clamp(1.1, 1.1, 1.2); /* minor line-height tightening */
}
Fluid container padding:
.container {
padding-inline: clamp(1rem, 5vw, 3rem);
max-width: 72rem;
margin-inline: auto;
}
This single declaration handles the full range from 320px mobile (16px padding) to wide desktop (48px padding) with no breakpoints.
Fluid border-radius:
.card {
border-radius: clamp(0.5rem, 1vw, 1.5rem);
}
Testing checklist
Before shipping fluid values, run through this quick checklist:
- Shrink to 320px: no clipping, no overflow caused by large vw coefficients.
- Expand to 2560px+: type and spacing do not become grotesquely large beyond the max clamp value.
- Zoom to 200%: font sizes still honour minimum sizes at the zoomed viewport.
- Resize continuously: open browser DevTools and drag the viewport slowly — confirm the value interpolates smoothly with no jumps.
- Validate contrast: when type scales down toward minimum, run color pairs through the WCAG contrast checker to ensure the smaller size still passes AA.
- Test with large user font-size: set browser default font-size to 20px and verify min/max values still make sense.
Browser compatibility
| Feature | Chrome | Firefox | Safari | Notes |
|---|---|---|---|---|
clamp() | 79+ | 75+ | 13.1+ | Safe in all modern browsers |
min() / max() | 79+ | 75+ | 11.1+ | Safe in all modern browsers |
clamp() in media queries | 79+ | 75+ | 13.1+ | Works but rem is preferred |
No polyfill is needed in 2026. Global support is above 97%.
Key takeaways
- clamp(min, preferred, max) — the preferred is evaluated first, then clamped between min and max.
- Preferred value is usually
calc(Xrem + Yvw)— combining a fixed REM offset with a fluid VW multiplier. - Use the fluid generator to derive coefficients from two anchor points without manual calculation.
- Works for spacing and radius, not just typography — any length value benefits from fluid scaling.
- Test at 320px and 200% zoom — the two most important edge cases for fluid values.
Continue with Fluid typography explained for worked examples with the generator.
Frequently asked questions
- Can the preferred value use calc()?
- Yes — `calc(0.5rem + 2vw)` is a common pattern. Keep units consistent and mind browser support (baseline in 2026 is excellent). The preferred value is evaluated first, then clamped.
- Does clamp() work for properties other than font-size?
- Yes. clamp() works for any length, percentage, or number value: width, height, padding, margin, gap, border-radius, line-height, and more. It is not limited to typography.
- How is clamp() different from min() and max()?
- `min(a, b)` picks the smaller value; `max(a, b)` picks the larger. `clamp(min, preferred, max)` is equivalent to `max(min, min(preferred, max))` — it chains both operations. Use clamp when you need both a floor and a ceiling.
- What is the recommended minimum viewport width for clamp() fluid type?
- 320px is the traditional minimum for mobile devices. Many teams use 375px for modern mobile. Whatever you choose, verify the minimum font-size renders comfortably at that viewport width.
- Can I use clamp() for animation values?
- Not directly in CSS transitions or keyframes — clamp() is a static calculation, not an interpolation function. For responsive animation values, use CSS custom properties driven by JavaScript media query listeners.
- Why does my clamp() value not respond at small sizes?
- If the preferred value evaluates to less than the minimum, clamp() returns the minimum. Your vw coefficient may be too small to grow above the minimum within your viewport range. Use the fluid typography generator to calculate the correct coefficient.