Skip to content

Commit ee987e3

Browse files
Discard matchVariant matches with unknown named values (#18799)
This PR fixes two issues: - When a variant is defined by `matchVariant` it could match unknown values but not apply the variant (because it's unknown). This would result in a utility being output that is the _same_ as a bare utility without variants but a longer name. These were intended to be discarded but weren't done so correctly. - Similarly, when we encounter a known value but its not a string the same thing would happen where we'd output a utility without applying the variant. This was also intended to be discarded. Basically given this code: ```js matchVariant( "foo", (value) => `&:is([data-foo='${value}'])`, { values: { DEFAULT: "", bar: "bar", obj: { some: "object" }, }, } ) ``` And this HTML: ```html <div class="foo-bar:bg-none foo-[baz]:bg-none foo-baz:bg-none foo-obj:bg-none"></div> ``` This CSS would be produced: ```css @layer utilities { .foo-bar\:bg-none { &:is([data-foo='bar']) { background-image: none; } } /* this one shouldn't be here */ .foo-baz\:bg-none { background-image: none; } /* this one shouldn't be here */ .foo-obj\:bg-none { background-image: none; } .foo-\[baz\]\:bg-none { &:is([data-foo='baz']) { background-image: none; } } } ```
1 parent ce9b290 commit ee987e3

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
### Fixed
1515

1616
- Don't transition `visibility` when using `transition` ([#18795](https://github.com/tailwindlabs/tailwindcss/pull/18795))
17+
- Discard matched variants with unknown named values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
18+
- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
1719

1820
## [4.1.12] - 2025-08-13
1921

packages/tailwindcss/src/compat/plugin-api.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,6 +2778,79 @@ describe('matchVariant', () => {
27782778
}"
27792779
`)
27802780
})
2781+
2782+
test('ignores variants that use unknown values', async () => {
2783+
let { build } = await compile(
2784+
css`
2785+
@plugin "my-plugin";
2786+
@layer utilities {
2787+
@tailwind utilities;
2788+
}
2789+
`,
2790+
{
2791+
loadModule: async (id, base) => {
2792+
return {
2793+
path: '',
2794+
base,
2795+
module: ({ matchVariant }: PluginAPI) => {
2796+
matchVariant('foo', (flavor) => `&:is(${flavor})`, {
2797+
values: {
2798+
known: 'known',
2799+
},
2800+
})
2801+
},
2802+
}
2803+
},
2804+
},
2805+
)
2806+
2807+
let compiled = build(['foo-[test]:flex', 'foo-known:flex', 'foo-unknown:flex'])
2808+
2809+
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
2810+
"@layer utilities {
2811+
.foo-known\\:flex:is(known), .foo-\\[test\\]\\:flex:is(test) {
2812+
display: flex;
2813+
}
2814+
}"
2815+
`)
2816+
})
2817+
2818+
test('ignores variants that produce non-string values', async () => {
2819+
let { build } = await compile(
2820+
css`
2821+
@plugin "my-plugin";
2822+
@layer utilities {
2823+
@tailwind utilities;
2824+
}
2825+
`,
2826+
{
2827+
loadModule: async (id, base) => {
2828+
return {
2829+
path: '',
2830+
base,
2831+
module: ({ matchVariant }: PluginAPI) => {
2832+
matchVariant('foo', (flavor) => `&:is(${flavor})`, {
2833+
values: {
2834+
string: 'some string',
2835+
object: { some: 'object' },
2836+
},
2837+
})
2838+
},
2839+
}
2840+
},
2841+
},
2842+
)
2843+
2844+
let compiled = build(['foo-[test]:flex', 'foo-string:flex', 'foo-object:flex'])
2845+
2846+
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
2847+
"@layer utilities {
2848+
.foo-string\\:flex:is(some string), .foo-\\[test\\]\\:flex:is(test) {
2849+
display: flex;
2850+
}
2851+
}"
2852+
`)
2853+
})
27812854
})
27822855

27832856
describe('addUtilities()', () => {

packages/tailwindcss/src/compat/plugin-api.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,12 @@ export function buildPluginApi({
203203
} else if (variant.value.kind === 'named' && options?.values) {
204204
let defaultValue = options.values[variant.value.value]
205205
if (typeof defaultValue !== 'string') {
206-
return
206+
return null
207207
}
208208

209209
ruleNodes.nodes = resolveVariantValue(defaultValue, variant.modifier, ruleNodes.nodes)
210+
} else {
211+
return null
210212
}
211213
})
212214
},

0 commit comments

Comments
 (0)