skip to content

Fluid Sizing with clamp(), min(), max()

Math functions that let a value scale continuously between bounds, replacing stepped breakpoints for type and spacing. "Build fluid typography without media queries" is a common practical prompt.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

5

What does the CSS clamp() function do with its three arguments, and how would you use it to make a heading's font-size scale with the viewport?

level: juniorimportance: must knowfreq 70%

answer

  1. three arguments, one of them varies
  2. floor, target, ceiling
  3. the middle one is the scaling value
  4. max(MIN, min(PREFERRED, MAX))

basics

~20 s

clamp(MIN, PREFERRED, MAX) returns the preferred value when it lies between the bounds, MIN when it falls below, and MAX when it rises above. font-size: clamp(1.5rem, 4vw, 3rem) scales a heading with the viewport but never below 1.5rem or above 3rem.

solid answer

~40 s

`clamp()` takes three arguments in the order minimum, preferred, maximum, and the specification defines it as exactly `max(MIN, min(PREFERRED, MAX))`. The preferred value is normally the one that varies — a viewport-relative expression such as `4vw` — while the two bounds are fixed limits. So `font-size: clamp(1.5rem, 4vw, 3rem)` gives a heading that is 24px on a phone (because 4vw is smaller than 1.5rem there), grows smoothly through the middle of the range, and stops at 48px on a wide monitor. The value is recomputed continuously as the viewport changes, which is what makes it a replacement for a stack of media queries that step the size at fixed breakpoints. It is valid anywhere a length, percentage or number is accepted — `width`, `padding`, `gap`, grid track sizes, not just type.

go deeper

for a junior

Memorise the argument order — minimum, preferred, maximum — and be able to write one fluid declaration such as font-size: clamp(1.5rem, 4vw, 3rem) and say what it produces on a narrow and a wide screen.

for a middle

Explain the evaluation as max(MIN, min(PREFERRED, MAX)), work through the value at two concrete viewport widths, and note that each argument is itself a math expression so no calc() wrapper is needed.

for a senior

Show judgment about which properties deserve fluid values and which need a real breakpoint, and insist on rem bounds so the result still tracks the reader's font-size preference rather than freezing at a pixel constant.

for a principal

Frame it as a scale contract: a handful of clamped custom properties define type and spacing once, so product teams stop inventing per-component breakpoints and the design system's sizing stays auditable in one place.

