Tailwind’s hidden trade-off

posted Originally published at medium.com 8 min read

If you hang around modern frontend discourse (or pretty much any React codebase), Tailwind feels like the default.

It’s fast. It’s polished. It has great docs. It makes UIs look decent by accident.

And yet… I don’t use it.

This post is my attempt to explain that without turning it into a holy war. I’m not here to dunk on Tailwind, and I’m definitely not here to cosplay as the last defender of “real CSS”. Tailwind is an impressive piece of work.

But for the way I build things, it’s not my tool.

Tailwind’s actual superpower

Most arguments around Tailwind get stuck at the surface:

  • “Class soup is ugly”
  • “It’s basically inline styles”
  • “No, it’s faster”
  • “No, it destroys semantics”
  • “No, BEM is worse”

That debate is endless.

The real Tailwind pitch is simpler: constraints + speed.

Tailwind turns styling into selecting from a predefined design system (spacing scale, font scale, colors, shadows, radii). That’s why it feels so productive. You’re not inventing a new margin value every 5 minutes , you’re picking from a menu. Tailwind’s own docs explicitly frame it as “designing with constraints”, and pair that with variants for hover, focus, and responsive behaviour.

When Tailwind works, it feels like this:

  • You have a component in your head
  • You type the HTML
  • You sprinkle utilities until it looks right
  • You move on with your life

No naming. No context switching between files. No “where is this class defined”. No cascade surprises.

And if you’re working in a team (or inheriting a codebase), the consistency is a big deal. You can open any component and recognise the spacing and typography patterns instantly, because everyone is pulling from the same scale.

That’s a real benefit.

“Isn’t it just inline styles?” Not really.

This is worth addressing because it’s the most common misunderstanding.

Inline styles are limited. No pseudo-classes, no pseudo-elements, no media queries, no selector relationships, no tooling. Utility classes live in real stylesheets, so they can do everything CSS can do (including complex selectors, responsive behaviour, caching, processing, etc.).

So no, Tailwind is not style="".

It’s more like: “write your CSS once, pre-slice it into Lego bricks, then build UIs by snapping bricks together.”

The moment Tailwind clicks (and why people love it)

I’ve worked on Tailwind projects. I get the appeal.

There are three moments where Tailwind feels undeniably good:

1) You stop naming things

Naming is hard. Naming CSS classes is its own little tax.

Tailwind sidesteps that by making your “naming” be the composition itself. You don’t create .pricingCardFeatured and then hunt for it later. You just describe the styles right where they apply.

Even if you disagree with the conclusion, the problem is real: CSS and HTML are coupled whether you like it or not.

2) It makes boring layout work fast

Spacing, flex, grid, alignment, typography: Tailwind is extremely optimized for the 80 percent case.

It’s especially good at “dashboard UI” work: cards, nav bars, modals, forms, tables, list items, and all the slightly boring stuff every product needs.

3) It gives you a default design system

This is the underrated part.

People don’t just like Tailwind because it’s utilities. They like it because the defaults are tasteful and cohesive. You can move quickly without designing a spacing scale from scratch.

That “design tokens out of the box” feeling is the gateway drug.

The main trade-off Tailwind makes

Tailwind’s trade-off is not “long class attributes”.

That’s annoying, but it’s manageable.

The real trade-off is: you move a big chunk of your styling layer into your markup layer.

Sometimes that’s exactly what you want.

But it changes how the codebase feels.

And that’s where I start to drift away from Tailwind, because for me HTML readability is not optional. It’s where I think about:

  • structure
  • accessibility
  • content hierarchy
  • interactive states
  • layout relationships

When every element is carrying 15 to 40 styling decisions, the document stops reading like a document.

Yes, components help. Yes, you can extract patterns. Yes, you can use @apply (we’ll talk about that). But the default mode is still “style lives in the markup”.

Some people love that. I don’t.

Another trade-off

There’s one more Tailwind trade-off that doesn’t show up:

you’re not just adopting a set of utilities, you’re adopting a whole ecosystem.

Tailwind works because it’s a tightly designed package: a Domain-Specific Language, a compiler pipeline, a config-driven design system, a documentation culture, and a bunch of editor tooling that makes it pleasant.

That’s great… until it isn’t.

This week, a GitHub discussion around Tailwind’s documentation (about adding an llms.txt endpoint for LLM-friendly docs) unexpectedly highlighted the fragility of that setup. In that thread, Tailwind’s creator described docs traffic being down significantly since early 2023, revenue being down sharply even while the framework remains popular, and a major reduction in engineering headcount. (c.f. https://github.com/tailwindlabs/tailwindcss.com/pull/2388)

I’m not bringing this up to predict doom. Tailwind is widely used and not “going away tomorrow.”

I’m bringing it up because it clarifies the risk profile of adopting a framework-level styling layer:

  • If Tailwind’s development slows, you can’t just “swap it out”.
  • Your markup is full of Tailwind’s vocabulary.
  • And if you leaned into @apply, even your CSS is now Tailwind-dependent.

So the exit cost isn’t “rewrite some styles”. It’s rewrite markup, retrain habits, rebuild constraints, and reestablish a design system.

With native CSS, you can still make bad architecture choices , but at least you’re building on the platform’s most stable contract. If tomorrow you change approaches, your code doesn’t stop compiling because a tooling layer changed shape.

The practical pain points (from actually using it)

Here’s what bothered me in real projects, beyond vibes.

1) “Content gets buried”

