Tailwind CSS in Production · Missing and Wrong Styles

The Button That Ignored Its Override

A shared Button component accepted extra classes, but passing p-2 or bg-red-600 did nothing: conflicting utilities are decided by CSS order, not class order.

Create a free account to save this page, take notes and track your progress. Sign in

Scenario

Example team: a design-system Button has px-4 py-2 bg-indigo-600 and appends props.className. A feature team needs a compact red delete button and passes className="px-2 py-1 bg-red-600".

What you see in production

The delete button is still indigo and still large. Developers add !important (bg-red-600!), then more, and the component becomes impossible to reason about.

The naive code

const Button = ({ className = "", ...props }) => (
  <button className={`rounded-lg px-4 py-2 bg-indigo-600 text-white ${className}`} {...props} />
);

Reproduce it

Both buttons below have both colours in their class list, in opposite orders. They look the same:

Class order doesn't decide the winnerTry it yourself
<div class="flex gap-4 p-6">
  <button class="rounded-lg bg-indigo-600 bg-red-600 px-4 py-2 text-white">indigo then red</button>
  <button class="rounded-lg bg-red-600 bg-indigo-600 px-4 py-2 text-white">red then indigo</button>
  <button class="rounded-lg bg-red-600 px-2 py-1 text-sm text-white">merged: only red, compact</button>
</div>

Root cause

When two utilities set the same property, the one later in the generated stylesheet wins, and Tailwind sorts utilities by its own rules, not by the order in your class attribute. Appending classes can't reliably override a component's defaults.

The fix

  • Merge class lists with tailwind-merge (twMerge("px-4 py-2 bg-indigo-600", "px-2 py-1 bg-red-600") keeps only the last of each conflicting group).
  • Better still, give the component variants (variant="danger" size="sm") with complete class strings, for example using cva (class-variance-authority).
  • Reserve ! (important) for real edge cases.

Trade-offs

  • tailwind-merge adds a small runtime cost and must know your custom theme utilities (configure it if you add @theme tokens).
  • Variants limit flexibility on purpose; that's what keeps a design system consistent.

Detect it in production

  • Review components that concatenate className without a merge function.
  • Search for ! important utilities; a growing count is a sign of override fights.

Key points

  • Conflicting utilities resolve by stylesheet order, not attribute order.
  • Merge with tailwind-merge or expose explicit variants.
  • !important is a symptom, not a fix.

Practice questions

If an element has class="p-4 p-2", which padding applies and why?

Answer

Whichever utility appears later in the generated CSS, which Tailwind decides, not the order in the class attribute. Avoid conflicting utilities on one element; merge them with tailwind-merge or use variants.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?