Skip to content

Commit 7052555

Browse files
Ensure compound variants work with variants with multiple selectors (#14016)
* Ensure compound variants work with variants with multiple selectors * Update changelog
1 parent e8546df commit 7052555

File tree

3 files changed

+144
-38
lines changed

3 files changed

+144
-38
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Discard invalid `variants` and `utilities` with modifiers ([#13977](https://github.com/tailwindlabs/tailwindcss/pull/13977))
1616
- Add missing utilities that exist in v3, such as `resize`, `fill-none`, `accent-none`, `drop-shadow-none`, and negative `hue-rotate` and `backdrop-hue-rotate` utilities ([#13971](https://github.com/tailwindlabs/tailwindcss/pull/13971))
1717
- Don’t allow at-rule-only variants to be compounded ([#14015](https://github.com/tailwindlabs/tailwindcss/pull/14015))
18+
- Ensure compound variants work with variants with multiple selectors ([#14016](https://github.com/tailwindlabs/tailwindcss/pull/14016))
1819

1920
### Added
2021

packages/tailwindcss/src/variants.test.ts

Lines changed: 141 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -738,12 +738,25 @@ test('group-[...]', () => {
738738

739739
test('group-*', () => {
740740
expect(
741-
run([
742-
'group-hover:flex',
743-
'group-focus:flex',
744-
'group-hover:group-focus:flex',
745-
'group-focus:group-hover:flex',
746-
]),
741+
compileCss(
742+
css`
743+
@variant hocus {
744+
&:hover,
745+
&:focus {
746+
@slot;
747+
}
748+
}
749+
@tailwind utilities;
750+
`,
751+
[
752+
'group-hover:flex',
753+
'group-focus:flex',
754+
'group-hocus:flex',
755+
756+
'group-hover:group-focus:flex',
757+
'group-focus:group-hover:flex',
758+
],
759+
),
747760
).toMatchInlineSnapshot(`
748761
".group-hover\\:flex:is(:where(.group):hover *) {
749762
display: flex;
@@ -759,6 +772,10 @@ test('group-*', () => {
759772
760773
.group-hover\\:group-focus\\:flex:is(:where(.group):hover *):is(:where(.group):focus *) {
761774
display: flex;
775+
}
776+
777+
.group-hocus\\:flex:is(:is(:where(.group):hover, :where(.group):focus) *) {
778+
display: flex;
762779
}"
763780
`)
764781

@@ -816,12 +833,24 @@ test('peer-[...]', () => {
816833

817834
test('peer-*', () => {
818835
expect(
819-
run([
820-
'peer-hover:flex',
821-
'peer-focus:flex',
822-
'peer-hover:peer-focus:flex',
823-
'peer-focus:peer-hover:flex',
824-
]),
836+
compileCss(
837+
css`
838+
@variant hocus {
839+
&:hover,
840+
&:focus {
841+
@slot;
842+
}
843+
}
844+
@tailwind utilities;
845+
`,
846+
[
847+
'peer-hover:flex',
848+
'peer-focus:flex',
849+
'peer-hocus:flex',
850+
'peer-hover:peer-focus:flex',
851+
'peer-focus:peer-hover:flex',
852+
],
853+
),
825854
).toMatchInlineSnapshot(`
826855
".peer-hover\\:flex:is(:where(.peer):hover ~ *) {
827856
display: flex;
@@ -837,6 +866,10 @@ test('peer-*', () => {
837866
838867
.peer-hover\\:peer-focus\\:flex:is(:where(.peer):hover ~ *):is(:where(.peer):focus ~ *) {
839868
display: flex;
869+
}
870+
871+
.peer-hocus\\:flex:is(:is(:where(.peer):hover, :where(.peer):focus) ~ *) {
872+
display: flex;
840873
}"
841874
`)
842875

@@ -1502,26 +1535,54 @@ test('supports', () => {
15021535

15031536
test('not', () => {
15041537
expect(
1505-
run([
1506-
'not-[:checked]:flex',
1507-
1508-
'group-not-[:checked]:flex',
1509-
'group-not-[:checked]/parent-name:flex',
1510-
'group-not-checked:flex',
1511-
1512-
'peer-not-[:checked]:flex',
1513-
'peer-not-[:checked]/parent-name:flex',
1514-
'peer-not-checked:flex',
1515-
]),
1538+
compileCss(
1539+
css`
1540+
@variant hocus {
1541+
&:hover,
1542+
&:focus {
1543+
@slot;
1544+
}
1545+
}
1546+
@tailwind utilities;
1547+
`,
1548+
[
1549+
'not-[:checked]:flex',
1550+
'not-hocus:flex',
1551+
1552+
'group-not-[:checked]:flex',
1553+
'group-not-[:checked]/parent-name:flex',
1554+
'group-not-checked:flex',
1555+
'group-not-hocus:flex',
1556+
'group-not-hocus/parent-name:flex',
1557+
1558+
'peer-not-[:checked]:flex',
1559+
'peer-not-[:checked]/sibling-name:flex',
1560+
'peer-not-checked:flex',
1561+
'peer-not-hocus:flex',
1562+
'peer-not-hocus/sibling-name:flex',
1563+
],
1564+
),
15161565
).toMatchInlineSnapshot(`
1517-
".not-\\[\\:checked\\]\\:flex:not(:checked) {
1566+
".not-hocus\\:flex:not(:hover, :focus) {
1567+
display: flex;
1568+
}
1569+
1570+
.not-\\[\\:checked\\]\\:flex:not(:checked) {
15181571
display: flex;
15191572
}
15201573
15211574
.group-not-checked\\:flex:is(:where(.group):not(:checked) *) {
15221575
display: flex;
15231576
}
15241577
1578+
.group-not-hocus\\:flex:is(:where(.group):not(:hover, :focus) *) {
1579+
display: flex;
1580+
}
1581+
1582+
.group-not-hocus\\/parent-name\\:flex:is(:where(.group\\/parent-name):not(:hover, :focus) *) {
1583+
display: flex;
1584+
}
1585+
15251586
.group-not-\\[\\:checked\\]\\:flex:is(:where(.group):not(:checked) *) {
15261587
display: flex;
15271588
}
@@ -1534,11 +1595,19 @@ test('not', () => {
15341595
display: flex;
15351596
}
15361597
1598+
.peer-not-hocus\\:flex:is(:where(.peer):not(:hover, :focus) ~ *) {
1599+
display: flex;
1600+
}
1601+
1602+
.peer-not-hocus\\/sibling-name\\:flex:is(:where(.peer\\/sibling-name):not(:hover, :focus) ~ *) {
1603+
display: flex;
1604+
}
1605+
15371606
.peer-not-\\[\\:checked\\]\\:flex:is(:where(.peer):not(:checked) ~ *) {
15381607
display: flex;
15391608
}
15401609
1541-
.peer-not-\\[\\:checked\\]\\/parent-name\\:flex:is(:where(.peer\\/parent-name):not(:checked) ~ *) {
1610+
.peer-not-\\[\\:checked\\]\\/sibling-name\\:flex:is(:where(.peer\\/sibling-name):not(:checked) ~ *) {
15421611
display: flex;
15431612
}"
15441613
`)
@@ -1556,22 +1625,46 @@ test('not', () => {
15561625

15571626
test('has', () => {
15581627
expect(
1559-
run([
1560-
'has-[:checked]:flex',
1561-
1562-
'group-has-[:checked]:flex',
1563-
'group-has-[:checked]/parent-name:flex',
1564-
'group-has-checked:flex',
1565-
1566-
'peer-has-[:checked]:flex',
1567-
'peer-has-[:checked]/sibling-name:flex',
1568-
'peer-has-checked:flex',
1569-
]),
1628+
compileCss(
1629+
css`
1630+
@variant hocus {
1631+
&:hover,
1632+
&:focus {
1633+
@slot;
1634+
}
1635+
}
1636+
@tailwind utilities;
1637+
`,
1638+
[
1639+
'has-[:checked]:flex',
1640+
'has-hocus:flex',
1641+
1642+
'group-has-[:checked]:flex',
1643+
'group-has-[:checked]/parent-name:flex',
1644+
'group-has-checked:flex',
1645+
'group-has-hocus:flex',
1646+
'group-has-hocus/parent-name:flex',
1647+
1648+
'peer-has-[:checked]:flex',
1649+
'peer-has-[:checked]/sibling-name:flex',
1650+
'peer-has-checked:flex',
1651+
'peer-has-hocus:flex',
1652+
'peer-has-hocus/sibling-name:flex',
1653+
],
1654+
),
15701655
).toMatchInlineSnapshot(`
15711656
".group-has-checked\\:flex:is(:where(.group):has(:checked) *) {
15721657
display: flex;
15731658
}
15741659
1660+
.group-has-hocus\\:flex:is(:where(.group):has(:hover, :focus) *) {
1661+
display: flex;
1662+
}
1663+
1664+
.group-has-hocus\\/parent-name\\:flex:is(:where(.group\\/parent-name):has(:hover, :focus) *) {
1665+
display: flex;
1666+
}
1667+
15751668
.group-has-\\[\\:checked\\]\\:flex:is(:where(.group):has(:checked) *) {
15761669
display: flex;
15771670
}
@@ -1584,6 +1677,14 @@ test('has', () => {
15841677
display: flex;
15851678
}
15861679
1680+
.peer-has-hocus\\:flex:is(:where(.peer):has(:hover, :focus) ~ *) {
1681+
display: flex;
1682+
}
1683+
1684+
.peer-has-hocus\\/sibling-name\\:flex:is(:where(.peer\\/sibling-name):has(:hover, :focus) ~ *) {
1685+
display: flex;
1686+
}
1687+
15871688
.peer-has-\\[\\:checked\\]\\:flex:is(:where(.peer):has(:checked) ~ *) {
15881689
display: flex;
15891690
}
@@ -1592,6 +1693,10 @@ test('has', () => {
15921693
display: flex;
15931694
}
15941695
1696+
.has-hocus\\:flex:has(:hover, :focus) {
1697+
display: flex;
1698+
}
1699+
15951700
.has-\\[\\:checked\\]\\:flex:has(:checked) {
15961701
display: flex;
15971702
}"

packages/tailwindcss/src/variants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ export function createVariants(theme: Theme): Variants {
216216

217217
// Replace `&` in target variant with `*`, so variants like `&:hover`
218218
// become `&:not(*:hover)`. The `*` will often be optimized away.
219-
node.selector = `&:not(${node.selector.replace('&', '*')})`
219+
node.selector = `&:not(${node.selector.replaceAll('&', '*')})`
220220

221221
// Track that the variant was actually applied
222222
didApply = true
@@ -442,7 +442,7 @@ export function createVariants(theme: Theme): Variants {
442442

443443
// Replace `&` in target variant with `*`, so variants like `&:hover`
444444
// become `&:has(*:hover)`. The `*` will often be optimized away.
445-
node.selector = `&:has(${node.selector.replace('&', '*')})`
445+
node.selector = `&:has(${node.selector.replaceAll('&', '*')})`
446446

447447
// Track that the variant was actually applied
448448
didApply = true

0 commit comments

Comments
 (0)