CSS clamp() takes three values, a minimum, a preferred value and a maximum, and returns the preferred value held inside that range. Write font-size: clamp(1.5rem, 1rem + 2vw, 3rem) and the text grows with the window, but never drops below 1.5rem or climbs past 3rem. One declaration replaces a stack of media queries.

Most guides stop at font size. This one covers what goes wrong in practice: how to read a clamp value, why vw-only text fails zoom, how to use clamp for padding and widths, and where the function quietly causes overflow. If you only need the numbers, the clamp() calculator generates the value for you.

The Quick Answer

h1 {
  font-size: clamp(1.5rem, 1rem + 2vw, 3rem);
}

With a 16px root, the preferred value 1rem + 2vw equals 16px plus 2% of the viewport width. That reaches the 24px minimum at a 400px viewport and the 48px maximum at 1600px. Below 400px the heading stays at 24px, above 1600px it stays at 48px, and in between it scales in a straight line.

This heading uses clamp(1rem, 0.75rem + 1vw, 1.5rem)

Resize your browser window and watch it grow and shrink between 16px and 24px.

How clamp() Thinks: max() Wrapped Around min()

The easiest way to predict clamp is to know what it is shorthand for:

/* These two declarations are identical */
width: clamp(20rem, 50vw, 40rem);
width: max(20rem, min(50vw, 40rem));

The inner min() applies the ceiling, then the outer max() applies the floor. Because the floor is applied last, the minimum wins whenever the two collide. That detail explains several bugs later in this guide.

Syntax Rules and Common Mistakes

The three arguments are separated by commas. Inside any argument you can do math with +, -, * and /, mix units freely, and skip calc(). Most clamp bugs come from a handful of mistakes:

MistakeWhat happensFix
1rem+2vwThe declaration is invalid and silently ignoredPut spaces around + and -, as in 1rem + 2vw
clamp(16, 2vw, 32)Invalid, because lengths need unitsWrite 16px or 1rem
clamp(1rem, 2rem, 3rem)The size never changes, because the preferred value has no viewport unitAdd vw to the preferred value
clamp(3rem, 2vw, 1rem)Minimum is larger than maximum, so 3rem always winsOrder the values small, preferred, large
padding-top: clamp(1rem, 5%, 3rem)The percentage resolves against the container width, not its heightUse vw or rem for vertical spacing

Watch the percentage base: in font-size a percentage is relative to the parent's font size. In width it is relative to the containing block's width. The same 5% means different things on different properties.

Reading a clamp() Value

Here is a value for a font that should go from 16px at a 400px viewport to 24px at a 1200px viewport:

font-size: clamp(1rem, 0.75rem + 1vw, 1.5rem);

Read the preferred value as two parts. The 0.75rem is a fixed starting offset (12px). The 1vw is the growth rate: every 100px of viewport adds 1px. Here is what that produces:

ViewportPreferred (12px + 1vw)Result after clamp
320px15.2px16px (minimum)
400px16px16px
800px20px20px
1200px24px24px
1600px28px24px (maximum)

You rarely need to work this out by hand. Pick your two sizes and two viewport widths in the clamp() calculator and copy the result.

Fluid font-size With clamp()

Headings gain the most from a fluid font-size. They need to be large on a desktop and compact on a phone, and the steps in between are hard to cover with breakpoints. A common approach is a small set of size tokens built from custom properties:

:root {
  /* Generated for a 360px to 1200px viewport range */
  --step-0: clamp(1rem, 0.946rem + 0.238vw, 1.125rem);
  --step-1: clamp(1.25rem, 1.036rem + 0.952vw, 1.75rem);
  --step-2: clamp(1.5rem, 1.071rem + 1.905vw, 2.5rem);
}

body { font-size: var(--step-0); }
h3   { font-size: var(--step-1); }
h1   { font-size: var(--step-2); }

Why vw-only text fails zoom

It is tempting to write font-size: 4vw. The problem appears when someone zooms the page. Browser zoom shrinks the viewport measured in CSS pixels, so a size based only on vw shrinks as they zoom in, and the text grows far less than the zoom level promises. WCAG success criterion 1.4.4 requires that text can be resized up to 200 percent without loss of content or functionality.

Two habits keep clamp-based text on the safe side:

  • Build the preferred value from rem plus vw, and use rem for the minimum and maximum, so the user's text-size setting still counts.
  • Keep the maximum at no more than about 2.5 times the minimum. This is a rule of thumb from accessibility specialists, discussed in this OddBird article on fluid type.
/* Safe: rem + vw, maximum is 2.5x the minimum */
font-size: clamp(1rem, 0.75rem + 1vw, 2.5rem);

/* Risky: maximum is 4x the minimum */
font-size: clamp(1rem, 1rem + 4vw, 4rem);

Test it: zoom to 200 percent on a desktop browser and confirm the text is visibly larger than it was at 100 percent. If it is not, tighten the range.

Using clamp for Padding, Margin and Gap

Spacing is where clamp padding patterns save the most code. Section spacing that looks right on a phone feels cramped on a wide monitor, and the other way around. One declaration covers both:

