consistent-variant-order
Enforce a consistent order for Tailwind CSS variant prefixes
What this rule does
Reorders the variant prefixes inside a single class so that the chain is always written the same way. hover:dark:bg-red and dark:hover:bg-red produce the same CSS in Tailwind v4, but inconsistent variant order makes grep, code review, and diffs noisy. This rule picks one canonical order and rewrites everything to match, with autofix on the first offender and editor suggestions on the rest.
Pseudo-elements (before, after, file, placeholder, selection, marker, backdrop, first-line, first-letter, details-content) are always pinned innermost — closest to the utility — because in Tailwind v4 a pseudo-element placed before an element-selecting variant produces broken CSS like &::before { &>svg { … } }.
DS-optional — when settings.tailwindcss.entryPoint is configured, the rule uses the design system's variant priority table. When it isn't, the rule falls back to a built-in static order. Both fallbacks are deterministic; this is the only DS-dependent rule that tolerates a missing entry point silently.
Options
order
string[], optional.
Custom priority list. Variants appear in the order you list them; anything not listed sorts after, in its original position. Use this when your team has a house style that differs from Tailwind's default (e.g. you prefer dark: outermost in every chain). The pseudo-element pin still applies regardless of where they appear in your list.
{
"tailwindcss/consistent-variant-order": [
"error",
{ "order": ["dark", "sm", "md", "lg", "xl", "hover", "focus"] }
]
}entryPoint
string, optional.
Per-rule override of settings.tailwindcss.entryPoint. Same semantics as the project-level setting; almost never needed.
Examples
✗ Incorrect
// hover before responsive
<div className="hover:sm:flex" />
// ~~~~~~~~~~~~~ → sm:hover:flex
// hover before dark
<div className="hover:dark:text-white" />
// ~~~~~~~~~~~~~~~~~~~~~ → dark:hover:text-white
// pseudo-element before element-selecting variant — broken in Tailwind v4
<div className="before:[&>svg]:text-red-500" />
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ → [&>svg]:before:text-red-500✓ Correct
// Responsive → state
<div className="sm:hover:flex" />
// Color scheme → state
<div className="dark:hover:text-white" />
// Pseudo-element innermost
<div className="[&>svg]:before:text-red-500" />
<div className="dark:has-[.active]:before:text-red-500" />Interactions with other rules
enforce-sort-order: complementary.enforce-sort-ordersorts whole classes against each other;consistent-variant-ordersorts the prefixes inside one class. Run both.enforce-canonical: orthogonal. Canonical normalizes the utility shape (m-0,bg-red-500/50), not the variant chain.no-unknown-classes: when a variant is reordered the resulting class is still semantically equivalent, so unknown-class detection doesn't see a difference.
When to disable it
- You rely on
prettier-plugin-tailwindcssfor variant ordering too: the formatter handles it, the rule is redundant. Leaving both on is safe but extra work. - Custom variant order that's hard to express as a flat list (e.g. order depends on the utility): disable and rely on review.
- Generated code where variant order encodes meaning you don't want rewritten.