What clamp() replaced
Before clamp(), scaling type across screen sizes meant declaring a font size and then overriding it at two or three breakpoints. It worked, and it had a visible flaw: the text jumped. At 767 pixels wide the heading was one size, at 768 it was another, and every width in between got whichever of the two happened to apply.
clamp() takes three values — a minimum, a preferred value and a maximum — and returns whichever applies at the current viewport width. The preferred value is where the work happens: expressed partly in vw it grows continuously with the screen, and the other two hold it flat once it reaches either end. One declaration, no breakpoints, no jump.
The middle value is the whole problem
Writing clamp(1rem, 2vw, 2rem) is easy. Getting the middle term to actually reach 1rem and 2rem at the widths you had in mind is not, and guessing a vw figure almost never lands it.
There is only one line through two points. Give it the two sizes and the two viewport widths you want them to occur at, and the slope of that line becomes the vw component while its intercept becomes the fixed component. That is what the [msm_tool slug="css-clamp"] solves, and it is why the output has two parts added together rather than a single vw value.
A worked example: 16px at a 360px viewport, 32px at 1280px. The line through those gives clamp(1rem, 0.6087rem + 1.7391vw, 2rem). At 360px that middle term evaluates to exactly 16px; at 1280px, exactly 32px. Between them it scales smoothly, and outside them the minimum and maximum take over.
Keep the rem component
This is the detail most worth carrying away, and it is an accessibility one rather than a stylistic one.
A preferred value written purely in vw — clamp(1rem, 2vw, 2rem) — ignores the reader's browser text-size setting entirely between its two ends. Someone who has increased their default font size because they need to gets no benefit across the whole fluid range.
Solving for the line naturally produces a rem component alongside the vw one, and that component is what keeps the text responding to browser zoom. It is not an optional refinement. It is the difference between fluid type that respects a reader's settings and fluid type that overrides them.
Choosing the two viewport widths
These are design decisions, not measurements of the device landscape. They are the widths at which the text should stop shrinking and stop growing.
A common pair is a phone width at the bottom and your content column's maximum at the top. Setting the upper bound to 1920 because monitors go that wide is usually a mistake — if your article column stops at 720 pixels, the text has no reason to keep growing past the point where the column does.
It is not only for type
Padding, margins, gaps and container widths all accept clamp(), and all benefit from scaling continuously rather than stepping. Section padding that grows from 24px on a phone to 96px on a desktop reads far better than the same value jumping at a breakpoint, and it removes a media query while it is at it.
The same solving applies. Two sizes, two widths, one line.
Where to be careful
- Do not let the minimum go too small. Fluid type invites setting a 12px floor because it looks fine on your screen. Body text below about 16px is hard work on a phone, and the minimum is precisely the value phone readers will get.
- Check the maximum on a large monitor. The upper bound is the one nobody tests, because most people develop on a laptop. A heading that stops growing at a sensible size looks deliberate; one that keeps growing looks like a mistake.
- Do not clamp every element separately. A whole type scale built from unrelated clamp values drifts out of proportion at the extremes. Scale one root value and express the rest in
rem against it, or clamp a handful of steps deliberately.
- Test at browser zoom, not just at window widths. Zoom and viewport width are different things, and a value with no
rem component behaves badly under one while looking perfect under the other.
Browser support
clamp() has been supported across every current browser for years, and there is no meaningful reason to avoid it on that basis. If you support something genuinely ancient, a plain font-size declaration before the clamp acts as a fallback — the older browser ignores the line it cannot parse and keeps the one it can.