Published October 15, 2026
Can you use CSS clamp() inside custom properties? Yes, in both directions. You can store an entire clamp() expression in a variable, and you can drop var() references into a clamp()'s arguments. This is not a trick or an edge case. The spec allows it, browsers support it, and it turns a scattered type scale into one block of tokens you can retune from a single place.
:root { --fluid-h1: clamp(2rem, 1.5rem + 4vw, 4rem); --fluid-body: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); } h1 { font-size: var(--fluid-h1); } body { font-size: var(--fluid-body); }
This is direction one: the variable holds the whole fluid value. Change the type scale for the entire site and you edit two lines in :root. Every heading, every component that references the token updates. For a design system, that is the difference between a fifteen minute tweak and an afternoon of find and replace.
Direction two puts the variables inside the clamp() instead:
:root { --type-min: 2rem; --type-max: 4rem; } h1 { font-size: clamp(var(--type-min), 1.5rem + 4vw, var(--type-max)); }
This earns its keep when you want to override just the bounds in a theme. A compact theme sets --type-max smaller. A marketing page sets it larger. The slope stays put while the guardrails move, which is a cleaner split of responsibilities than rewriting the full expression per theme.
The one catch: animation needs @property
Here is where people get burned. var() resolves at the point of use, which is why the value stays fluid as the viewport changes. Resizing works. What does not work, by default, is animating the variable itself. An unregistered custom property cannot interpolate, so a transition on --fluid-h1 flips discretely from old value to new instead of easing.
The fix is to register the property with @property, giving it a type the browser can interpolate:
@property --fluid-h1 { syntax: "<length>"; inherits: false; initial-value: 2rem; }
With that registration, transitions and animations on the token interpolate smoothly. Without it, do not be surprised when the change snaps. This is the one genuine gotcha in an otherwise boring, reliable pattern: fluid yes, animatable only when registered.
One more note on fallbacks. If you need to support browsers that predate clamp(), the static-declaration trick still applies on top of variables: declare font-size: 2rem; on the line above font-size: var(--fluid-h1);. A browser that cannot parse either clamp() or var() keeps the static line and drops the rest. The mechanics are covered in detail in the fallbacks guide linked below.
I keep all fluid type in tokens now. When a designer says the headings feel too shouty on tablets, I change one line in :root instead of hunting through components. The pattern has no real downside once you know about the @property catch, and it makes the type scale feel like a system instead of a pile of magic numbers.
Frequently asked questions
Can you put clamp() inside a CSS variable?
Yes. A declaration like --fluid-h1: clamp(2rem, 1.5rem + 4vw, 4rem) is valid, and every element that uses var(--fluid-h1) gets the fluid value.
Can you use var() inside clamp()?
Yes. clamp(var(--type-min), 1.5rem + 4vw, var(--type-max)) is valid. This is useful when you want themes to override just the minimum or maximum.
Does a variable holding clamp() still scale with the viewport?
Yes. var() is resolved at the point of use, so the clamp() re-evaluates as the viewport changes. The fluidity survives the indirection.
Can you animate a custom property that contains clamp()?
Only if the property is registered with @property and given a length syntax. Unregistered custom properties flip discretely instead of interpolating during transitions.
What is the fallback for browsers that do not support clamp() or custom properties?
Put a static declaration on the line above, like font-size: 2rem; followed by font-size: var(--fluid-h1);. Old browsers keep the static value and discard the line they cannot parse.
Generate your clamp values.