Substitute @variant inside utilities (#19263)

Fixes #19258
This commit is contained in:
Jordan Pittman 2025-11-04 11:28:52 -05:00 committed by GitHub
parent babe825f2d
commit dc6a3ce349
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 5 deletions

View File

@ -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))

View File

@ -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,
@ -77,15 +77,21 @@ export function buildDesignSystem(theme: Theme): DesignSystem {
return new DefaultMap<Candidate>((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,
)
// 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,

View File

@ -4665,6 +4665,60 @@ 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, 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', 'foo', 'bar-one'])
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
"@layer base {
@media (prefers-color-scheme: dark) {
body {
color: red;
}
}
}
@layer utilities {
.underline {
text-decoration-line: underline;
}
@media (prefers-color-scheme: dark) {
.bar-one {
--bar: 1;
}
.foo {
--foo: foo;
}
}
}"
`)
})
it("should error when `layer(…)` is used, but it's not the first param", async () => {
await expect(async () => {
return await compileCss(