Course outline · 0% complete

0/30 lessons0%

Course overview →

Fluid sizing with clamp

lesson 7-3 · ~8 min · 23/30

Fluid sizing with guardrails

Breakpoints jump. A headline that is 32px at 767px wide and 56px at 768px looks broken mid-resize, and covering every width with queries means writing a dozen of them.

Sometimes a size should glide instead, and clamp(min, preferred, max) keeps a value near its preferred size while never passing the guardrails.

h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
}
.content {
  width: clamp(320px, 90%, 60rem);
}
ArgumentRole
minthe floor, never go below
preferredused whenever it fits
maxthe ceiling, never go above

The h1 wants to be 5% of the viewport width, using vw from lesson 5-3, so it grows as the window does while never dropping below 2rem on phones or passing 3.5rem on large monitors. One line replaces three media queries.

The companion trick for images belongs in almost every stylesheet.

img {
  max-width: 100%;
  height: auto;
}

Any image now shrinks to fit its container instead of overflowing on small screens, and height: auto keeps the aspect ratio intact while it does.

3.5rem2remmin holdspreferred, 5vwmax holdsviewport widthfont size
clamp tracks its preferred value through the middle band and flattens against the floor and the ceiling outside it.

Tracing clamp at three viewport widths

clamp is simple enough to write directly, taking the preferred value and pushing it up to the min when too small or down to the max when too big.

function clamp(min, preferred, max) {
  return Math.min(Math.max(preferred, min), max);
}

// clamp(32, 5vw, 56) where 5vw = 0.05 * viewport width
console.log(clamp(32, 0.05 * 500, 56));
console.log(clamp(32, 0.05 * 900, 56));
console.log(clamp(32, 0.05 * 1400, 56));

Output

32
45
56
Viewport5vwResultGuardrail
500px2532min applied
900px4545preferred used
1400px7056max applied

The nesting order matters, since Math.max raises to the floor first and Math.min then caps at the ceiling. Reversing them would still work here, and it breaks when a min is accidentally larger than a max, where CSS clamp also lets the min win.

Clamping a content column

contentColumnWidth(viewportWidth) implements the rule width: clamp(320px, 90%, 960px), where the 90% is measured against the viewport width.

function clamp(min, preferred, max) {
  return Math.min(Math.max(preferred, min), max);
}

function contentColumnWidth(viewportWidth) {
  return clamp(320, 0.9 * viewportWidth, 960);
}

console.log(contentColumnWidth(350));
console.log(contentColumnWidth(800));
console.log(contentColumnWidth(1400));

Output

320
720
960
Viewport90% of itColumn width
350px315320
800px720720
1400px1260960

The preferred value is 0.9 * viewportWidth, and the rest is a single clamp call.

This is the standard reading-column rule. It leaves a small margin on phones, stays fluid through tablet widths, and stops widening on large monitors where over-long lines become hard to read.

When the floor takes over

On a 320px-wide viewport where 5vw is 16px, font-size: clamp(2rem, 5vw, 3.5rem) resolves to 2rem.

The preferred value of 1rem falls below the minimum guardrail, so clamp raises it to the min.

ViewportPreferred (5vw)Result
320px1rem2rem, the min
800px2.5rem2.5rem
1600px5rem3.5rem, the max

On very large screens the max guardrail caps the value the same way. The middle row is the only one where the viewport actually drives the size, which is the band the fluid behavior lives in.

Naming the middle argument

In clamp(2rem, 5vw, 3.5rem), the middle value is the preferred value, used whenever it fits between the guardrails.

clamp(min, preferred, max) tries the preferred value first, raising it to the min when too small and capping it at the max when too big.

PositionName
firstmin
secondpreferred
thirdmax

Making the preferred value viewport-relative, as 5vw does, is what produces the fluid glide. A fixed preferred value would produce a constant size with two guardrails that never come into play.