When class lists get long, the actual content becomes harder to scan. This is a real maintenance cost: editing copy, reviewing diffs, reading templates, doing quick refactors.

If your workflow depends on “fold all classes” plugins, that’s a signal. Not necessarily a deal breaker, but a signal.

2) You start needing tooling to keep it readable

Tailwind is amazing with good tooling:

  • class autocomplete
  • sorting
  • linting
  • folding
  • component extraction patterns

Without that, it can feel like typing encrypted abbreviations.

Again: not fatal, just friction.

3) It nudges you toward breakpoint-heavy design

This one is subtle, and it depends on how you write CSS.

A really common Tailwind style is: sm:, md:, lg: everywhere.

That works, but it can also push you into “design at breakpoints” instead of building layouts that are fluid by default.

To be clear: Tailwind can do fluid layouts. But the path of least resistance often becomes breakpoint choreography.

4) The “escape hatch” problem

Tailwind is great until you hit something that doesn’t map nicely to utilities.

At that point you either:

  • write custom CSS anyway
  • use arbitrary values
  • create weird utility combos
  • reach for plugins
  • or accept something “close enough”

I don’t even mind mixing. The web is messy. But if I’m going to write CSS anyway, I start asking: why am I also maintaining a second styling language?

“But you can just use @apply”

A lot of Tailwind discussions end with:

“Just extract components.” or “Just use @apply.”

And you can. The issue is what you’re extracting into.

@apply is not CSS. It’s Tailwind syntax that requires Tailwind in your build pipeline. If you remove Tailwind, those styles break.

This is where Tailwind starts to feel less like “CSS helpers” and more like adopting a framework.

That’s fine. Frameworks are normal. I just prefer when my styling layer stays as close as possible to the platform.

My honest reason: I like CSS as a language

This is the part that gets misread as elitism, so let me say it plainly:

I don’t avoid Tailwind because I think Tailwind users are wrong.
I avoid Tailwind because I enjoy writing CSS, and modern CSS has become extremely capable.

I like:

  • custom properties as real design tokens
  • cascade layers to keep priorities predictable
  • container queries for component-level responsiveness
  • newer selectors and pseudo-classes
  • small utilities that I control
  • debugging styles in DevTools as actual CSS rules, not a long list of class mappings

Tailwind can coexist with modern CSS, but it’s still an abstraction layer. And when you already know the base language well, abstractions have to earn their place.

The part people really want: “Okay, what do you do instead?”

I try to steal Tailwind’s best idea (constraints) without adopting Tailwind’s whole approach.

Here’s the pattern:

1) Design tokens in CSS, not JS

Instead of a Tailwind config defining spacing and colors, I define tokens using custom properties:

:root {
  /* spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  /* colors */
  --bg: #f6f0e8;
  --text: #111;
  --accent: #1f6b55;
}

This gets me the “designing with constraints” benefit, but it stays native.

2) A tiny set of utilities that I actually use

I’m not against utilities. I’m against importing 10,000 of them by default.

So I’ll define a few:

.stack {
  display: grid;
  gap: var(--space-4);
}

.card {
  border: 2px solid var(--text);
  border-radius: 12px;
  padding: var(--space-6);
  background: var(--bg);
}

Now my HTML is readable again:

<section class="stack">
  <article class="card">
    <h2>Title</h2>
    <p>Text</p>
  </article>
  ....
</section>

3) Use CSS for the relationships Tailwind struggles with

Tailwind is great at styling an element. CSS is great at styling relationships.

Parent state, sibling relationships, component structure, “if this exists then style that”, fluid layouts, container-driven behaviour.

This is why I keep coming back to CSS. The language keeps getting better at exactly the kinds of things that used to be Tailwind’s sweet spot.

When Tailwind absolutely makes sense
If you read this as “Tailwind bad”, you missed the point.

Tailwind is a great choice when:

  • you have a product team and need consistent UI fast
  • you want onboarding to be easy (many devs know Tailwind now)
  • you want a default design system with strong guardrails
  • you build mostly inside component frameworks where markup readability matters less than component boundaries
  • you don’t want to spend time building and maintaining a CSS architecture

It can also be a solid bridge for devs who feel lost in CSS, because its docs + constraints teach styling patterns indirectly.

Why I still don’t use it
If I had to summarise my position in one sentence:
I want Tailwind’s constraints, but I want them in native CSS, with readable HTML, and without adopting a second styling Domain-Specific Language.

That’s it.

Tailwind solves real problems. It just solves them in a way that makes my code feel less like the web platform and more like a framework language.

If that trade-off is worth it for you, use it. Seriously.

If you’re on the fence, my suggestion is boring but honest: try both approaches on a real project, then pick the one that makes you move faster without hating your codebase three months later.

Support My Work

Feel free to leave your comments or questions.
If you found this article helpful, please share it with your peers.

If you want more content from me, you can go directly on my blog.
I also created a 7-day email course to learn more about modern CSS.

Happy coding!

2 Comments

0 votes
0 votes

More Posts

Starting out

Hopewell - Jan 17

Diagonal Background Pattern with TailwindCSS

Tony Edgal - Oct 20, 2025

Space vs. Gap — Tailwind CSS Explained

goldenekpendu - Oct 4, 2025

Stop Saying You're "Bad" at CSS. You're Forging a Superpower.

Habib - Nov 16, 2025

What I Learned Building Nature’s View — My First Responsive Website Without Frameworks

Hopewell - Jan 14
chevron_left