SIGN IN SIGN UP

A utility-first CSS framework for rapid UI development.

0 0 25 TypeScript

Canonicalize negative arbitrary values (#19858)

This PR adds a few more canonicalizations for some cases I noticed on
our templates.

When dealing with arbitrary values, and the utility is a "negative"
utility, then we will try to put the `-` inside of the arbitrary value:

```diff
- -left-[9rem]
+ left-[-9rem]
```

The idea is that the arbitrary value is already an escape hatch for when
a value is not available by default. The `-` in front uses an implicit
`calc(<expression> * -1)` which might be confusion if you have an value
like this already.

This also can allow for some further optimizations. For example
```diff
- -mt-[492px]
  ↓↓↓↓↓↓↓                           Into a simpler arbitrary value
+ mt-[-492px]
  ↓↓↓↓↓↓↓                           Into a bare value
+ mt-123
```

This PR also improve the constant folding of calc expressions a bit more
such that nested calc expressions with 2 constants and an unknown can be
folded. Bit of a mouthful, but it allows us to handle this:
```diff
- mt-[calc(-1*calc(-1*var(--foo)))]
  ↓↓↓↓↓↓↓                           The -1 * -1 becomes a no-op
+ mt-[var(--foo)]
  ↓↓↓↓↓↓↓                           Into the shorthand for CSS variables
+ mt-(--foo)
```

Now that we can handle moving the `-` into the arbitrary value, there
are also cases where we can get the `-` _out_ of the arbitrary value:
```diff
- mt-[calc(-1*var(--foo))]
  ↓↓↓↓↓↓↓                           Simplify calc, move `-` to the front
+ -mt-[var(--foo)]
  ↓↓↓↓↓↓↓                           Into the shorthand for CSS variables
+ -mt-(--foo)
```

Another missing piece that this PR adds is the concept of canonicalizing
or normalizing calc expressions. This is a separate step used when
calculating the signature for each utility. This allows us to normalize
`calc(-1*var(--foo))` and `calc(var(--foo)*-1)`. Without this they would
not be considered the same, but not it will.

It's only used when comparing values, it won't unify the actual
arbitrary values with this logic (at least for now).

With the additional constant folding logic and the canonicalization when
comparing signatures it unlocks the necessary power to perform the above
transformations.

## Test plan

1. Existing tests still pass
2. Added additional tests for the constant folding logic
3. Added tests for the canonicalization of calc expressions
4. Added new tests where we move the `-` inside the value, or move the
`-` outside of the arbitrary value.
R
Robin Malfait committed
df6209ab8b957e56e6e00042a3e4080c14a072ea
Parent: 52fd421
Committed by GitHub <noreply@github.com> on 3/26/2026, 1:44:09 PM