A utility-first CSS framework for rapid UI development.
Support `--default(…)` in `--value(…)` and `--modifier(…)` to support fallback values (#19989)
This PR adds a new `--default(…)` option that can be used inside
`--value(…)` or `--modifier(…)` such that functional utilities without
an explicit value/modifier can still be defined as a functional utility.
It would also allow you to use a functional utility without a value and
_with_ a modifier, e.g.: `shadow/50`.
---
This allows us to re-implement functional utilities with a default value
in CSS using `@utility`.
Used the explicit `--default(…)` argument of `--value(…)` for a few
reasons.
1. It's explicit about being a falllback value. If you have `@utility
foo-*`, then you want to be able to use `foo`, but `foo-bad` should not
compile.
2. When `--value(…)` is used in (complex) property values (think a bunch
of `calc(…)` expressions), then we don't need a separate property for
this.
One of the ideas was to have a literal fallback:
```css
@utility tab-* {
tab-size: 4;
tab-size: --value(number);
}
```
For `tab`, this would compile to:
```css
.tab {
tab-size: 4;
}
```
For `tab-123`, this would compile to:
```css
.tab {
tab-size: 4;
tab-size: 123;
}
```
Getting rid of the `tab-size: 4` would be an option, but it's a common
pattern in real CSS for fallback values (think hex background color,
over a more modern `oklch` color).
For `tab-foo`, this would compile to:
```css
.tab {
tab-size: 4;
}
```
Which means that we have an infinite amount classes that would result in
the same class, which is bad. We could special case this one because the
internal `value` would still be `null`, but it might be too confusing.
This syntax without the `--default(…)` also means repetition of certain
properties. Add `--modifier(…)` to the mix, and there is even more
repetition going on.
Another option to consider is that the default fallback is just another
option in the `--value(…, 4)`, but if a default fallback is a keyword,
then there is a chance that this might conflict with actual keywords we
interpret.
Main motivation is to be able to re-implement utilities such as
`shadow/50` purely in CSS. It's also something we support in the JS
based APIs, but not in the CSS based one, so while it's a "new" feature,
it's more like a missing feature right now, and often a reason for
people to use the JS based APIs instead.
For consistency reasons, this is also implemented for `--modifier(…)`
such that you can use a default value there. E.g. when re-implementing
`text-sm` where a default `line-height` is set without the explicit use
of a modifier.
Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16824
## Test plan
1. Added a handful of new tests to make sure this functionality works
2. Existing tests still pass R
Robin Malfait committed
08cad84bbe2002398655d7f981eae47379e07fc3
Parent: 0f6f7d4
Committed by GitHub <noreply@github.com>
on 5/4/2026, 3:03:00 PM