Robin van der Vleuten

Making Tailwind stick in existing stylesheets

A few years ago I wrote about how I started thinking about my styles. That post was mostly about the mental switch: stop treating every bit of styling as something that needs a beautiful component name first.

That idea still feels right to me.

Tailwind makes it practical in a way the older atomic CSS tools never quite did. You can build a screen with small decisions, right there in the markup, and the result is usually easier to change than a pile of optimistic class names.

But most projects do not start with an empty stylesheet.

They start with a few years of Sass, a CMS theme, a typography wrapper, button classes from three redesigns ago, and one selector that nobody wants to touch because it is probably load-bearing.

That is where Tailwind can feel surprisingly fragile.

The awkward middle

The awkward middle is the phase where Tailwind is already useful, but old CSS is still everywhere.

You add a utility because you want the next change to be local:

html
<a class="button bg-gray-900 text-white px-4 py-2">
Continue
</a>

Then the browser shrugs and keeps the button blue.

Somewhere else, a selector like this is waiting:

css
.checkout .button {
background: #1d4ed8;
color: white;
}

The utility did nothing wrong. The old selector is just heavier.

This is the part I dislike most about gradual adoption. Not the old CSS itself. Old CSS is allowed to exist. The annoying part is that every utility becomes a question instead of an answer.

Will this class apply here? Does the parent have a special wrapper? Is there a more specific rule in a file I forgot about? Why is changing a background color suddenly archaeology?

One boring configuration line

Tailwind has an important configuration option that marks every utility declaration as !important.

js
// tailwind.config.js
module.exports = {
important: true,
}

It is not subtle. That is the point.

With that option enabled, Tailwind emits utilities that are much harder for old component CSS to accidentally override.

css
.bg-gray-900 {
background-color: #111827 !important;
}

The markup starts behaving like the markup again:

html
<a class="button bg-gray-900 text-white px-4 py-2">
Continue
</a>

Now bg-gray-900 is not a suggestion. It is the decision for this element.

That sounds dangerous if you have spent years learning to avoid !important. I have too. Most random !important declarations are a smell because they are local patches without a larger rule behind them.

This is different. It is one rule for one layer.

Tailwind utilities become the place where small overrides live. Existing CSS can keep handling the broad strokes while you move the project over piece by piece.

Why I like it before everything is Tailwind

When a project is already fully written with Tailwind, this option matters less. There are fewer old selectors trying to compete with your utilities.

The benefit shows up when you are not there yet.

It makes migration incremental. You can improve one template without first flattening the entire CSS cascade around it.

It keeps changes close to the markup. If a link needs a different size, the change is visible on the link instead of hidden in another partial.

It reduces tiny naming problems. You do not need .button--checkout-but-slightly-darker just because one button is special.

It avoids selector escalation. Without it, the easy fix is often writing a more specific selector. That works once. Then the next person has to beat yours.

That last point is the trap. Specificity rarely grows because someone planned it. It grows because every small change needs to survive the selectors that came before it.

Where it can bite

There is one trade-off I always check before enabling it: JavaScript that writes inline styles.

Some libraries control elements by setting styles directly. Think maps, sliders, animations, or widgets that bring their own opinions. If those inline styles need to beat Tailwind classes, global important utilities can get in the way.

For most application markup, I prefer class changes anyway:

html
<div class="hidden md:block">
...
</div>

That is easier to reason about than mixing classes with style="display: block". But third-party code does not always ask what I prefer, which is rude but common.

So I would not enable this blindly in a project with lots of DOM-manipulating widgets. I would test those first.

Closing thoughts

I still do not like seeing !important sprinkled through a stylesheet. It usually means nobody knows who is supposed to win anymore.

But enabling it for Tailwind utilities is not sprinkling. It is drawing a boundary.

Old CSS can keep doing what it does while you move forward. New markup gets a calmer way to express small styling decisions. No selector arms race, no mystery modifier names, no pretending the old cascade is simpler than it is.