Technote

Themes & plugins Practical

Series Layouts that hold up on mobile Part 3 of 8

One clamp() instead of re-declaring sizes at every step

Re-declaring heading sizes at every breakpoint only adds maintenance. clamp() puts the minimum, the fluid middle and the maximum on one line and removes it.

The most common reason breakpoints multiply is that type and spacing are re-declared at every step. Set a heading to 28px on phones, 36px on tablets and 48px on desktop, and every width in between is slightly wrong. To patch the wrongness you add another breakpoint — and now there is one more place to re-declare.

Declare a range, not a set of steps

clamp() takes three values: a minimum, a preferred value and a maximum. If the preferred value falls below the minimum it uses the minimum; above the maximum it uses the maximum; in between it uses the preferred value as calculated. Put a viewport-relative unit into that middle value and the two extremes join up smoothly.

h1 { font-size: clamp(1.75rem, 1.25rem + 2.5vw, 3rem); } never drops below 1.75rem on a very narrow screen, grows as the viewport widens, and stops at 3rem. That single line replaces two or three media queries and everything inside them.

The same result from two approaches — with very different maintenance costs

Always mix a rem into the middle value

Do not write the preferred value as a bare viewport unit such as 4vw. A pure viewport unit does not respond at all when a reader increases text size in their browser. Someone who reads at larger text gets no change, and the text-resize requirement in the accessibility guidelines is simply not met.

So write the middle value as a sum: 1.25rem + 2.5vw. The rem term follows the user’s setting, the vw term follows the viewport. Both axes have to stay alive for fluid type to be a quality decision rather than a convenience.

The same rule for spacing

Section padding only needs to move between a minimum that does not feel cramped on a phone and a maximum that does not feel excessive on a large display. section { padding-block: clamp(3rem, 8vw, 7rem); } does exactly that, and grid gaps behave the same way.

Collect the values as tokens and they stay a single set.

Three or four fluid values is plenty — more and you have invented steps again

Where not to use it

Not everything needs to be fluid. Border widths, corner radii and icon sizes are small enough that fluidity is invisible and only adds arithmetic. Line height is better left as a unitless number (line-height: 1.7) so it follows the font size on its own.

Finally, check both ends for real: is the minimum genuinely readable at 320px, and is the maximum restrained on a very wide display? Verify those two points and every width in between is, by definition, between them.

The wider view of front-end weight and speed lives in the Performance archive, and how we apply this structure to a live site is published on the process page.

Next part

Fluid sizes still will not save a layout whose grid tracks swell to fit their contents. Next: what 1fr does not actually promise.

More on this topic

All technotes

Themes & plugins Practical

Decide the editable regions before you design them

A design the CMS cannot express stays up for negotiation long after it ships. Deciding what editors may change, first, removes the negotiation entirely.

Designers 6 min read

₩270,000 · Join the program