Skip to content

Commit 79f21a8

Browse files
Prevent not-* from being used with variants with multiple sibling rules (#15689)
Variants like this can't be easily negated by our current system: ```css @custom-variant dark { &.is-dark { @slot; } @media (prefers-color-scheme: dark) { @slot; } } ``` Right now it produces the following CSS which is logically incorrect: ```css .utility-name { &:not(.is-dark) { /* ... */ } @media not (prefers-color-scheme: dark) { /* ... */ } } ``` The correct CSS is this which requires moving things around: ```css .utility-name { @media not (prefers-color-scheme: dark) { &:not(.is-dark) { /* ... */ } } } ``` We're opting to disable this instead of generating incorrect CSS for now. I'd like to bring this back in the future for simpler cases in the future.
1 parent 4035ab0 commit 79f21a8

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Ensure `-outline-offset-*` utilities are suggested in IntelliSense ([#15646](https://github.com/tailwindlabs/tailwindcss/pull/15646))
1717
- Write to `stdout` when `--output` is set to `-` or omitted for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
1818
- Write to `stdout` when `--output -` flag is used for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656))
19+
- Prevent `not-*` from being used with variants that have multiple sibling rules ([#15689](https://github.com/tailwindlabs/tailwindcss/pull/15689))
1920
- _Upgrade (experimental)_: Pretty print `--spacing(…)` to prevent ambiguity ([#15596](https://github.com/tailwindlabs/tailwindcss/pull/15596))
2021

2122
### Changed

packages/tailwindcss/src/compile.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,15 @@ export function applyVariant(
219219
let result = applyVariant(isolatedNode, variant.variant, variants, depth + 1)
220220
if (result === null) return null
221221

222+
if (variant.root === 'not' && isolatedNode.nodes.length > 1) {
223+
// The `not` variant cannot negate sibling rules / at-rules because these
224+
// are an OR relationship. Doing so would require transforming sibling
225+
// nodes into nesting while negating them. This isn't possible with the
226+
// current implementation of the `not` variant or with how variants are
227+
// applied in general (on a per-node basis).
228+
return null
229+
}
230+
222231
for (let child of isolatedNode.nodes) {
223232
// Only some variants wrap children in rules. For example, the `force`
224233
// variant is a noop on the AST. And the `has` variant modifies the

packages/tailwindcss/src/variants.test.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,30 @@ test('not', async () => {
16441644
}
16451645
}
16461646
}
1647+
@custom-variant parallel-style-rules {
1648+
&:hover {
1649+
@slot;
1650+
}
1651+
&:focus {
1652+
@slot;
1653+
}
1654+
}
1655+
@custom-variant parallel-at-rules {
1656+
@media foo {
1657+
@slot;
1658+
}
1659+
@media bar {
1660+
@slot;
1661+
}
1662+
}
1663+
@custom-variant parallel-mixed-rules {
1664+
&:hover {
1665+
@slot;
1666+
}
1667+
@media bar {
1668+
@slot;
1669+
}
1670+
}
16471671
@tailwind utilities;
16481672
`,
16491673
[
@@ -1660,23 +1684,27 @@ test('not', async () => {
16601684
'not-multiple-media-conditions:flex',
16611685
'not-starting:flex',
16621686

1687+
'not-parallel-style-rules:flex',
1688+
'not-parallel-at-rules:flex',
1689+
'not-parallel-mixed-rules:flex',
1690+
16631691
// The following built-in variants don't have not-* versions because
16641692
// there is no sensible negative version of them.
16651693

16661694
// These just don't make sense as not-*
1667-
'not-force',
1668-
'not-*',
1695+
'not-force:flex',
1696+
'not-*:flex',
16691697

16701698
// These contain pseudo-elements
1671-
'not-first-letter',
1672-
'not-first-line',
1673-
'not-marker',
1674-
'not-selection',
1675-
'not-file',
1676-
'not-placeholder',
1677-
'not-backdrop',
1678-
'not-before',
1679-
'not-after',
1699+
'not-first-letter:flex',
1700+
'not-first-line:flex',
1701+
'not-marker:flex',
1702+
'not-selection:flex',
1703+
'not-file:flex',
1704+
'not-placeholder:flex',
1705+
'not-backdrop:flex',
1706+
'not-before:flex',
1707+
'not-after:flex',
16801708

16811709
// This is not a conditional at rule
16821710
'not-starting:flex',

0 commit comments

Comments
 (0)