From 9823ffe2ea627db71800d31816b1a8dba3811442 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Sep 2025 12:52:20 +0200 Subject: [PATCH 1/4] add failing test --- packages/tailwindcss/src/index.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index cbdb88186dc9..82293f1d03fc 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -3772,6 +3772,26 @@ describe('@custom-variant', () => { ) }) + test('@custom-variant cannot contain dashes on its own', () => { + return expect( + compileCss(css` + @custom-variant - (&.dash); + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@custom-variant -\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.]`, + ) + }) + + test('@custom-variant cannot contain multiple dashes on their own', () => { + return expect( + compileCss(css` + @custom-variant --- (&.dashed); + `), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: \`@custom-variant ---\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.]`, + ) + }) + test('@custom-variant must not container special characters', () => { return expect( compileCss(css` From a9ba89be656483e0ac2bd8dfcd45a9a8479e52d5 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Sep 2025 12:52:26 +0200 Subject: [PATCH 2/4] adjust variant name validation regex This will disallow variants starting with `-` (or existing on its own) --- packages/tailwindcss/src/variants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tailwindcss/src/variants.ts b/packages/tailwindcss/src/variants.ts index 701f08c0bdb4..c29a936eb9c0 100644 --- a/packages/tailwindcss/src/variants.ts +++ b/packages/tailwindcss/src/variants.ts @@ -18,7 +18,7 @@ import { DefaultMap } from './utils/default-map' import { isPositiveInteger } from './utils/infer-data-type' import { segment } from './utils/segment' -export const IS_VALID_VARIANT_NAME = /^@?[a-zA-Z0-9_-]*$/ +export const IS_VALID_VARIANT_NAME = /^@?[a-z0-9][a-zA-Z0-9_-]*$/ type VariantFn = ( rule: Rule, From c5c3ed7e2fadaf222f98cbd950af58829476d610 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Sep 2025 16:14:41 +0200 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a1dcc5195cc..5ece9ec790f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Hide internal fields from completions in `matchUtilities` ([#18820](https://github.com/tailwindlabs/tailwindcss/pull/18820)) - Ignore `.vercel` folders by default (can be overridden by `@source …` rules) ([#18855](https://github.com/tailwindlabs/tailwindcss/pull/18855)) - Consider variants starting with `@-` to be invalid (e.g. `@-2xl:flex`) ([#18869](https://github.com/tailwindlabs/tailwindcss/pull/18869)) +- Do not allow custom variants to start with a `-` ([#18867](https://github.com/tailwindlabs/tailwindcss/pull/18867)) - Upgrade: Migrate `aria` theme keys to `@custom-variant` ([#18815](https://github.com/tailwindlabs/tailwindcss/pull/18815)) - Upgrade: Migrate `data` theme keys to `@custom-variant` ([#18816](https://github.com/tailwindlabs/tailwindcss/pull/18816)) - Upgrade: Migrate `supports` theme keys to `@custom-variant` ([#18817](https://github.com/tailwindlabs/tailwindcss/pull/18817)) From 7a727032c86e69b5df3bb6a345528dbec563d7d5 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 3 Sep 2025 16:27:09 +0200 Subject: [PATCH 4/4] adjust error message more correct rules in place Co-Authored-By: Jordan Pittman --- packages/tailwindcss/src/compat/plugin-api.ts | 2 +- packages/tailwindcss/src/index.test.ts | 6 +++--- packages/tailwindcss/src/index.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/tailwindcss/src/compat/plugin-api.ts b/packages/tailwindcss/src/compat/plugin-api.ts index 42b1c6176734..6d13f4678e95 100644 --- a/packages/tailwindcss/src/compat/plugin-api.ts +++ b/packages/tailwindcss/src/compat/plugin-api.ts @@ -119,7 +119,7 @@ export function buildPluginApi({ addVariant(name, variant) { if (!IS_VALID_VARIANT_NAME.test(name)) { throw new Error( - `\`addVariant('${name}')\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.`, + `\`addVariant('${name}')\` defines an invalid variant name. Variants should only contain alphanumeric, dashes, or underscore characters and start with a lowercase letter or number.`, ) } diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 82293f1d03fc..d66199e6c6a8 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -3768,7 +3768,7 @@ describe('@custom-variant', () => { @custom-variant foo:bar (&:hover, &:focus); `), ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: \`@custom-variant foo:bar\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.]`, + `[Error: \`@custom-variant foo:bar\` defines an invalid variant name. Variants should only contain alphanumeric, dashes, or underscore characters and start with a lowercase letter or number.]`, ) }) @@ -3778,7 +3778,7 @@ describe('@custom-variant', () => { @custom-variant - (&.dash); `), ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: \`@custom-variant -\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.]`, + `[Error: \`@custom-variant -\` defines an invalid variant name. Variants should only contain alphanumeric, dashes, or underscore characters and start with a lowercase letter or number.]`, ) }) @@ -3788,7 +3788,7 @@ describe('@custom-variant', () => { @custom-variant --- (&.dashed); `), ).rejects.toThrowErrorMatchingInlineSnapshot( - `[Error: \`@custom-variant ---\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.]`, + `[Error: \`@custom-variant ---\` defines an invalid variant name. Variants should only contain alphanumeric, dashes, or underscore characters and start with a lowercase letter or number.]`, ) }) diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 07a23a3251d2..4be573efbb11 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -356,7 +356,7 @@ async function parseCss( if (!IS_VALID_VARIANT_NAME.test(name)) { throw new Error( - `\`@custom-variant ${name}\` defines an invalid variant name. Variants should only contain alphanumeric, dashes or underscore characters.`, + `\`@custom-variant ${name}\` defines an invalid variant name. Variants should only contain alphanumeric, dashes, or underscore characters and start with a lowercase letter or number.`, ) }