From 4e3e72428df235caaff88f1f299fb9bf20bdf98d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 4 Nov 2025 10:51:01 -0500 Subject: [PATCH 1/4] Add test for `@variant` inside `addBase` --- packages/tailwindcss/src/index.test.ts | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index f1dad6ccda4e..e67df73dcf40 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -4665,6 +4665,45 @@ test('addBase', async () => { `) }) +test('JS APIs support @variant', async () => { + let { build } = await compile( + css` + @plugin "my-plugin"; + @layer base, utilities; + @layer utilities { + @tailwind utilities; + } + `, + { + loadModule: async () => ({ + path: '', + base: '/root', + module: ({ addBase }: PluginAPI) => { + addBase({ body: { '@variant dark': { color: 'red' } } }) + }, + }), + }, + ) + + let compiled = build(['underline']) + + expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(` + "@layer base { + @media (prefers-color-scheme: dark) { + body { + color: red; + } + } + } + + @layer utilities { + .underline { + text-decoration-line: underline; + } + }" + `) +}) + it("should error when `layer(…)` is used, but it's not the first param", async () => { await expect(async () => { return await compileCss( From 5483f6d00098c486383beeb072937d3073117edf Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 4 Nov 2025 10:51:34 -0500 Subject: [PATCH 2/4] Move comment --- packages/tailwindcss/src/design-system.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tailwindcss/src/design-system.ts b/packages/tailwindcss/src/design-system.ts index d533743c172b..100dcbc3812e 100644 --- a/packages/tailwindcss/src/design-system.ts +++ b/packages/tailwindcss/src/design-system.ts @@ -77,11 +77,11 @@ export function buildDesignSystem(theme: Theme): DesignSystem { return new DefaultMap((candidate) => { let ast = compileAstNodes(candidate, designSystem, flags) - // Arbitrary values (`text-[theme(--color-red-500)]`) and arbitrary - // properties (`[--my-var:theme(--color-red-500)]`) can contain function - // calls so we need evaluate any functions we find there that weren't in - // the source CSS. try { + // Arbitrary values (`text-[theme(--color-red-500)]`) and arbitrary + // properties (`[--my-var:theme(--color-red-500)]`) can contain function + // calls so we need evaluate any functions we find there that weren't in + // the source CSS. substituteFunctions( ast.map(({ node }) => node), designSystem, From ced5c3ad4a678fe2bcb39ddc498139abb135a5a8 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 4 Nov 2025 10:52:00 -0500 Subject: [PATCH 3/4] Substitute `@variant` inside utilities --- packages/tailwindcss/src/design-system.ts | 8 +++++++- packages/tailwindcss/src/index.test.ts | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/tailwindcss/src/design-system.ts b/packages/tailwindcss/src/design-system.ts index 100dcbc3812e..384746b57b17 100644 --- a/packages/tailwindcss/src/design-system.ts +++ b/packages/tailwindcss/src/design-system.ts @@ -23,7 +23,7 @@ import { Theme, ThemeOptions, type ThemeKey } from './theme' import { Utilities, createUtilities, withAlpha } from './utilities' import { DefaultMap } from './utils/default-map' import { extractUsedVariables } from './utils/variables' -import { Variants, createVariants } from './variants' +import { Variants, createVariants, substituteAtVariant } from './variants' export const enum CompileAstFlags { None = 0, @@ -86,6 +86,12 @@ export function buildDesignSystem(theme: Theme): DesignSystem { ast.map(({ node }) => node), designSystem, ) + + // JS plugins might contain an `@variant` inside a generated utility + substituteAtVariant( + ast.map(({ node }) => node), + designSystem, + ) } catch (err) { // If substitution fails then the candidate likely contains a call to // `theme()` that is invalid which may be because of incorrect usage, diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index e67df73dcf40..5cd5282b00f7 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -4678,14 +4678,19 @@ test('JS APIs support @variant', async () => { loadModule: async () => ({ path: '', base: '/root', - module: ({ addBase }: PluginAPI) => { + module: ({ addBase, addUtilities, matchUtilities }: PluginAPI) => { addBase({ body: { '@variant dark': { color: 'red' } } }) + addUtilities({ '.foo': { '@variant dark': { '--foo': 'foo' } } }) + matchUtilities( + { bar: (value) => ({ '@variant dark': { '--bar': value } }) }, + { values: { one: '1' } }, + ) }, }), }, ) - let compiled = build(['underline']) + let compiled = build(['underline', 'foo', 'bar-one']) expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(` "@layer base { @@ -4700,6 +4705,16 @@ test('JS APIs support @variant', async () => { .underline { text-decoration-line: underline; } + + @media (prefers-color-scheme: dark) { + .bar-one { + --bar: 1; + } + + .foo { + --foo: foo; + } + } }" `) }) From 4b9db06fabd2739be58a8f4bb07810075329da3d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 4 Nov 2025 11:22:04 -0500 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a405a1c476b..99b35a7d755e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Substitute `@variant` inside legacy JS APIs ([#19263](https://github.com/tailwindlabs/tailwindcss/pull/19263)) + ### Added - _Experimental_: Add `@container-size` utility ([#18901](https://github.com/tailwindlabs/tailwindcss/pull/18901))