.section {
  padding-block: clamp(2rem, 1rem + 5vw, 6rem);
  padding-inline: clamp(1rem, 4vw, 3rem);
}

.card-grid {
  display: grid;
  gap: clamp(1rem, 0.5rem + 2vw, 2.5rem);
}

The section padding reaches its 32px minimum at a 320px viewport and its 96px maximum at 1600px. The grid gap runs from 16px at 400px up to 40px at 1600px. Spacing is more forgiving than text when it comes to zoom, but keeping the minimum and maximum in rem still means spacing follows the user's text size.

For a consistent system, store spacing steps as custom properties, the same way as the type scale above:

:root {
  --space-s: clamp(0.5rem, 0.4rem + 0.5vw, 1rem);
  --space-m: clamp(1rem, 0.8rem + 1vw, 1.75rem);
  --space-l: clamp(2rem, 1rem + 5vw, 6rem);
}

Vertical percentage padding is the one trap here. As the mistakes table showed, padding-top: 5% measures against the container's width, so use rem or vw instead.

clamp() for Width and Line Length

clamp also sets widths that grow with their container but stop at sensible limits:

.container {
  width: clamp(20rem, 90%, 70rem);
  margin-inline: auto;
}

Be careful with a minimum width, though. Because the minimum wins, a floor wider than the screen causes horizontal overflow on small phones:

/* 45ch is roughly 400px at 16px type: overflows a 320px phone */
.prose { width: clamp(45ch, 100%, 75ch); }

/* Safer: only a ceiling, no floor */
.prose { width: min(100%, 75ch); }

If you only need a maximum, use min(). Reach for clamp when you truly need both limits. The ch unit is covered in the CSS units guide.

clamp() vs Media Queries, min() and max()

ToolWhat it doesUse it when
clamp()Smooth value with a floor and a ceilingA size should scale continuously, such as type and spacing
min()Picks the smaller valueYou only need a maximum, such as min(100%, 75ch)
max()Picks the larger valueYou only need a minimum, such as a safe tap-target size
Media querySwitches rules at a breakpointLayout changes in steps, such as columns or navigation

Clamp handles values that change continuously. It cannot hide an element, change the number of columns or reorder content. Use media queries for those jobs. One more option: if you replace vw with container query units such as cqi, the same pattern can follow a component's width instead of the window, provided the element sits inside a container.

Can I Use clamp()? Browser Support and Fallbacks

According to web.dev, clamp() works from Chrome 79, Edge 79, Firefox 75 and Safari 13.1. That covers every current browser, so most projects need no fallback. For the full compatibility table, see the MDN clamp() reference.

If you do need to support an older browser, write a plain value first. A browser that does not understand clamp() drops that declaration and keeps the one before it:

h1 {
  font-size: 2rem;                          /* fallback */
  font-size: clamp(1.5rem, 1rem + 2vw, 3rem);
}

clamp() Is Not line-clamp

Easy to confuse: clamp() limits a size between two bounds. line-clamp cuts text off after a set number of lines. They are unrelated. For truncating text, see the CSS line clamp guide and the line clamp generator.

Frequently Asked Questions

Should I use clamp() or media queries?

Use clamp() when a value should change smoothly with the viewport, such as a font size or section padding. Use media queries when something changes in steps, such as the number of columns, a navigation layout, or an element appearing and disappearing. Many layouts use both.

What is the difference between clamp(), min() and max()?

min() returns the smaller of its values and max() returns the larger. clamp() combines them: clamp(a, b, c) behaves like max(a, min(b, c)). Use min() when you only need a ceiling, max() when you only need a floor, and clamp() when you need both.

Why is my clamp() font size not changing when I resize the window?

The usual causes are a preferred value with no viewport unit (rem alone never changes), a viewport that sits outside the range where the preferred value falls between the minimum and maximum, a minimum that is larger than the maximum, or a later rule overriding font-size. Check the computed font-size in DevTools while you resize.

Does clamp() break browser zoom for text?

It can if the preferred value depends only on vw. Zooming in shrinks the viewport in CSS pixels, so a vw-based size shrinks too and the text grows less than the zoom level. Build the preferred value from rem plus vw, use rem for the minimum and maximum, and keep the maximum at no more than 2.5 times the minimum.

Can I use CSS variables inside clamp()?

Yes. clamp(var(--min), 1rem + 2vw, var(--max)) works, and a variable can also hold a whole clamp() expression. Keep spaces around + and - inside the expression.

Can clamp() be used for width and height?

Yes. clamp() works anywhere CSS accepts a length, percentage or number, including width, height, max-width, margin, padding, gap and inset. For width, percentages resolve against the containing block. For font-size, they resolve against the parent's font size.

What happens if the clamp() minimum is larger than the maximum?

The minimum wins. clamp(MIN, VAL, MAX) is defined as max(MIN, min(VAL, MAX)), so if MIN is larger than MAX the result is always MIN. This is usually a typo, so check the order of your values.

Generate your clamp() value

Set your minimum and maximum sizes and viewport widths, preview the result, and copy CSS or Tailwind output.

Open the clamp() Calculator