Skip to main content

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 → return min
  • If preferred > max → return max
  • 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);
ArgumentRoleCommon type
MINIMUMHard floor — never return less than thisrem or px
PREFERREDFluid middle — usually calc() with vw/cqwcalc(Xrem + Yvw)
MAXIMUMHard ceiling — never return more than thisrem 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

FeatureChromeFirefoxSafariNotes
clamp()79+75+13.1+Safe in all modern browsers
min() / max()79+75+11.1+Safe in all modern browsers
clamp() in media queries79+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.