Skip to content

Commit b0fcc99

Browse files
RobinMalfaitclaude
andcommitted
Constrain group-* and peer-* targets to reduce style invalidation
Generate `:is(<group> &:is(*))` instead of `&:is(<group> *)` (and the `~` equivalent for `peer-*`). The target now sits in the subject position, so browsers can limit style invalidation to the elements that carry the utility instead of every descendant or following sibling of the group/peer. Matching behavior and specificity are unchanged. `:is(*)` keeps the default namespace restriction of the original `*`, and the outer `:is(…)` keeps compound variants such as `has-group-*` behaving the same. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 9798a8a commit b0fcc99

6 files changed

Lines changed: 114 additions & 91 deletions

File tree

‎packages/tailwindcss/src/compat/plugin-api.test.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ describe('addVariant', () => {
16301630
).toMatchInlineSnapshot(`
16311631
"
16321632
@layer utilities {
1633-
.group-hocus\\:flex:is(:is(:where(.group):hover, :where(.group):focus) *) {
1633+
:is(:is(:where(.group):hover, :where(.group):focus) .group-hocus\\:flex:is(*)) {
16341634
display: flex;
16351635
}
16361636
@@ -1744,7 +1744,7 @@ describe('addVariant', () => {
17441744
).toMatchInlineSnapshot(`
17451745
"
17461746
@layer utilities {
1747-
.group-hocus\\:flex:is(:where(.group):hover *), .group-hocus\\:flex:is(:where(.group):focus *) {
1747+
:is(:where(.group):hover .group-hocus\\:flex:is(*)), :is(:where(.group):focus .group-hocus\\:flex:is(*)) {
17481748
display: flex;
17491749
}
17501750
@@ -1784,7 +1784,7 @@ describe('addVariant', () => {
17841784
).toMatchInlineSnapshot(`
17851785
"
17861786
@layer utilities {
1787-
.group-hocus\\:flex:is(:where(.group):hover *), .group-hocus\\:flex:is(:where(.group):focus *) {
1787+
:is(:where(.group):hover .group-hocus\\:flex:is(*)), :is(:where(.group):focus .group-hocus\\:flex:is(*)) {
17881788
display: flex;
17891789
}
17901790
@@ -1827,12 +1827,12 @@ describe('addVariant', () => {
18271827
"
18281828
@layer utilities {
18291829
@media (hover: hover) {
1830-
.group-hocus\\:flex:is(:where(.group):hover *) {
1830+
:is(:where(.group):hover .group-hocus\\:flex:is(*)) {
18311831
display: flex;
18321832
}
18331833
}
18341834
1835-
.group-hocus\\:flex:is(:where(.group):focus *) {
1835+
:is(:where(.group):focus .group-hocus\\:flex:is(*)) {
18361836
display: flex;
18371837
}
18381838
@@ -1964,7 +1964,7 @@ describe('addVariant', () => {
19641964
).toMatchInlineSnapshot(`
19651965
"
19661966
@layer utilities {
1967-
.group-optional\\:flex:is(:where(.group):optional *), .group-optional\\/foo\\:flex:is(:where(.group\\/foo):optional *), .peer-optional\\:flex:is(:where(.peer):optional ~ *), .optional\\:flex:optional {
1967+
:is(:where(.group):optional .group-optional\\:flex:is(*)), :is(:where(.group\\/foo):optional .group-optional\\/foo\\:flex:is(*)), :is(:where(.peer):optional ~ .peer-optional\\:flex:is(*)), .optional\\:flex:optional {
19681968
display: flex;
19691969
}
19701970
}
@@ -2903,7 +2903,7 @@ describe('matchVariant', () => {
29032903
).toMatchInlineSnapshot(`
29042904
"
29052905
@layer utilities {
2906-
.group-optional-\\[test\\]\\:flex:is(:where(.group):optional:has(test) :where(.group) *), .group-optional-\\[test\\]\\/foo\\:flex:is(:where(.group\\/foo):optional:has(test) :where(.group\\/foo) *), .peer-optional-\\[test\\]\\:flex:is(:where(.peer):optional:has(test) :where(.peer) ~ *), .optional-\\[test\\]\\:flex:optional:has(test) .optional-\\[test\\]\\:flex {
2906+
:is(:where(.group):optional:has(test) :where(.group) .group-optional-\\[test\\]\\:flex:is(*)), :is(:where(.group\\/foo):optional:has(test) :where(.group\\/foo) .group-optional-\\[test\\]\\/foo\\:flex:is(*)), :is(:where(.peer):optional:has(test) :where(.peer) ~ .peer-optional-\\[test\\]\\:flex:is(*)), .optional-\\[test\\]\\:flex:optional:has(test) .optional-\\[test\\]\\:flex {
29072907
display: flex;
29082908
}
29092909
}

‎packages/tailwindcss/src/index.test.ts‎

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,42 +1444,42 @@ describe('sorting', () => {
14441444
).toMatchInlineSnapshot(`
14451445
"
14461446
@media (hover: hover) {
1447-
.group-hover\\:flex:is(:where(.group):hover *) {
1447+
:is(:where(.group):hover .group-hover\\:flex:is(*)) {
14481448
display: flex;
14491449
}
14501450
}
14511451
1452-
.group-focus\\:flex:is(:where(.group):focus *) {
1452+
:is(:where(.group):focus .group-focus\\:flex:is(*)) {
14531453
display: flex;
14541454
}
14551455
14561456
@media (hover: hover) {
1457-
.peer-hover\\:flex:is(:where(.peer):hover ~ *) {
1457+
:is(:where(.peer):hover ~ .peer-hover\\:flex:is(*)) {
14581458
display: flex;
14591459
}
14601460
14611461
@media (hover: hover) {
1462-
.group-hover\\:peer-hover\\:flex:is(:where(.group):hover *):is(:where(.peer):hover ~ *), .peer-hover\\:group-hover\\:flex:is(:where(.peer):hover ~ *):is(:where(.group):hover *) {
1462+
:is(:where(.peer):hover ~ :is(:where(.group):hover .group-hover\\:peer-hover\\:flex:is(*)):is(*)), :is(:where(.group):hover :is(:where(.peer):hover ~ .peer-hover\\:group-hover\\:flex:is(*)):is(*)) {
14631463
display: flex;
14641464
}
14651465
}
14661466
1467-
.group-focus\\:peer-hover\\:flex:is(:where(.group):focus *):is(:where(.peer):hover ~ *), .peer-hover\\:group-focus\\:flex:is(:where(.peer):hover ~ *):is(:where(.group):focus *) {
1467+
:is(:where(.peer):hover ~ :is(:where(.group):focus .group-focus\\:peer-hover\\:flex:is(*)):is(*)), :is(:where(.group):focus :is(:where(.peer):hover ~ .peer-hover\\:group-focus\\:flex:is(*)):is(*)) {
14681468
display: flex;
14691469
}
14701470
}
14711471
1472-
.peer-focus\\:flex:is(:where(.peer):focus ~ *) {
1472+
:is(:where(.peer):focus ~ .peer-focus\\:flex:is(*)) {
14731473
display: flex;
14741474
}
14751475
14761476
@media (hover: hover) {
1477-
.group-hover\\:peer-focus\\:flex:is(:where(.group):hover *):is(:where(.peer):focus ~ *), .peer-focus\\:group-hover\\:flex:is(:where(.peer):focus ~ *):is(:where(.group):hover *) {
1477+
:is(:where(.peer):focus ~ :is(:where(.group):hover .group-hover\\:peer-focus\\:flex:is(*)):is(*)), :is(:where(.group):hover :is(:where(.peer):focus ~ .peer-focus\\:group-hover\\:flex:is(*)):is(*)) {
14781478
display: flex;
14791479
}
14801480
}
14811481
1482-
.group-focus\\:peer-focus\\:flex:is(:where(.group):focus *):is(:where(.peer):focus ~ *), .peer-focus\\:group-focus\\:flex:is(:where(.peer):focus ~ *):is(:where(.group):focus *) {
1482+
:is(:where(.peer):focus ~ :is(:where(.group):focus .group-focus\\:peer-focus\\:flex:is(*)):is(*)), :is(:where(.group):focus :is(:where(.peer):focus ~ .peer-focus\\:group-focus\\:flex:is(*)):is(*)) {
14831483
display: flex;
14841484
}
14851485
@@ -3601,7 +3601,7 @@ describe('plugins', () => {
36013601
).toMatchInlineSnapshot(`
36023602
"
36033603
@layer utilities {
3604-
.group-hocus\\:flex:is(:is(:where(.group):hover, :where(.group):focus) *) {
3604+
:is(:is(:where(.group):hover, :where(.group):focus) .group-hocus\\:flex:is(*)) {
36053605
display: flex;
36063606
}
36073607
@@ -3636,7 +3636,7 @@ describe('plugins', () => {
36363636
).toMatchInlineSnapshot(`
36373637
"
36383638
@layer utilities {
3639-
.group-hocus\\:flex:is(:where(.group):hover *), .group-hocus\\:flex:is(:where(.group):focus *) {
3639+
:is(:where(.group):hover .group-hocus\\:flex:is(*)), :is(:where(.group):focus .group-hocus\\:flex:is(*)) {
36403640
display: flex;
36413641
}
36423642
@@ -3674,7 +3674,7 @@ describe('plugins', () => {
36743674
).toMatchInlineSnapshot(`
36753675
"
36763676
@layer utilities {
3677-
.group-hocus\\:flex:is(:where(.group):hover *), .group-hocus\\:flex:is(:where(.group):focus *) {
3677+
:is(:where(.group):hover .group-hocus\\:flex:is(*)), :is(:where(.group):focus .group-hocus\\:flex:is(*)) {
36783678
display: flex;
36793679
}
36803680
@@ -3715,12 +3715,12 @@ describe('plugins', () => {
37153715
"
37163716
@layer utilities {
37173717
@media (hover: hover) {
3718-
.group-hocus\\:flex:is(:where(.group):hover *) {
3718+
:is(:where(.group):hover .group-hocus\\:flex:is(*)) {
37193719
display: flex;
37203720
}
37213721
}
37223722
3723-
.group-hocus\\:flex:is(:where(.group):focus *) {
3723+
:is(:where(.group):focus .group-hocus\\:flex:is(*)) {
37243724
display: flex;
37253725
}
37263726
@@ -4199,7 +4199,7 @@ describe('@custom-variant', () => {
41994199
).toMatchInlineSnapshot(`
42004200
"
42014201
@layer utilities {
4202-
.group-hocus\\:flex:is(:is(:where(.group):hover, :where(.group):focus) *) {
4202+
:is(:is(:where(.group):hover, :where(.group):focus) .group-hocus\\:flex:is(*)) {
42034203
display: flex;
42044204
}
42054205
@@ -4294,7 +4294,7 @@ describe('@custom-variant', () => {
42944294
).toMatchInlineSnapshot(`
42954295
"
42964296
@layer utilities {
4297-
.group-selected\\:underline:is(:where(.group)[data-selected] *), .selected\\:underline[data-selected] {
4297+
:is(:where(.group)[data-selected] .group-selected\\:underline:is(*)), .selected\\:underline[data-selected] {
42984298
text-decoration-line: underline;
42994299
}
43004300
}
@@ -4322,7 +4322,7 @@ describe('@custom-variant', () => {
43224322
).toMatchInlineSnapshot(`
43234323
"
43244324
@layer utilities {
4325-
.group-hocus\\:underline:is(:is(:where(.group):hover, :where(.group):focus) *), .hocus\\:underline:hover, .hocus\\:underline:focus {
4325+
:is(:is(:where(.group):hover, :where(.group):focus) .group-hocus\\:underline:is(*)), .hocus\\:underline:hover, .hocus\\:underline:focus {
43264326
text-decoration-line: underline;
43274327
}
43284328
}
@@ -4353,7 +4353,7 @@ describe('@custom-variant', () => {
43534353
).toMatchInlineSnapshot(`
43544354
"
43554355
@layer utilities {
4356-
.group-hocus\\:underline:is(:where(.group):hover *), .group-hocus\\:underline:is(:where(.group):focus *), .hocus\\:underline:hover, .hocus\\:underline:focus {
4356+
:is(:where(.group):hover .group-hocus\\:underline:is(*)), :is(:where(.group):focus .group-hocus\\:underline:is(*)), .hocus\\:underline:hover, .hocus\\:underline:focus {
43574357
text-decoration-line: underline;
43584358
}
43594359
}
@@ -4458,12 +4458,12 @@ describe('@custom-variant', () => {
44584458
"
44594459
@layer utilities {
44604460
@media (hover: hover) {
4461-
.group-hocus\\:underline:is(:where(.group):hover *) {
4461+
:is(:where(.group):hover .group-hocus\\:underline:is(*)) {
44624462
text-decoration-line: underline;
44634463
}
44644464
}
44654465
4466-
.group-hocus\\:underline:is(:where(.group):focus *) {
4466+
:is(:where(.group):focus .group-hocus\\:underline:is(*)) {
44674467
text-decoration-line: underline;
44684468
}
44694469
@@ -4503,7 +4503,7 @@ describe('@custom-variant', () => {
45034503
"
45044504
@layer utilities {
45054505
@media (hover: hover) {
4506-
.group-hocus\\:underline:is(:where(.group):hover *), .hocus\\:underline:hover {
4506+
:is(:where(.group):hover .group-hocus\\:underline:is(*)), .hocus\\:underline:hover {
45074507
text-decoration-line: underline;
45084508
}
45094509
}

‎packages/tailwindcss/src/intellisense.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ test('getVariants compound', async () => {
9797
]
9898

9999
expect(list).toEqual([
100-
['@media (hover: hover) { &:is(:where(.group):hover *) }'],
101-
['@media (hover: hover) { &:is(:where(.group\\/sidebar):hover *) }'],
102-
['@media (hover: hover) { &:is(:where(.group):is(:where(.group):hover *) *) }'],
100+
['@media (hover: hover) { :is(:where(.group):hover &:is(*)) }'],
101+
['@media (hover: hover) { :is(:where(.group\\/sidebar):hover &:is(*)) }'],
102+
['@media (hover: hover) { :is(:is(:where(.group):hover :where(.group):is(*)) &:is(*)) }'],
103103
[],
104104
[],
105105
])

‎packages/tailwindcss/src/prefix.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ test('utilities must be prefixed', async () => {
3838
}
3939
4040
@media (hover: hover) {
41-
.tw\\:group-hover\\:flex:is(:where(.tw\\:group):hover *), .tw\\:peer-hover\\:flex:is(:where(.tw\\:peer):hover ~ *) {
41+
:is(:where(.tw\\:group):hover .tw\\:group-hover\\:flex:is(*)), :is(:where(.tw\\:peer):hover ~ .tw\\:peer-hover\\:flex:is(*)) {
4242
display: flex;
4343
}
4444

0 commit comments

Comments
 (0)