diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e8f259c8..6f0181e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Substitute `@variant` inside legacy JS APIs ([#19263](https://github.com/tailwindlabs/tailwindcss/pull/19263)) - Handle utilities with `1` keys in a JS config ([#19254](https://github.com/tailwindlabs/tailwindcss/pull/19254)) ### Added diff --git a/Cargo.lock b/Cargo.lock index dc3dce005..162cf37dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,19 +68,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -109,15 +96,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -595,7 +573,6 @@ dependencies = [ "bexpand", "bstr", "classification-macros", - "crossbeam", "dunce", "fast-glob", "globwalk", diff --git a/crates/oxide/Cargo.toml b/crates/oxide/Cargo.toml index 9b800b049..0965320c9 100644 --- a/crates/oxide/Cargo.toml +++ b/crates/oxide/Cargo.toml @@ -9,7 +9,6 @@ globwalk = "0.9.1" log = "0.4.22" rayon = "1.10.0" fxhash = { package = "rustc-hash", version = "2.1.1" } -crossbeam = "0.8.4" tracing = { version = "0.1.40", features = [] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } walkdir = "2.5.0" diff --git a/packages/tailwindcss/src/design-system.ts b/packages/tailwindcss/src/design-system.ts index d533743c1..384746b57 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, @@ -77,15 +77,21 @@ 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, ) + + // 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 f1dad6ccd..5cd5282b0 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -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( diff --git a/playgrounds/nextjs/package.json b/playgrounds/nextjs/package.json index e6b30f127..a822b0dd9 100644 --- a/playgrounds/nextjs/package.json +++ b/playgrounds/nextjs/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@types/node": "catalog:", "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.1", + "@types/react-dom": "^19.2.2", "eslint": "^9.37.0", "eslint-config-next": "^15.5.6", "typescript": "^5.5.4" diff --git a/playgrounds/v3/package.json b/playgrounds/v3/package.json index 2f8d432d1..121f31e52 100644 --- a/playgrounds/v3/package.json +++ b/playgrounds/v3/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@types/node": "^20.14.8", "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.1", + "@types/react-dom": "^19.2.2", "autoprefixer": "^10.4.21", "eslint": "^9.37.0", "eslint-config-next": "^15.5.6", diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 0d1696e40..e0e696d5d 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.1", + "@types/react-dom": "^19.2.2", "bun": "^1.3.0", "vite": "catalog:" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54c64a973..23f775083 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -499,8 +499,8 @@ importers: specifier: ^19.2.2 version: 19.2.2 '@types/react-dom': - specifier: ^19.2.1 - version: 19.2.1(@types/react@19.2.2) + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) eslint: specifier: ^9.37.0 version: 9.37.0(jiti@2.6.1) @@ -533,8 +533,8 @@ importers: specifier: ^19.2.2 version: 19.2.2 '@types/react-dom': - specifier: ^19.2.1 - version: 19.2.1(@types/react@19.2.2) + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.4.47) @@ -570,8 +570,8 @@ importers: specifier: ^19.2.2 version: 19.2.2 '@types/react-dom': - specifier: ^19.2.1 - version: 19.2.1(@types/react@19.2.2) + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) bun: specifier: ^1.3.0 version: 1.3.0 @@ -2433,8 +2433,8 @@ packages: '@types/postcss-import@14.0.3': resolution: {integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==} - '@types/react-dom@19.2.1': - resolution: {integrity: sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==} + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} peerDependencies: '@types/react': ^19.2.0 @@ -6048,7 +6048,7 @@ snapshots: dependencies: postcss: 8.4.41 - '@types/react-dom@19.2.1(@types/react@19.2.2)': + '@types/react-dom@19.2.2(@types/react@19.2.2)': dependencies: '@types/react': 19.2.2