Responsive Design

CSS clamp()

One function that replaces dozens of media queries — constraining any CSS value between a min and a max while scaling fluidly with a preferred value in between.

3
Arguments
0
Media Queries Needed
7
Sections
01 / Overview

A single function for fluid constraints

Before clamp(), building a fluid scale meant stacking media queries: set a font size for mobile, override it at 768 px, override again at 1200 px. The result was a stepped scale — values jumped between breakpoints rather than flowing continuously. Every new breakpoint needed its own rule, and every design change rippled through all of them.

clamp(min, preferred, max) is a CSS math function introduced in the CSS Values and Units Level 4 spec. It evaluates its middle argument — the preferred value — and clamps the result so it never falls below min or exceeds max. The preferred value is typically a viewport-relative expression like 4vw or 2vw + 1rem, which is what makes the output fluid: it grows smoothly with the viewport without ever leaving the guardrails.

Browser support is universal across all modern engines. It works on font-size, padding, margin, gap, width, border-radius — any property that accepts a length, percentage, or number.

02 / Anatomy

Min · Preferred · Max

MIN first argument

The lower bound. The computed value will never be smaller than this. Use a fixed unit (rem, px) to guarantee legibility and touch-target minimums on the smallest screens.

PREFERRED second argument

The ideal value when unconstrained. A viewport-relative expression like 4vw or a calc() expression makes it fluid. This is where the scaling magic lives.

MAX third argument

The upper bound. The computed value will never exceed this. Use a fixed unit to keep text and elements from growing uncomfortably large on ultra-wide displays.

clamp(MIN, PREFERRED, MAX)

Mathematically, clamp(a, b, c) is equivalent to max(a, min(b, c)). The browser evaluates the preferred value first, then clamps it within the min/max range. If min > max, min wins — so always keep min ≤ max.

You can use calc() expressions inside any of the three arguments, and mix units freely — clamp(1rem, 0.5rem + 2vw, 2rem) is perfectly valid. This makes it possible to write preferred values that account for both a base size and a viewport-proportional component.

css
/* Equivalent forms */
.heading {
  font-size: clamp(1.5rem, 4vw, 3rem);

  /* Same thing, written as nested min/max */
  font-size: max(1.5rem, min(4vw, 3rem));
}
03 / Typography

Font sizes that scale without breakpoints

The most common use of clamp() is fluid typography. The goal is a font size that is comfortably readable on a 320 px phone, scales proportionally as the viewport grows, and never becomes overwhelming on a 2560 px monitor.

A practical formula mixes a fixed baseline with a viewport-proportional amount: clamp(min, 0.5rem + 2vw, max). The 0.5rem provides a stable floor so the font does not collapse to zero on very narrow contexts, while the 2vw component supplies the fluid growth.

