diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ad1a59cc60..5395c16dc489 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure `-outline-offset-*` utilities are suggested in IntelliSense ([#15646](https://github.com/tailwindlabs/tailwindcss/pull/15646)) - Write to `stdout` when `--output` is set to `-` or omitted for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656)) - Write to `stdout` when `--output -` flag is used for `@tailwindcss/cli` ([#15656](https://github.com/tailwindlabs/tailwindcss/pull/15656)) +- Prevent `not-*` from being used with variants that have multiple sibling rules ([#15689](https://github.com/tailwindlabs/tailwindcss/pull/15689)) - _Upgrade (experimental)_: Pretty print `--spacing(…)` to prevent ambiguity ([#15596](https://github.com/tailwindlabs/tailwindcss/pull/15596)) ### Changed diff --git a/packages/tailwindcss/src/compile.ts b/packages/tailwindcss/src/compile.ts index 2275971fb71e..5d7e82f73d03 100644 --- a/packages/tailwindcss/src/compile.ts +++ b/packages/tailwindcss/src/compile.ts @@ -219,6 +219,15 @@ export function applyVariant( let result = applyVariant(isolatedNode, variant.variant, variants, depth + 1) if (result === null) return null + if (variant.root === 'not' && isolatedNode.nodes.length > 1) { + // The `not` variant cannot negate sibling rules / at-rules because these + // are an OR relationship. Doing so would require transforming sibling + // nodes into nesting while negating them. This isn't possible with the + // current implementation of the `not` variant or with how variants are + // applied in general (on a per-node basis). + return null + } + for (let child of isolatedNode.nodes) { // Only some variants wrap children in rules. For example, the `force` // variant is a noop on the AST. And the `has` variant modifies the diff --git a/packages/tailwindcss/src/variants.test.ts b/packages/tailwindcss/src/variants.test.ts index 5f4b9d106aa2..e70970a92bed 100644 --- a/packages/tailwindcss/src/variants.test.ts +++ b/packages/tailwindcss/src/variants.test.ts @@ -1644,6 +1644,30 @@ test('not', async () => { } } } + @custom-variant parallel-style-rules { + &:hover { + @slot; + } + &:focus { + @slot; + } + } + @custom-variant parallel-at-rules { + @media foo { + @slot; + } + @media bar { + @slot; + } + } + @custom-variant parallel-mixed-rules { + &:hover { + @slot; + } + @media bar { + @slot; + } + } @tailwind utilities; `, [ @@ -1660,23 +1684,27 @@ test('not', async () => { 'not-multiple-media-conditions:flex', 'not-starting:flex', + 'not-parallel-style-rules:flex', + 'not-parallel-at-rules:flex', + 'not-parallel-mixed-rules:flex', + // The following built-in variants don't have not-* versions because // there is no sensible negative version of them. // These just don't make sense as not-* - 'not-force', - 'not-*', + 'not-force:flex', + 'not-*:flex', // These contain pseudo-elements - 'not-first-letter', - 'not-first-line', - 'not-marker', - 'not-selection', - 'not-file', - 'not-placeholder', - 'not-backdrop', - 'not-before', - 'not-after', + 'not-first-letter:flex', + 'not-first-line:flex', + 'not-marker:flex', + 'not-selection:flex', + 'not-file:flex', + 'not-placeholder:flex', + 'not-backdrop:flex', + 'not-before:flex', + 'not-after:flex', // This is not a conditional at rule 'not-starting:flex',