tailwindlabs/tailwindcss · error · Error
Cannot use `@variant` with empty variant
Error message
Cannot use `@variant` with empty variant
What it means
Thrown by substituteAtVariant() while processing an @variant at-rule in CSS. The rule's params are split on ':' to get a stacked variant list (e.g. 'hover:focus'); if any segment is empty after trimming, the variant is malformed. This catches stray colons like '@variant hover:' or '@variant :focus'.
Source
Thrown at packages/tailwindcss/src/variants.ts:1300
for (let [idx, compoundVariant] of compoundVariants.entries()) {
// Starting with the `&` rule node
//
// Only clone the nodes when we have multiple compound variants to deal
// with. The last one can use the original nodes. We do need unique AST
// nodes for sourcemap `dst` location information.
let node = styleRule(
'&',
idx === compoundVariants.length - 1
? variantNode.nodes
: variantNode.nodes.map(cloneAstNode),
)
let stackedVariants = segment(compoundVariant, ':')
for (let i = stackedVariants.length - 1; i >= 0; --i) {
let variant = stackedVariants[i].trim()
if (!variant) {
throw new Error(`Cannot use \`@variant\` with empty variant`)
}
let variantAst = designSystem.parseVariant(variant)
if (variantAst === null) {
throw new Error(`Cannot use \`@variant\` with unknown variant: ${variant}`)
}
let result = applyVariant(node, variantAst, designSystem.variants)
if (result === null) {
throw new Error(`Cannot use \`@variant\` with variant: ${variant}`)
}
}
if (node.selector === '&') {
nodes.push(...node.nodes)
} else {
nodes.push(node)
}View on GitHub (pinned to 16e94cbf7f)
Solutions
- Remove the stray colon so every segment is non-empty: '@variant hover:focus { ... }'.
- When building variant stacks dynamically, filter out empty segments before joining.
- Validate that no segment of the colon-split string is empty before writing the @variant rule.
Example fix
/* before — throws */
@variant :hover {
.btn { color: red; }
}
/* after */
@variant hover {
.btn { color: red; }
} Defensive patterns
Strategy: validation
Validate before calling
function validateVariantStack(params: string): void {
for (const part of params.split(':')) {
if (part.trim() === '') {
throw new Error(`Empty variant segment in '${params}'`)
}
}
} Type guard
function hasNoEmptyVariant(params: string): boolean {
return params.split(':').every((s) => s.trim().length > 0)
} Prevention
- Avoid leading/trailing/double colons in @variant params.
- When joining variant stacks dynamically, filter empty strings first.
- Lint @variant rules for stray colons.
When it happens
Trigger: Writing `@variant :focus { ... }` (leading colon), `@variant hover: { ... }` (trailing colon), or `@variant hover::focus { ... }` (double colon) in CSS processed by Tailwind v4. The segment() split produces an empty string for the slot between/around the colons.
Common situations: Manually authoring @variant rules and adding a stray colon. Programmatically building variant strings by joining with ':' when one element is empty. Copying pseudo-selector syntax (::) into a variant stack.
Related errors
- Cannot use `@variant` with unknown variant: ${variant}
- Cannot use `@variant` with variant: ${variant}
- Invalid theme value `${value}` for namespace `${key}`
- The rule `@apply ${list}` must not have a body.
- Cannot apply utility class `${candidate}` because the `${unk
AI-assisted analysis of tailwindlabs/tailwindcss@16e94cbf7f (2026-08-12).
Data as JSON: /api/errors/b381f49393e6c0e6.
Report an issue: GitHub.