You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix stacking variant order when variants inside a group are treated as equal (#14431)
This PR fixes an issue with the order of CSS when using stacked variants
when two variants have the same order (as defined by the custom
comperator function).
## The problem
Take, for example, our breakpoint variants. Those are split into `max-*`
variants and a group containing all `min-*` variants as well as the
unprefixed static ones (e.g. `lg`, `sm`).
We currently define a custom sort order for all breakpoints variants
that will compare their order based on the resolved value provided. So
if you define `--breakpoint-sm: 100px` and `--breakpoint-lg: 200px`, we
first check if both breakpoints have the same unit and then we rank
based on the numerical value, making `sm` appear before `lg`.
But since the `min-*` variant and the `sm` variant share the same group,
this also means that `min-sm` and `sm` as well as `min-lg` and `lg` will
always have the same order (which makes sense—they also have the exact
same CSS they generate!)
The issue now arises when you use these together with variant stacking.
So, say you want to stack the two variants `max-lg:min-sm`. We always
want stacked variants to appear _after_ their non-stacked individual
parts (since they are more specific). To do this right now, we generate
a bitfield based on the variant order. If you have four variants like
this:
| Order | Variant |
| ------------- | ------------- |
| 0 | `max-lg` |
| 1 | `max-sm` |
| 2 | `min-sm` |
| 3 | `min-lg` |
We will assign one bit for each used variant starting from the lowest
bit, so for the stack `max-lg:min-sm` we will set the bitfield to `0101`
and those for the individual variants would result in `0100` (for
`min-sm`) and `0001` (for `max-lg`). We then convert this bitfield to a
number and order based on that number. This ensures that the stack
always sorts higher.
The issue now arises from the fact that the variant order also include
the unprefixed variants for a breakpoint. So in our case of `lg` and
`sm`, the full list would look like this:
| Order | Variant |
| ------------- | ------------- |
| 0 | `max-lg` |
| 1 | `max-sm` |
| 2 | `min-sm` |
| 3 | `sm` |
| 4 | `min-lg` |
| 5 | `lg` |
This logic now breaks when you start to compute a stack for something
like `max-lg:min-lg` _while also using the `lg` utility:
| Stack | Bitmap | Integer Value |
| ------------- | ------------- | ------------- |
| `max-lg:min-lg` | `010001` | 17 |
| `lg` | `100000` | 18 |
As you can see here, the sole `lg` variant will now sort higher than the
compound of `max-lg:min-lg`. That's not something we want!
## Proposed solution
To fix this, we need to encode the information of _same_ variant order
somehow. A single array like the example above is not sufficient for
this, since it will remove the information of the similar sort order.
Instead, we now computed a list of nested arrays for the order lookup
that will combine variants of similar values (while keeping the order
the same). So from the 6 item array above, we now have the following
nested array:
| Order | Variant |
| ------------- | ------------- |
| 0 | [`max-lg`] |
| 1 | [`max-sm`] |
| 2 | [`min-sm`, `sm`] |
| 3 | [`min-lg`, `lg`] |
When we use the first layer index for the bitfield, we can now see how
this solves the issue:
| Stack | Bitmap | Integer Value |
| ------------- | ------------- | ------------- |
| `max-lg:min-lg` | `1001` | 9 |
| `lg` | `1000` | 8 |
That's pretty-much it! There are a few other changes in this PR that
mostly handles with a small regression by this change where now, named
`group` variants and unnamed `group` variants would now have the same
order (something that was undefined behavior before).
---------
Co-authored-by: Adam Wathan <[email protected]>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
### Fixed
11
11
12
12
- Support `borderRadius.*` as an alias for `--radius-*` when using dot notation inside the `theme()` function ([#14436](https://github.com/tailwindlabs/tailwindcss/pull/14436))
13
+
- Ensure individual variants from groups are always sorted earlier than stacked variants from the same groups ([#14431](https://github.com/tailwindlabs/tailwindcss/pull/14431))
0 commit comments