tailwindlabs/tailwindcss · error · Error
`@custom-variant ${name}` defines an invalid variant name. V
Error message
`@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. What it means
Thrown when the first space-delimited token of `@custom-variant`'s params fails `IS_VALID_VARIANT_NAME = /^@?[a-z0-9][a-zA-Z0-9_-]*(?<![_-])$/`. Variant names must start with a lowercase letter or digit, contain only alphanumerics, dashes, or underscores, and must not end with `-` or `_`.
Source
Thrown at packages/tailwindcss/src/index.ts:360
}
// Collect all the `@variant` at-rules, we will replace them later once
// all variants are registered in the system.
else {
variantNodes.push(node)
}
}
// Register custom variants from `@custom-variant` at-rules
if (node.name === '@custom-variant') {
if (ctx.parent !== null) {
throw new Error('`@custom-variant` cannot be nested.')
}
let [name, selector] = segment(node.params, ' ')
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 and start with a lowercase letter or number.`,
)
}
if (node.nodes.length > 0 && selector) {
throw new Error(`\`@custom-variant ${name}\` cannot have both a selector and a body.`)
}
// Variants with a selector, but without a body, e.g.: `@custom-variant hocus (&:hover, &:focus);`
if (node.nodes.length === 0) {
if (!selector) {
throw new Error(`\`@custom-variant ${name}\` has no selector or body.`)
}
let selectors = segment(selector.slice(1, -1), ',')
if (selectors.length === 0 || selectors.some((selector) => selector.trim() === '')) {
throw new Error(
`\`@custom-variant ${name} (${selectors.join(',')})\` selector is invalid.`,View on GitHub (pinned to 16e94cbf7f)
Solutions
- Rename to start with `a-z0-9` and contain only `[a-zA-Z0-9_-]` without a trailing `-` or `_`: `@custom-variant hocus (...)`.
- Convert PascalCase to kebab-case: `Hocus` → `hocus`.
- Strip trailing dashes/underscores.
Example fix
/* before */ @custom-variant Hocus_ (&:hover, &:focus); /* after */ @custom-variant hocus (&:hover, &:focus);
Defensive patterns
Strategy: type-guard
Type guard
// Identical to the library's IS_VALID_VARIANT_NAME (variants.ts:27).
const IS_VALID_VARIANT_NAME = /^@?[a-z0-9][a-zA-Z0-9_-]*(?<![_-])$/
function isValidVariantName(name: string): boolean {
return IS_VALID_VARIANT_NAME.test(name)
} Prevention
- Start variant names with a lowercase letter or digit; never uppercase.
- Avoid trailing `-` or `_`; prefer kebab-case (`my-variant`).
- Share the `IS_VALID_VARIANT_NAME` regex in a constants file so lint and runtime agree.
When it happens
Trigger: Naming a variant `@custom-variant Hocus`, `@custom-variant my variant`, `@custom-variant foo-` (trailing dash), `@custom-variant 1foo` is allowed (starts with digit) but `@custom-variant .foo` is not.
Common situations: Using CamelCase or PascalCase by convention; trailing dash from a partial rename; embedding special characters copied from a selector.
Related errors
- `@utility ${node.params}` defines an invalid utility name. U
- `@custom-variant ${name}` cannot have both a selector and a
- `@custom-variant ${name}` has no selector or body.
- `@custom-variant ${name} (${selectors.join(',')})` selector
- The prefix "${themePrefix}" is invalid. Prefixes must be low
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/3fa61641c8d44c38.
Report an issue: GitHub.