How to css clamp(): specification & fluid-type reference
- Step 1Read the signature — `clamp(MIN, PREFERRED, MAX)`. MIN is the floor (result never goes below it). MAX is the ceiling (never above). PREFERRED is the target, used whenever it sits between MIN and MAX. For fluid type, MIN and MAX are rem and PREFERRED is `rem + vw`.
- Step 2Compute the slope — `slope = (maxPx − minPx) / (maxVw − minVw)`. This is the px-per-px-of-viewport rate. Multiply by 100 to express it as a vw value: a 16→24px climb over 320→1440 is `8 / 1120 = 0.00714`, i.e. `0.714vw`.
- Step 3Compute the intercept — `intercept = minPx − slope × minVw`, then convert to rem with `÷ 16`. This is the value the line would have at viewport width 0, expressed in rem so it anchors the vw slope correctly: for the example, `(16 − 0.00714 × 320) / 16 = 0.857rem`.
- Step 4Assemble the bounds — MIN rem = `minPx ÷ 16`, MAX rem = `maxPx ÷ 16`. With the intercept and slope, the full expression is `clamp(minRem, interceptRem ± slopeVw, maxRem)`. The sign is `+` for a positive slope (text grows with width), `−` if Max px < Min px.
- Step 5Verify the bounds behaviour — At Min vw the PREFERRED term equals Min px → result is the floor. At Max vw it equals Max px → ceiling. Below Min vw, PREFERRED computes under the floor so MIN wins; above Max vw, it exceeds the ceiling so MAX wins. That is how clamp() keeps fluid type bounded.
- Step 6Generate to confirm — Type the four numbers into the tool above and compare its output to your hand calculation — every value rounds to 3 decimals. For one-sided bounds (floor only, or ceiling only) use `max()` or `min()` instead; the generator only emits the two-sided `clamp()`.
The three arguments of clamp()
How each argument maps to fluid typography. The tool always produces all three.
| Argument | Role | In fluid type (this tool) |
|---|---|---|
| MIN | Lower bound — result never below it | minPx / 16 in rem (the floor) |
| PREFERRED | Target value when between bounds | interceptRem ± slopeVw (the line) |
| MAX | Upper bound — result never above it | maxPx / 16 in rem (the ceiling) |
The fluid-type math, end to end
The exact formulas this generator uses (lib/font/font-utils.ts, root fixed at 16), worked for the default 16→24px / 320→1440 inputs.
| Step | Formula | Default result |
|---|---|---|
| Min rem (floor) | minPx / 16 | 1.000rem |
| Max rem (ceiling) | maxPx / 16 | 1.500rem |
| Slope | (maxPx − minPx) / (maxVw − minVw) | 8 / 1120 = 0.00714 |
| vw term | slope × 100 | 0.714vw |
| Intercept rem | (minPx − slope × minVw) / 16 | 0.857rem |
| Full expression | clamp(minRem, interceptRem + slopeVw, maxRem) | clamp(1.000rem, 0.857rem + 0.714vw, 1.500rem) |
Browser support and related functions
clamp(), min() and max() shipped together as CSS math functions. Versions are first stable release.
| Function | Use | First support |
|---|---|---|
clamp(min, pref, max) | Two-sided bound (this tool) | Chrome 79, Firefox 75, Safari 13.1 |
max(a, b) | Floor only (unbounded growth above) | Chrome 79, Firefox 75, Safari 11.1 |
min(a, b) | Ceiling only (unbounded below) | Chrome 79, Firefox 75, Safari 11.1 |
calc(...) | Mix units in the preferred term | Widely supported (Chrome 26, FF 16, Safari 7) |
Cookbook
Reference snippets: each shows a spec point with the literal expression the generator produces (or the equivalent for related functions). Root size 16 throughout.
Default expression, fully decomposed
ExampleThe defaults (16→24px over 320→1440) decomposed into each spec component, so you can map the formula to the output character by character.
clamp( 1.000rem , 0.857rem + 0.714vw , 1.500rem )
↑ MIN ↑ PREFERRED ↑ MAX
16/16 intercept + slope 24/16
slope = (24-16)/(1440-320) = 0.00714 → 0.714vw
intercept = (16 - 0.00714*320)/16 = 0.857remAt the bounds: floor and ceiling kick in
ExamplePlug the viewport extremes into the preferred term and confirm it equals the bounds, then see what happens past them.
At 320px: 0.857rem + 0.714vw = 0.857rem + 0.714% * 320px(as rem) ≈ 1.000rem → equals MIN At 1440px: ≈ 1.500rem → equals MAX Below 320px: preferred < 1.000rem → MIN wins (pinned) Above 1440px: preferred > 1.500rem → MAX wins (pinned)
One-sided bound with max() (floor only)
ExampleIf you want a floor but unbounded growth, clamp() is the wrong function — use max(). The generator does not emit this; write it directly.
/* Never smaller than 1rem, grows without an upper cap */ font-size: max(1rem, 2vw); /* Symmetric: a ceiling but no floor */ font-size: min(2rem, 5vw);
Negative slope when Max px < Min px
ExampleThe spec allows a negative coefficient on the vw term. The tool emits a minus sign — the text shrinks as the viewport grows. Valid, but rarely intended.
Inputs: Min 24px · Max 16px · 320 → 1440
Output:
clamp(1.500rem, 1.643rem - 0.714vw, 1.000rem)
↑ note: MIN(1.5) > MAX(1.0) and the slope is negativeZero slope when Min px equals Max px
ExampleEqual bounds produce a constant — a verbose way to write a fixed size. The spec resolves it to that single value at every viewport.
Inputs: Min 18px · Max 18px · 320 → 1440 Output: clamp(1.125rem, 1.125rem + 0.000vw, 1.125rem) → always exactly 1.125rem (same as font-size: 1.125rem)
Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
PREFERRED below MIN at small viewports
Expected — MIN winsBy spec, clamp() returns MIN whenever the PREFERRED value computes below it. For fluid type this means below your Min vw the size is pinned at the rem floor and stops shrinking. This is the intended safety net — the text never gets smaller than your declared minimum, no matter how narrow the screen.
PREFERRED above MAX at large viewports
Expected — MAX winsSymmetrically, clamp() returns MAX when PREFERRED exceeds it, so above your Max vw the size pins at the rem ceiling. This is what stops vw-driven type from ballooning on ultrawide monitors. The generator always emits a MAX, so this protection is built in.
MIN greater than MAX (inverted bounds)
By design — spec-definedThe CSS spec defines clamp() when MIN > MAX: it returns MIN (MIN takes precedence). This generator can produce MIN > MAX if you enter Max px below Min px, giving a negative slope and an expression like clamp(1.500rem, ..., 1.000rem). It is valid CSS but almost never what you want — confirm Max px > Min px.
Browser without clamp() support
Rare — declaration droppedAn engine that does not parse clamp() discards the whole font-size declaration and falls back to the otherwise-effective value (inherited or default). Provide a plain font-size: 1rem; line before the clamp() for a tiny legacy audience. Support is ~95%+ in 2026 (Chrome 79, Firefox 75, Safari 13.1), so this is rarely needed.
Mixing incompatible units in PREFERRED
Invalid — rule ignoredThe spec requires the arguments to resolve to compatible types. The tool always emits rem + vw, which is valid (both are lengths). If you hand-edit the preferred term to mix a length with a bare number or a percentage where a length is required, the browser treats the value as invalid and ignores the declaration. Keep the generator's rem ± vw form.
Root font size assumed 16 in the math
By designAll rem conversions here divide by 16. The spec's clamp() does not care about your root size — but the rem values it returns are interpreted against your actual html font-size at render time. If that is not 16, the rendered px will differ from what you typed. The generator's 16 assumption is a fixed convention, not a spec requirement.
Rounding to 3 decimals
By design — implementation choiceThe spec imposes no rounding; the generator applies .toFixed(3) so the output string is short and stable. The sub-thousandth difference between the rounded and exact values is far below one device pixel at any real viewport, so it has no visible effect. This is a presentation choice, not a spec behaviour.
Wanting only a floor or only a ceiling
Use min()/max() insteadclamp() is inherently two-sided. If you want a floor with unbounded growth, the correct function is max(floor, fluid); for a ceiling with unbounded shrink, min(ceiling, fluid). This generator only produces two-sided clamp() — write the one-sided forms by hand. Both shipped alongside clamp().
Max vw equal to or below Min vw
Error — tool rejectsThe slope formula divides by (maxVw − minVw), which would be zero or negative. The tool guards this with Max viewport must be greater than min viewport. and emits nothing. This is the only input combination the generator refuses outright.
Frequently asked questions
What does clamp() return, exactly?
The PREFERRED (middle) value, bounded so it is never below MIN nor above MAX. If PREFERRED is below MIN it returns MIN; if above MAX it returns MAX. For fluid type, MIN/MAX are rem bounds and PREFERRED is a rem + vw line.
How is the vw slope computed?
slope = (maxPx − minPx) / (maxVw − minVw), then × 100 to make it a vw value. A 16→24px climb over 320→1440 is 8 / 1120 = 0.00714, i.e. 0.714vw. This generator does that exactly and rounds to 3 decimals.
How is the rem intercept computed?
intercept = minPx − slope × minVw, then ÷ 16 for rem. It is the line's value at viewport width 0, expressed in rem so it anchors the vw slope. For the default inputs that is (16 − 0.00714 × 320) / 16 = 0.857rem.
What happens at the exact Min and Max viewport widths?
At Min vw the PREFERRED term equals Min px, so the result is the floor; at Max vw it equals Max px, so the result is the ceiling. Between them it interpolates linearly; outside them it pins to the nearest bound.
Which browsers support clamp()?
Chrome 79 (2019), Firefox 75 (2020), Safari 13.1 (2020), and Edge via Chromium. Coverage is ~95%+ globally in 2026. min() and max() shipped at the same time (Safari even earlier, 11.1), so one-sided bounds are equally safe.
What if I want only a minimum, no maximum?
Use max(): max(1rem, 2vw) keeps a floor but grows unbounded. For a ceiling only, use min(): min(2rem, 5vw). clamp() is two-sided by definition; this generator produces only the two-sided form, so write the one-sided versions by hand.
Can clamp() arguments use different units?
Yes, as long as they resolve to compatible types. The tool emits rem bounds and a rem + vw preferred term — all lengths, all valid. You can mix rem, px, em, %, vw, vh in principle, but mixing incompatible types (a length with a bare number) makes the value invalid and the declaration is ignored.
What does the tool do if MIN ends up greater than MAX?
It emits the expression anyway (e.g. when you set Max px below Min px). By spec, clamp() returns MIN in that case, so the bigger bound wins. It is valid CSS but almost always a mistake — verify Max px > Min px before using the output.
Why does the output assume a 16px root?
The generator hardcodes 16 for its px-to-rem conversions. The clamp() spec itself is root-agnostic, but the rem values are interpreted against your actual html font-size at render time. If your root is not 16, the rendered sizes differ from the px you typed.
Does clamp() cause layout shift?
No, when applied consistently. It resolves at layout time before CLS measurement, so the reserved space settles before paint. It is generally better for CLS than media-query steps, which can jump at a breakpoint.
Is there a fallback for non-supporting browsers?
A browser that does not understand clamp() drops the declaration and uses the otherwise-effective font-size. Add a plain font-size: 1rem; before the clamp() line if you must support very old engines — but with ~95%+ support this is rarely necessary in 2026.
How do I produce an actual expression from this reference?
Type Min px, Max px, Min vw and Max vw into the four fields above and Generate — the output matches the formulas on this page to 3 decimals. For a whole modular scale of clamp() values at once, use the typography-scale-builder instead.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.