Ensure custom variants can be used via @variant in addBase (#20247)

This PR fixes an issue where `@variant` inside `addBase` is being used
with a custom variant.

The issue is that we substitute the `@variant` calls immediately when we
call the `addBase` function. That means that variants that aren't
processed yet will error out.

This is a regression, because this used to work in Tailwind CSS v4.3.0
and started failing in Tailwind CSS v4.3.1.

## Test plan

1. Added a regression test
This commit is contained in:
Robin Malfait 2026-06-16 23:05:50 +02:00 • committed by GitHub
parent cc3b634fe0
commit 707c23b955
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 37 additions and 7 deletions

View file

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure `@tailwindcss/cli` in `--watch` mode doesn't crash on Windows when `@source` points to a directory that doesn't exist ([#20242](https://github.com/tailwindlabs/tailwindcss/pull/20242))
- Ensure `@tailwindcss/vite` doesn't crash in Deno v2.8.x when `context.parentURL` is not a valid URL ([#20245](https://github.com/tailwindlabs/tailwindcss/pull/20245))
- Ensure `@tailwindcss/cli` in `--watch` mode rebuilds when the input CSS file changes in an ignored directory ([#20246](https://github.com/tailwindlabs/tailwindcss/pull/20246))
- Ensure `@variant` rules generated by `addBase` can use custom variants defined later ([#20247](https://github.com/tailwindlabs/tailwindcss/pull/20247))
## [4.3.1] - 2026-06-12

View file

@ -1527,6 +1527,41 @@ describe('addBase', () => {
"
`)
})
test('@variant inside addBase works with custom variants', async () => {
expect(
await compileCss(
css`
@plugin "my-plugin";
@custom-variant custom (&.custom);
`,
{
loadModule: async () => ({
path: '',
base: '/root',
module: plugin(function ({ addBase }) {
addBase({
':root': {
'@variant custom': {
'--x': '1',
},
},
})
}),
}),
},
),
).toMatchInlineSnapshot(`
"
@layer base {
:root.custom {
--x: 1;
}
}
"
`)
})
})
describe('addVariant', () => {

View file

@ -13,12 +13,7 @@ import { escape } from '../utils/escape'
import { inferDataType } from '../utils/infer-data-type'
import { segment } from '../utils/segment'
import { toKeyPath } from '../utils/to-key-path'
import {
compoundsForSelectors,
IS_VALID_VARIANT_NAME,
substituteAtSlot,
substituteAtVariant,
} from '../variants'
import { compoundsForSelectors, IS_VALID_VARIANT_NAME, substituteAtSlot } from '../variants'
import { walk, WalkAction } from '../walk'
import type { ResolvedConfig, UserConfig } from './config/types'
import { createThemeFn } from './plugin-functions'
@ -115,7 +110,6 @@ export function buildPluginApi({
if (referenceMode) return
let baseNodes = objectToAst(css)
featuresRef.current |= substituteFunctions(baseNodes, designSystem)
featuresRef.current |= substituteAtVariant(baseNodes, designSystem)
let rule = atRule('@layer', 'base', baseNodes)
walk([rule], (node) => {
node.src = src