Live Demo Drag right edge to resize
Fluid Headline
font-size: clamp(18px, 4vw, 48px) — px
css
/* Fluid type scale */
:root {
  --text-xs    : clamp(0.75rem,  0.7rem + 0.25vw,  0.875rem);
  --text-sm    : clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
  --text-base  : clamp(1rem,     0.9rem + 0.5vw,   1.125rem);
  --text-lg    : clamp(1.125rem, 1rem   + 0.75vw,  1.5rem);
  --text-xl    : clamp(1.5rem,   1.25rem + 1.5vw,  2.25rem);
  --text-2xl   : clamp(2rem,     1.5rem + 2.5vw,  3.5rem);
  --text-3xl   : clamp(2.5rem,   1.75rem + 4vw,   5rem);
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
p  { font-size: var(--text-base); }
04 / Spacing

Padding and gaps that breathe

Layout spacing has the same problem as typography: a padding: 80px that looks generous on a desktop becomes suffocating on a 375 px phone. The traditional fix is padding: 24px at mobile and padding: 80px at desktop — two rules, one breakpoint. With clamp() you replace both with a single declaration.

Viewport-width units work especially well for horizontal space because page sections and heroes grow wider with the viewport. Combining a small fixed floor with a vw-based preferred value gives you spacing that feels proportional at every width without any manual tuning.

Before — with media queries
.section {
  padding: 24px 20px;
}

@media (min-width: 768px) {
  .section {
    padding: 48px 40px;
  }
}

@media (min-width: 1200px) {
  .section {
    padding: 80px 80px;
  }
}
After — with clamp()
.section {
  padding:
    clamp(24px, 6vw, 80px)
    clamp(20px, 5vw, 80px);
}
css
/* Fluid spacing scale */
:root {
  --space-xs  : clamp(4px,   0.5vw,  8px);
  --space-sm  : clamp(8px,   1vw,   16px);
  --space-md  : clamp(16px,  2vw,   32px);
  --space-lg  : clamp(24px,  4vw,   64px);
  --space-xl  : clamp(40px,  6vw,   96px);
  --space-2xl : clamp(64px,  8vw,  128px);
}

/* Used in layout */
.hero    { padding: var(--space-2xl) var(--space-xl); }
.section { padding: var(--space-xl)  var(--space-lg); }
.card    { padding: var(--space-lg)  var(--space-md); }
05 / Use Cases

Where clamp() fits naturally

Any CSS property that accepts a length benefits from clamp() when the value should scale with the viewport. The patterns below cover the most common applications.

Fluid Typography font-size: clamp(1rem, 0.5rem + 2vw, 2rem);

Scale headings and body text continuously without breakpoints.

Section Padding padding: clamp(32px, 6vw, 96px) clamp(20px, 5vw, 80px);

Section breathing room that adapts to screen real estate.

Grid Gap gap: clamp(12px, 2vw, 32px);

Card and grid gaps that tighten on mobile and open up on desktop.

Max-width Container width: clamp(280px, 90vw, 1200px);

Replace the classic max-width + margin: auto pattern.

Line Length max-width: clamp(45ch, 60%, 75ch);

Keep body text within the optimal 45–75 character reading measure.

Border Radius border-radius: clamp(4px, 1vw, 16px);

Rounded corners that scale gracefully with component size.

06 / Pitfalls

Common mistakes and how to avoid them

Min greater than max. If you write clamp(2rem, 4vw, 1rem) the min exceeds the max, and the browser will resolve to the min value always. Double-check that argument one is always ≤ argument three before shipping.

Viewport units and zooming. Using pure vw as the preferred value can break browser zoom. A font-size of 4vw won't grow when a user zooms in — because the viewport width doesn't change when you zoom. The fix is to mix a rem or em component: 0.5rem + 2vw scales with both viewport width and user zoom settings.

Accessibility and minimum sizes. The min argument exists precisely to protect users. Text should never drop below 0.875rem (14 px at the browser default). Touch targets need at least 44px. Treat the min not as a design decision but as a hard safety contract.

Over-clamp everything. Not every value needs fluid sizing. Icon sizes, border widths, and most component-level details are fine as fixed values. Reach for clamp() when a value genuinely needs to scale across a wide viewport range — not by default.

css
/* ✗ Zoom-unfriendly — pure vw breaks browser zoom */
.bad {
  font-size: clamp(14px, 4vw, 32px);
}

/* ✓ Zoom-friendly — rem base + vw growth */
.good {
  font-size: clamp(0.875rem, 0.5rem + 2vw, 2rem);
}

/* ✗ Min > Max — always resolves to min */
.broken {
  font-size: clamp(2rem, 3vw, 1rem);  /* always 2rem */
}
07 / Takeaway

Fewer rules, more fluidity

clamp() is not a replacement for every responsive technique — it is the right tool when a value should scale continuously with the viewport. Used thoughtfully, it eliminates entire categories of breakpoint rules, makes design systems more predictable, and produces interfaces that feel native at every screen size rather than snapping between fixed states.

Start with your type scale and section spacing. Those two applications alone will eliminate the majority of your layout breakpoints. Then move to gaps, container widths, and line lengths. Build the min and max from actual design constraints — the smallest acceptable and the largest comfortable — and let the preferred value do the fluid work in between.

Always pair vw-based preferred values with a rem component to respect browser zoom. Always set a sensible min for accessibility. And keep the function where it belongs: property values and custom properties, not scattered inline across every element.