Move modifier of not-*, has-*, and in-* variant to sub variant (#19100)
This PR fixes an issue where a compound variant with a modifier such as `not-group-hover/name:flex` would not generate anything because the `/name` modifier belongs to the `not` variant, and not the compounded `group-hover` variant. This PR is a **workaround** (and definitely not perfect) by special casing the `not`, `has`, and `in` variants such that their modifiers are moved internally to the sub variant as-if the `/name` existed on `group-hover`. We don't do it for other compound variants such as `group` and `peer` because then `group-peer-focus/name:underline` would result in a breaking change: ```diff - .group-peer-focus\\/name\\:flex:is(:where(.group\\/name):is(:where(.peer):focus ~ *) *) + .group-peer-focus\/name\:flex:is(:where(.group):is(:where(.peer\/name):focus ~ *) *) ``` In case the diff is not clear, the name has moved: <img width="1219" height="78" alt="image" src="https://github.com/user-attachments/assets/dce7bc95-9d93-452d-a275-b3891a05a1a4" /> This is also a limited workaround, because if you need multiple modifiers it won't work. I would've loved to special case this _inside_ the `not`, `has`, and `in` code that handles these variants, but we handle the variants in a depth-first way, so by the time you are handling the `not` variant, the sub variant was already handled... In a perfect world, you can use something like `not-group/name-hover` but then it becomes unambiguous because is `name` the name, is `name-hover`? ## Test plan Added a new test that wouldn't generate anything before this fix. Fixes: #15772
This commit is contained in:
parent
0c14df1a15
commit
3aadba7cc1
4 changed files with 32 additions and 1 deletions
|
|
@ -14,8 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Fixed
|
||||
|
||||
- Fix Safari devtools rendering issue due to `color-mix` fallback ([#19069](https://github.com/tailwindlabs/tailwindcss/pull/19069))
|
||||
- Suppress Lightning CSS warnings about `:deep`, `:slotted` and `:global` ([#19094](https://github.com/tailwindlabs/tailwindcss/pull/19094))
|
||||
- Suppress Lightning CSS warnings about `:deep`, `:slotted`, and `:global` ([#19094](https://github.com/tailwindlabs/tailwindcss/pull/19094))
|
||||
- Fix resolving theme keys when starting with the name of another theme key in JS configs and plugins ([#19097](https://github.com/tailwindlabs/tailwindcss/pull/19097))
|
||||
- Allow named groups in combination with `not-*`, `has-*`, and `in-*` ([#19100](https://github.com/tailwindlabs/tailwindcss/pull/19100))
|
||||
|
||||
## [4.1.14] - 2025-10-01
|
||||
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,11 @@ const variants = [
|
|||
// Handle special `@` variants. These shouldn't be printed as `@-`
|
||||
['@xl:', '@xl:'],
|
||||
['@[123px]:', '@[123px]:'],
|
||||
|
||||
// Compound variants that forward modifiers
|
||||
['not-group-hover/name:', 'not-group-hover/name:'],
|
||||
['has-group-peer-hover/name:', 'has-group-peer-hover/name:'],
|
||||
['in-group-peer-hover/name:', 'in-group-peer-hover/name:'],
|
||||
]
|
||||
|
||||
let combinations: [string, string][] = []
|
||||
|
|
|
|||
|
|
@ -806,6 +806,13 @@ export function parseVariant(variant: string, designSystem: DesignSystem): Varia
|
|||
case 'compound': {
|
||||
if (value === null) return null
|
||||
|
||||
// Forward the modifier of the compound variants to its subVariant.
|
||||
// This allows for `not-group-hover/name:flex` to work.
|
||||
if (modifier && (root === 'not' || root === 'has' || root === 'in')) {
|
||||
value = `${value}/${modifier}`
|
||||
modifier = null
|
||||
}
|
||||
|
||||
let subVariant = designSystem.parseVariant(value)
|
||||
if (subVariant === null) return null
|
||||
|
||||
|
|
|
|||
|
|
@ -2586,6 +2586,24 @@ test('matchVariant sorts deterministically', async () => {
|
|||
}
|
||||
})
|
||||
|
||||
test('move modifier of compound variant to sub-variant if its also a compound variant', async () => {
|
||||
expect(
|
||||
await run([
|
||||
'not-group-focus/name:flex',
|
||||
'has-group-focus/name:flex',
|
||||
'in-group-focus/name:flex',
|
||||
|
||||
// Keep the `name` on the `group`, don't move it to the `peer` because
|
||||
// that would be a breaking change.
|
||||
'group-peer-focus/name:flex',
|
||||
]),
|
||||
).toMatchInlineSnapshot(`
|
||||
".not-group-focus\\/name\\:flex:not(:is(:where(.group\\/name):focus *)), .group-peer-focus\\/name\\:flex:is(:where(.group\\/name):is(:where(.peer):focus ~ *) *), :where(:is(:where(.group\\/name):focus *)) .in-group-focus\\/name\\:flex, .has-group-focus\\/name\\:flex:has(:is(:where(.group\\/name):focus *)) {
|
||||
display: flex;
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test.each([
|
||||
// These are style rules
|
||||
[['.foo'], Compounds.StyleRules],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue