From 126d7d8fc288843cd7ce4afb5132723a404208c5 Mon Sep 17 00:00:00 2001 From: uditDewan Date: Thu, 16 Jul 2026 18:36:12 -0400 Subject: [PATCH 1/2] Ensure custom variants using @scope generate working CSS Custom variants defined with an @scope at-rule emitted the @scope block nested inside the generated utility class instead of wrapping it, so the resulting CSS had no effect in browsers. Treat @scope like other conditional at-rules (@media, @supports, @container) when resolving nesting, so it is hoisted above the generated style rules. Fixes #18961 --- packages/tailwindcss/src/ast.test.ts | 24 +++++++++++ packages/tailwindcss/src/ast.ts | 2 + packages/tailwindcss/src/index.test.ts | 58 ++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/packages/tailwindcss/src/ast.test.ts b/packages/tailwindcss/src/ast.test.ts index f3499b350752..d6db07174d37 100644 --- a/packages/tailwindcss/src/ast.test.ts +++ b/packages/tailwindcss/src/ast.test.ts @@ -1093,6 +1093,30 @@ describe('optimization', () => { `) }) + it('should hoist `@scope` at-rules', async () => { + expect( + optimize(css` + @layer utilities { + .a { + @scope (.b) to (.c) { + --x: 1; + } + } + } + `), + ).toMatchInlineSnapshot(` + " + @layer utilities { + @scope (.b) to (.c) { + .a { + --x: 1; + } + } + } + " + `) + }) + it('should leave `@property` and `@apply` alone', async () => { expect( optimize(css` diff --git a/packages/tailwindcss/src/ast.ts b/packages/tailwindcss/src/ast.ts index e22c13e83900..8777de06e5ba 100644 --- a/packages/tailwindcss/src/ast.ts +++ b/packages/tailwindcss/src/ast.ts @@ -1311,6 +1311,7 @@ const HOISTABLE_AT_RULES = new Set([ '@layer', '@media', '@page', + '@scope', '@starting-style', '@supports', '@view-transition', @@ -1323,6 +1324,7 @@ const DROPPABLE_IF_EMPTY_AT_RULES = new Set([ '@container', '@media', '@page', + '@scope', '@starting-style', '@supports', '@view-transition', diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 2a72336fb738..fe76d39075d1 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -4683,6 +4683,64 @@ describe('@custom-variant', () => { " `) }) + + test('@scope with @slot', async () => { + expect( + await run( + ['in-checkout:underline'], + css` + @custom-variant in-checkout { + @scope (.checkout) { + @slot; + } + } + + @layer utilities { + @tailwind utilities; + } + `, + ), + ).toMatchInlineSnapshot(` + " + @layer utilities { + @scope (.checkout) { + .in-checkout\\:underline { + text-decoration-line: underline; + } + } + } + " + `) + }) + + test('@scope with a limit with @slot', async () => { + expect( + await run( + ['in-checkout:underline'], + css` + @custom-variant in-checkout { + @scope (.checkout) to (.payment) { + @slot; + } + } + + @layer utilities { + @tailwind utilities; + } + `, + ), + ).toMatchInlineSnapshot(` + " + @layer utilities { + @scope (.checkout) to (.payment) { + .in-checkout\\:underline { + text-decoration-line: underline; + } + } + } + " + `) + }) }) test('built-in variants can be overridden while keeping their order', async () => { From 5ef23bda07702d3d9effa88a68f2b645c05fb93e Mon Sep 17 00:00:00 2001 From: uditDewan Date: Thu, 16 Jul 2026 18:37:06 -0400 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a121f9fd7d..1b9081231899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Ensure custom variants using `@scope` wrap the generated utilities instead of nesting inside them ([#20344](https://github.com/tailwindlabs/tailwindcss/pull/20344)) ## [4.3.3] - 2026-07-16