Skip to content

no-conflicting-classes ​

oxlint rule that reports Tailwind CSS classes on one element whose CSS clashes, like p-2 p-4, comparing the declarations your design system emits.

At a glance ​

AutofixEditor suggestionsDesign systemOptions
NoNoRequired (entryPoint)entryPoint, reportRedundant, allow

What this rule does ​

Detects pairs of Tailwind classes on the same element, under the same variant, whose CSS declarations clash — where one of the two is silently discarded. The comparison is made against the CSS the design system actually emits, values included: property names alone cannot tell a conflict from a composition.

A shared property is a conflict only when the declaration that LOSES the cascade carries something the winner does not reproduce. Which means these are not conflicts, and none of them is special-cased:

  • the same value on both sides — mask-b-from-50% mask-b-from-black share four byte-identical declarations, so whoever wins, the result is the same;
  • a var() forwarder and the class supplying the variable — outline-2 declares outline-style: var(--tw-outline-style) and carries no value of its own; matching is by the real variable name, so a plugin's --scrollbar-* behaves exactly like Tailwind's --tw-*;
  • a winner that still pulls the loser in — drop-shadow-indigo-500 reads the --tw-drop-shadow-size that drop-shadow-xl writes, followed transitively (that is how the from-* / via-* / to-* chain composes);
  • a custom property reset to initial — animate-in initializes every --tw-enter-* for its modifiers to override;
  • declarations on different boxes — placeholder-* styles ::placeholder, space-x-* styles the children, neither styles the element.

Two classes that declare the same property with the same value are reported as redundant instead: not a conflict, but one of them is dead weight. reportRedundant: false opts out.

Which class wins is asked of the design system, not inferred from the order you wrote the classes in — Tailwind's output does not depend on that order. When the position cannot be known (for a class whose value you wrote, like w-[10px]), the diagnostic reports the clash without naming a winner.

Only two compositions cannot be derived from the CSS, and they stay declared in the source with the reason why: prose size variants and prose + max-w-* (the plugin intends the override and the emitted CSS is indistinguishable from an accident), and mask-composite modes. For a composition your own plugins produce, use the allow option rather than waiting for a release.

DS-dependent — requires settings.tailwindcss.entryPoint. When the design system can't load, the rule emits a single fatal designSystemUnavailable diagnostic per file instead of silently passing.

Options ​

OptionTypeDefaultDescription
reportRedundantbooleantrueReport two classes that declare the same property with the same value as redundant.
allow(string | [string, string])[][]Patterns to silence, matched against the class as written (variant prefix and ! included). A bare pattern silences any pair involving a matching class; a two-element pattern silences that combination, in either order.
entryPointstring—Per-rule override of settings.tailwindcss.entryPoint.
jsonc
{
  "rules": {
    "tailwindcss/no-conflicting-classes": [
      "error",
      {
        // Your plugin composes in a way the emitted CSS cannot show.
        "allow": [["^gutter-thin$", "^gutter-(thumb|track)-"]],
      },
    ],
  },
}

Examples ​

✗ Incorrect ​

tsx
// Same property, different value: one of them is discarded
<div className="text-red-500 text-blue-500" />

// Conflict survives across variants when both classes share the same one
<div className="hover:bg-red-500 hover:bg-blue-500" />

// `!important` doesn't help — same property, still a conflict
<div className="!text-red-500 !text-blue-500" />

// Same gradient role conflicts (`from-` + `from-`); different roles compose
<div className="from-red-500 from-blue-500" />

// Asymmetric narrowing: wider class AFTER the narrower one clobbers it
<div className="h-6 size-4" />

✓ Correct ​

tsx
// Different properties
<div className="flex items-center" />

// Different variants — applied under different conditions
<div className="hover:bg-red-500 focus:bg-blue-500" />

// Gradient stops are complementary
<div className="from-blue-500 via-purple-500 to-pink-500" />

// `shadow-*` + `ring-*` compose via disjoint --tw-* custom properties
<div className="shadow-lg ring-1 ring-offset-2" />

Interactions with other rules ​

  • no-duplicate-classes: complementary. Duplicates are the exact same class repeated; conflicts are different classes that hit the same property. Keep both rules on.
  • enforce-sort-order: cosmetic for the plain CSS cascade — which class wins is decided by the generated stylesheet, not by attribute order, so sorting only makes the pair easier to read. The one exception is tailwind-merge: it resolves same-group classes by last occurrence in the string, so reordering a fragment can flip which one survives. That only bites two conflicting classes co-located in a single merge fragment — the anti-pattern this rule already flags — so fixing the conflict removes the sort hazard too. See that rule's own note.
  • no-deprecated-classes: a deprecated alias and its modern equivalent (flex-grow + grow) will trip this rule on the property level. Fixing the deprecation usually resolves the conflict.
  • enforce-canonical: rewriting to canonical forms collapses trivially-aliased pairs before they reach this rule.

When to disable it ​

  • Generated class lists where the order is meaningful and you rely on "last wins" semantics intentionally (e.g. a base + override pattern in a design system primitive). Prefer extracting the override into a cn()/twMerge() call so the conflict becomes explicit.
  • Codebases whose own plugins compose in a way the emitted CSS cannot show: use the allow option rather than disabling the rule. Patterns are matched against the class as written, so include the variant prefix if you need to silence hover: forms too.
  • Tests / fixtures that intentionally build conflicting class strings to exercise other tooling.

Released under the MIT License.