tailwindlabs/tailwindcss · error · Error
`@custom-variant ${name} (${selectors.join(',')})` selector
Error message
`@custom-variant ${name} (${selectors.join(',')})` selector is invalid. What it means
Thrown in selector-form when the parenthesized selector, after stripping the outer parens and splitting on `,`, is empty or contains empty segments. The check `selectors.length === 0 || selectors.some(s => s.trim() === '')` rejects inputs like `()` or `(,)` that would produce no real selectors.
Source
Thrown at packages/tailwindcss/src/index.ts:377
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.`,
)
}
let atRuleParams: string[] = []
let styleRuleSelectors: string[] = []
for (let selector of selectors) {
selector = selector.trim()
if (selector[0] === '@') {
atRuleParams.push(selector)
} else {
styleRuleSelectors.push(selector)
}
}
let usesAtScope = atRuleParams.some((param) => param.startsWith('@scope'))View on GitHub (pinned to 16e94cbf7f)
Solutions
- Provide at least one non-empty selector: `@custom-variant hocus (&:hover, &:focus)`.
- Remove stray commas or whitespace inside the parens.
- Switch to the body form if the variant logic is more complex than a selector list.
Example fix
/* before */ @custom-variant hocus (,); /* after */ @custom-variant hocus (&:hover, &:focus);
Defensive patterns
Strategy: validation
Validate before calling
// Validate the selector list inside @custom-variant name (...).
function validateVariantSelectorList(selectorGroup: string): string | null {
// selectorGroup is the parenthesized portion, e.g. "(&:hover, &:focus)"
if (!selectorGroup.startsWith('(') || !selectorGroup.endsWith(')')) {
return null // not selector-form; body validation handles it
}
const inner = selectorGroup.slice(1, -1)
const parts = inner.split(',')
if (parts.length === 0 || parts.some((s) => s.trim() === '')) {
return `Selector list '${selectorGroup}' is empty or contains empty entries`
}
return null
} Prevention
- Always put at least one real selector inside the parens.
- When deleting a selector from a list, also remove its trailing comma.
- Add a unit test that asserts your variant selector lists have no empty segments.
When it happens
Trigger: Writing `@custom-variant hocus ()` or `@custom-variant hocus (,)` or `@custom-variant hocus ( , )`.
Common situations: Leaving the parens empty while editing; trailing comma from a deleted selector; whitespace-only entries.
Related errors
- `@custom-variant ${name}` defines an invalid variant name. V
- `@custom-variant ${name}` cannot have both a selector and a
- `@custom-variant ${name}` has no selector or body.
- The prefix "${resolvedConfig.prefix}" is invalid. Prefixes m
- `addVariant('${name}')` defines an invalid variant name. Var
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/c8efd914b251b6c2.
Report an issue: GitHub.