Skip to main content Accessibility Feedback

Tailwind is bad

Yes, this is a clickbait title, but it’s also (mostly) true.

If you’re not familiar with Tailwind (bless your heart), it’s a “utility-first CSS framework.”

I’ve written about my disdain for Tailwind before, but today I wanted to share an article from someone else that I think make my points better than I did. I also want to share what I think is a better path forward.

Let’s dig in!

Tailwind marketing and misinformation engine

The first article is “Tailwind marketing and misinformation engine” by Tero Piirainen.

In it, Tero documents how Adam Wathan took the seed of a good idea—reusable classes are good—and ran it to a harmful extreme: break every single property into it’s own class.

“The most reusable components are those with class names that are independent of the content.”

The sentence is from Nicolas Gallagher’s article about HTML semantics and front-end architecture. It was a turning point for Adam Wathan, the creator and frontman of Tailwind.

Here’s an example Tero provides of what Nicolas intended by by his sentence…

<nav class="uilist">
  <span class="uilist-item">
    ...
  </span>
</nav>

As Tero explains…

Instead of using a content-dependent class name like “news”, one should use a content-independent name like “uilist” or “uilist-item”.

That’s smart! In my work with clients, I often see the same UI used in many places with unique, content-specific class names.

Things like .news-list in one place, .comment-list in another, and .podcast-list somewhere else.

It makes your CSS harder to maintain and keep consistent. And while you can lean on tools like Sass to help a bit, things become much easier to manage when you have a single, abstracted class to covers all of these use-cases.

But as Tero notes…

But that’s not how Adam understood the sentence. Instead of moving towards more reusable class names, he introduced a custom grammar to inline styling rules directly to HTML:

<!-- "uilist" -->
<div class="
  sticky top-0 z-40 w-full backdrop-blur flex-none
  transition-colors duration-500 lg:z-50 lg:border-b
  bg-white/95 supports-backdrop-blur:bg-white/60
  dark:bg-transparent">

  <!-- "uilist-item" -->
  <span class="
    py-4 border-b border-slate-900/10 lg:px-8
    lg:border-0 dark:border-slate-300/10 px-4">
    ...
  </span>
</div>

This is and was my biggest gripe with Tailwind.

This approach is not better. It’s faster for prototyping, but it’s objectively worse for the long term maintenance of a site.

And while you may tell yourself that you’ll refactor it later, the reality is that for many, many projects, most developers don’t. This stuff is sticky and lives on forever.

Tero goes much deeper into the marketing bullshit, the vendor lock-in, and anti-CSS posturing of the framework.

I encourage you to go read the whole thing. It’s very good!

What would I recommend instead?

There’s no one right system.

I often find approaches like BEM and SMACSS too complex and rigid for my liking. But they’re not bad. They’re just not for me.

I’ve seen them used on projects. I’ve worked with them on projects. They work great, and produce CSS architecture that’s relatively easy to read, understand, and maintain over time.

My preferred approach, though, is something I call HUG CSS:

  1. Classless HTML for the core styles.
  2. Utility classes to nudge and tweak elements when needed.
  3. Group classes to simplify styling collections of elements that may have more complex needs.

It’s a bit like Andy Bell’s CUBE CSS, but slightly different naming conventions a bit fewer rules.

I’ve also used OOCSS by Nicole Sullivan in the past and enjoyed it.

My point is that there are many different approaches to CSS that respect the cascade (the C in CSS) and the nature of CSS, and produce code that’s nice to work with over time.

I don’t think Tailwind is one of them, and eventually, the tech debt it causes is going to be a big problem for the people who use it.