From ad6693906a321912c90fd13c4a92e2cb26660985 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 4 Jun 2026 06:46:40 -0400 Subject: [PATCH] Allow `@variant` to be used inside `addBase` (#19480) This seems like an unintentional omission --------- Co-authored-by: Robin Malfait --- CHANGELOG.md | 1 + .../tailwindcss/src/compat/plugin-api.test.ts | 49 +++++++++++++++++++ packages/tailwindcss/src/compat/plugin-api.ts | 8 ++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 567e3ab26..1bd4f34e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure standalone `@tailwindcss/cli` binaries are ignored when scanning for class candidates ([#20139](https://github.com/tailwindlabs/tailwindcss/pull/20139)) - Ensure class candidates are extracted from Twig `addClass(…)` and `removeClass(…)` calls ([#20198](https://github.com/tailwindlabs/tailwindcss/pull/20198)) - Don't crash in the Ruby or Vue preprocessors when scanning files containing invalid UTF-8 bytes ([#19588](https://github.com/tailwindlabs/tailwindcss/pull/19588)) +- Allow `@variant` to be used inside `addBase` ([#19480](https://github.com/tailwindlabs/tailwindcss/pull/19480)) ### Changed diff --git a/packages/tailwindcss/src/compat/plugin-api.test.ts b/packages/tailwindcss/src/compat/plugin-api.test.ts index c1d9cb57a..ff6bdce2f 100644 --- a/packages/tailwindcss/src/compat/plugin-api.test.ts +++ b/packages/tailwindcss/src/compat/plugin-api.test.ts @@ -1478,6 +1478,55 @@ describe('addBase', () => { " `) }) + + test('@variant is replaced inside rules using addBase', async () => { + expect( + await compileCss( + css` + @plugin "my-plugin"; + @theme { + --breakpoint-supertiny: 128px; + } + `, + { + loadModule: async () => ({ + path: '', + base: '/root', + module: plugin(function ({ addBase }) { + addBase({ + ':root': { + '@variant supertiny': { + '--PascalCase': '1', + '--camelCase': '1', + '--UPPERCASE': '1', + }, + '@variant data-enabled:focus, disabled': { + '--x': '1', + }, + }, + }) + }), + }), + }, + ), + ).toMatchInlineSnapshot(` + " + @layer base { + @media (min-width: 128px) { + :root { + --PascalCase: 1; + --camelCase: 1; + --UPPERCASE: 1; + } + } + + :root[data-enabled]:focus, :root:disabled { + --x: 1; + } + } + " + `) + }) }) describe('addVariant', () => { diff --git a/packages/tailwindcss/src/compat/plugin-api.ts b/packages/tailwindcss/src/compat/plugin-api.ts index af6e98d77..20ecb14ae 100644 --- a/packages/tailwindcss/src/compat/plugin-api.ts +++ b/packages/tailwindcss/src/compat/plugin-api.ts @@ -13,7 +13,12 @@ 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 } from '../variants' +import { + compoundsForSelectors, + IS_VALID_VARIANT_NAME, + substituteAtSlot, + substituteAtVariant, +} from '../variants' import { walk, WalkAction } from '../walk' import type { ResolvedConfig, UserConfig } from './config/types' import { createThemeFn } from './plugin-functions' @@ -110,6 +115,7 @@ 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