## The function in one line `clamp()` is a CSS math function that takes three comma-separated arguments and returns a single value. The order is fixed: **minimum, preferred, maximum**. The specification defines the result as `max(MIN, min(PREFERRED, MAX))` — first the preferred value is capped at the maximum, then that result is floored at the minimum. Read in plain language: *"use the preferred value, but never let it drop below MIN or rise above MAX."* ## Working through a real evaluation ```css h1 { font-size: clamp(1.5rem, 4vw, 3rem); } ``` Assume the root font-size is the browser default of 16px, so `1.5rem` is 24px and `3rem` is 48px. - Viewport 320px wide: `4vw` = 12.8px. That is below the 24px floor, so the used value is **24px**. - Viewport 800px wide: `4vw` = 32px, which sits between the bounds, so the used value is **32px**. - Viewport 1600px wide: `4vw` = 64px, above the 48px ceiling, so the used value is **48px**. Between roughly 600px and 1200px of viewport width the heading grows continuously — one pixel of viewport gives 0.04px of type — and outside that band it is pinned. There is no jump anywhere, which is the visible difference from a breakpoint-stepped scale. ## Where you can write it `clamp()` is a value, not a property, so it works anywhere the property accepts a `<length>`, `<percentage>`, `<number>`, `<angle>`, `<time>` or `<frequency>`: ```css .card { padding: clamp(1rem, 3vw, 2.5rem); } .stack { gap: clamp(0.5rem, 2vw, 1.5rem); } .wrapper { width: clamp(20rem, 90%, 70rem); } .grid { grid-template-columns: repeat(3, clamp(8rem, 20%, 16rem)); } .body-copy { line-height: clamp(1.4, 1.2 + 0.5vw, 1.7); } ``` It cannot stand in for a keyword — you cannot `clamp()` your way between `block` and `flex`, and it cannot change *which* declarations apply. It interpolates one number; a media query or container query changes the rules themselves. That distinction is worth saying out loud in an interview, because it is the honest boundary of the technique. ## The arguments are full math expressions Each of the three arguments is parsed as a calculation, so you may write arithmetic directly without wrapping it in `calc()`: ```css h2 { font-size: clamp(1.25rem, 1rem + 1.5vw, 2rem); } ``` Mixing a `rem` term with a `vw` term in the preferred value is the standard idiom for fluid type: the `rem` part keeps the value tied to the reader's root font-size, the `vw` part supplies the scaling. ## Common mistakes **Wrong argument order.** `clamp(1.5rem, 3rem, 4vw)` reads plausibly as "min, max, preferred" but it means something else entirely: the preferred value here is the constant `3rem`, and `4vw` is the ceiling, so on a narrow screen the ceiling drags the size down to a few pixels. The preferred, varying value always goes in the middle. **Min greater than max.** `width: clamp(300px, 50%, 200px)` is not an error and the browser does not ignore it. Because the definition applies `min()` first and `max()` last, the minimum wins outright and the used width is 300px. This turns into a silent bug when the bounds come from custom properties set in different places. **Assuming it removes the need for any media query.** Fluid values solve *sizing*; they do not reflow a three-column grid into one column. Real responsive work usually uses both. **Bounds in px.** Writing `clamp(16px, 4vw, 32px)` freezes the floor and ceiling against the reader's font-size preference. Prefer `rem` for the bounds. ## Why interviewers ask it It is the shortest question that reveals whether a candidate thinks about sizing as a continuous function of the viewport or as a set of discrete states. A strong answer names the `max(MIN, min(VAL, MAX))` equivalence, gives one worked evaluation, and mentions that the bounds should be expressed in `rem` so the result still honours user preferences.

  • What does width: clamp(300px, 50%, 200px) resolve to, given that the minimum exceeds the maximum?
    It resolves to 300px, and no error is reported. The specification evaluates `clamp()` as `max(MIN, min(VAL, MAX))`: the inner `min()` first caps 50% at 200px, then the outer `max()` raises that to the 300px minimum. The minimum always wins when the bounds are inverted, which makes swapped or custom-property-driven bounds a silent bug rather than a visible failure.
  • Does clamp() remove the need for media queries entirely?
    No. `clamp()` interpolates a single numeric value continuously, so it replaces breakpoint-stepped *sizes* — type scales, padding, gaps, measure. It cannot change which declarations apply, so it cannot switch a grid from three columns to one, flip a flex direction, or hide an element. Production stylesheets normally use fluid values for sizing and a small number of queries for structural changes.
  • Where else besides font-size do you reach for clamp() first?
    Spacing and measure, mostly: `padding` and `gap` on a card or section so density scales with the screen, and `width: clamp(20rem, 90%, 70rem)` on a content wrapper to set a readable measure with a floor and a ceiling in one declaration. It also works as a grid track size and for unitless numbers such as `line-height`.

saying these in an interview costs you the question

  • Saying the arguments are min, max, preferred
  • Claiming clamp() errors out when min exceeds max
  • Believing clamp() can replace all media queries, including layout changes
  • Writing the bounds in px and calling it accessible
  • Thinking the preferred value must be a viewport unit

context

open as a page

In CSS, why does width: min(100%, 60ch) behave like a maximum width rather than a minimum one?

level: middleimportance: should knowfreq 50%

basics

~20 s

min() returns the smallest of its arguments at any moment, so it acts as a ceiling: the element is capped at 60ch and shrinks to 100% of its container when that is narrower. The function name describes the comparison performed, not the design intent.

open as a page

How do you compute the vw plus rem preferred value inside clamp() so a font-size lands exactly on 16px at a 320px viewport and 24px at a 1280px viewport?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Treat it as a straight line through two points: slope is (24 - 16) / (1280 - 320) = 0.00833, or 0.833vw, and the intercept is 16 - 0.00833 x 320 = 13.33px = 0.833rem. That gives font-size: clamp(1rem, 0.833rem + 0.833vw, 1.5rem).

open as a page

Why is sizing body text with font-size: 4vw an accessibility problem, and how does a clamp() with a rem term fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Viewport units ignore the reader's browser font-size preference and text-only zoom, so 4vw text will not enlarge for someone who needs it bigger. Putting rem back into the value — clamp(1rem, 0.9rem + 1vw, 1.5rem) — restores that control while keeping the fluid behaviour.

open as a page

Why is the declaration font-size: clamp(1rem, 1rem+2vw, 2rem) invalid CSS, and what is the minimal fix?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

CSS math grammar requires whitespace around the + and - operators, so 1rem+2vw fails to parse and the whole declaration is dropped. The fix is spacing alone: clamp(1rem, 1rem + 2vw, 2rem). No calc() wrapper is needed, because each clamp() argument is already a math expression.

open as a page