Why you’ll probably regret using Tailwind

- Posted in css development webdev - 9 comments

I see Tailwind being used by a lot of people. I totally get why it’s so popular. It looks great, the design choices are sensible, and you have copy-pastable code that reliably renders in the same way.

It nets you results quickly which feels nice and rewarding. I think Tailwind is a perfect framework for two kinds of situations:

  1. people who don’t want to learn CSS
  2. people who want a standardized look and want to copy paste from an external library (i.e. Tailwind UI)

I mostly wanted to shut up about Tailwind, and I hoped that people would realize it should be avoided after a while. But that doesn’t seem to be happening. So I think I have to fight the fight and talk about why you should not use Tailwind.

I have had a lot of thoughts about Tailwind, and most of these thoughts come from a place of being the code maintainer of a large scale web application codebase. So mind you, when I am talking about regret in the title, I am talking about a big web app context where your code is going to be maintained for years to come.

I am not talking about your Sunday side project. I am talking about real, big work® projects here. I am talking about maintaining something that has a lot of complexity that many people will work on for years to come.

You might be tasked with choosing a CSS architecture for that. And you might have heard about this framework called Tailwind. It’s popular right now and it seems to be the 2020 choice.

You look at the website and you see this nice framework that promises you that things are going to be cool in Tailwind land. And you might be thinking: cool, let’s try that.

I urge you to reconsider your options. I would advise you to not use it.

I really don’t know why Tailwind got so popular over the last year. I think it’s because it does a lot of things right. Surely if the authors can make $1.8M in preorders for Tailwind UI, something must be up. But at a fundamental level, I think its ideas are wrong and lead to problems, and I am going to explain why.

The main problem is that the kind of code you are creating with Tailwind, especially on large scale applications, is the kind of code that you’re going to have to swap out in a few years. Why? Because it’s unmaintainable at best. I will explain below.

An API on top of an API

Tailwind is a framework that heavily relies on utility classes. Utility classes are classes that typically do one single thing e.g. .flex makes a <div> have display: flex. Utility classes create an API on top of what is already a declarative API (CSS itself).

This can lead to problems when

  1. the original API (CSS itself) might change in the process, so now you have a mapping problem
  2. the mapping itself is not that great, and has bugs on its own

To dive into situation 1, let’s think about how maybe CSS grid will have an evolution in its syntax. Now you don’t just have to update your code, but also the in-between framework (Tailwind).

To dive into an example of situation 2: I was on a project a while ago where somebody mapped Flexbox to utility classes wrongly (i.e. the mapping contained obvious mistakes) and that code had made into production, was versioned, and we couldn’t change it. I thought that was very painful. 

Tailwind’s coding style creates its own problems, which you then have to solve

There’s also a deeper problem: because you are essentially recreating the CSS API with utility classes, you end up with a huge file. The current Tailwind utilities.css file is 1.8 megabyte and contains 80 000 lines of code.

Tailwind proponents will tell you that you can remove unused CSS with Tailwind’s tooling. And this is exactly the problem that I want to talk about: now you have to solve a problem that’s inherent to the framework, adding complexity to your project.

In the Routify and Svelte Discord channels I routinely see people struggling to get the toolchain around Tailwind working. So many people struggling with a toolchain that is totally unnecessary is a real problem. Removing code that shouldn’t be there in the first place should not be necessary.

Sidenote: I’m not against utility classes

Surely it feels nice to be able to quickly write flexbox things in a declarative way, and it has it’s uses. I’ve happily wrote declarative flexbox code in the past to quickly set up layouts. I am not against utility classes at all. It’s quite handy to set up layouts quickly with utility classes. I can also totally relate to the idea of designing in the browser with a set of utility classes.

When I do web app layouts we typically have a lot of different panes and panels going on in your app with varied logic about how they should resize. In a prototyping or an early app phase it’s super handy to not have to write a specific (BEM) component for everything and just build things out with utility classes.

But you have to be careful with utility classes. Because what you’re essentially doing is building a complication on top of CSS itself.

When you use a utility classes, for example declaring a flex utility on a div, you are doing it in HTML instead of declaring it in CSS this can quickly lead to unmaintainable code.

I use utility classes in one-off situations. Tailwind style code uses utility classes for everything. Tailwind lovers will now come back to me with arguments about @apply but this is just throwing more complexity on something that is already complex.

Adding complexity to complexity

Tailwind’s HTML code is fine for very simple projects (which will now have the drag of a whole toolchain to purge unused selectors but OK).

But let’s just imagine you have a bit of a bigger project, with a basic app layout that is working, with a few panels and a few buttons.

You now have to update the code for the next requirement – a bit of responsiveness. The UI has to change according to a breakpoint. So you add some Tailwind breakpoint classes to the thing you are making.

You realize your specific situation requires a very specific breakpoint. So you change your Tailwind config to add this breakpoint. You kind of realize it would be easier to just write the breakpoint directly in CSS, but you want to do things the Tailwind way.

If you would do things the regular CSS way, your media queries list basically lives in two worlds: the tailwind config and a local CSS file. And now you’ve lost your breakpoint structure. So you have to adhere to the Tailwind style.

And then let’s say you have a new situation: dark mode has to be supported. So suddenly your colors all have to change according to the dark mode logic. So now you add a bunch of classes to whatever you are creating again… you look at your button code and you see that the color of the button is directly in the HTML (gray-100). And the opposed color is actually called gray-900 in your design system. So now you add a class to the button for the dark situations. And you now realize that there are 7 variants of your button in your codebase and they all have different class combinations.

Now, with the previous responsive example, and this dark thing piled on top of it, you have a sorry mess.

And then you realize that some aspects of what you’re doing would be way easier to handle directly in (S)CSS, using contextual media queries and proper use of the cascade.

Maybe your web app is never going to see the complexity I am talking about. Maybe your apps are not responsive or they don’t support dark mode. But most of my work supports some form of skinnability, is as responsive as possible and needs to be maintained over several yerars.

Putting values on the same class level in HTML is basically destroying the cascade for you. So don’t do it.

Complexity overload and no proper way to manage it

The problem with Tailwind is that you end up with an overload of classes in your HTML and no way to manage these.

Tailwind’s creator says it’s not a problem because your code can essentially lives in deep presentational components and it can be abstracted away. But this kind of coding strategy leads to its own problems (e.g. prop drilling)

I’m especially wary of this situation where you have one button that has 6 HTML classes, and then another button that has 5 HTML classes, and another one that has 7 HTML classes. And they’re all different combinations. Now you have to do some serious digging through the codebase to try and bring everything in line, and you’re going to be comparing which of the three buttons have class overlap. Nobody is waiting for that task. And frankly, it’s probably just nog going to happen.

It’s code rot that’s going to happen as soon as you have a few people working on a project with moderate complexity.

I fear for a Tailwind future

I already see Tailwind popping up all the time in Javascript articles that have nothing to do with CSS. The authors use it because it’s a quick way to style their tutorial. In the process they’re halving the shelf life of whatever they’re writing, and dirtying up their examples with a boatload of HTML classes.

I fear for the general codebases of web apps when too many people decide that Tailwind is the way to go. Now, on the other hand, this means I’ll have an infinite stream of work to refactor bad codebases to better ones. So I guess I’m not complaining.

Just do yourself a favor and stop using Tailwind.

9 Comments

  • Windtail says:

    I do not agree. Tailwind establishes clear and concise design language among developers, designers and other parties. It also cuts down a lot of CSS that repeats over and over 8n project.

    I work on big ass project without tailwind and it’s a pain.

    To each his own man, no need to shout out that one tool or other is bad. You don’t like it, don’t use it. Let people decide what suits them best.

  • Bruno Stasse says:

    “If you don’t agree with someone, keep it to yourself”? Wow, this is some really bad reaction.

    For people to decide what technologies suit them best, they need to know their pros and cons, which is exactly what this article is presenting regarding Tailwind. It might deter people looking for a styling solution from using Tailwind because they realise it’s actually not the ideal solution, contrary to what other articles might say.

    Besides, constructive criticism like expressed here is what allow progress and improvement of our tools. Please instead of trying to shut it up, be thankful that people take the time to write stuff like that, and maybe reply to it with precise counter arguments?

  • Wan Ahmad Firas says:

    I have to do some digging to find your article. Most of the reviews out there seem too good to be true. I understand that any framework SHOULD have reasonable cons, ie when I shouldn’t use it. It’s like the OOP frenzy as if procedural programming is spaghetti coding, no exception.

    Thank you for being a level-headed skeptic. At least now, even if I am to use Tailwind CSS, I understand what’s insured and what’s not. It’s funny when I searched ‘how to migrate from tailwind css’ it served the opposite. It feels like converting to Islam, no way out, just in.XD

  • Tommy says:

    I completely agree. Writing all of these classes all over the place. When it comes time (and it will) someone is going to have a hard time rewriting all of this CSS. There’s a reason we don’t write inline styles 😂

  • Markus says:

    Thank you very much for this article! It does indeed address a number of points that I had not thought of in this consequence.

    I’m currently facing the task of setting up a larger project for a client and have been thinking about integrating Tailwind CSS into my workflow. After reading the documentation twice and watching all the official tutorial videos, I was really tempted to jump on this bandwagon. Especially the code reduction with PurgeCSS was very tempting. Besides, some users claim that they have tripled their coding speed ;-)

    But…since I am someone who has always tried to follow best practices, web and coding standards, I was deterred from the beginning by the irritating HTML code that comes with the use of Tailwind CSS. Now, your arguments are very valid and I don’t have to think about it much longer.

    I am very grateful for this article, which opened my eyes and brought me back to the basics of good code.Thank you for stopping me from making a potentially serious mistake.

  • Interesting article. I think it’s important to abstract Tailwind CSS and Tailwind UI, two different things entirely. This post sounds like you’ve never actually used Tailwind in a project. As someone who has used Tailwind on two projects now (one big, one small), you don’t have to use Tailwind’s HTML code (and in fact I’d argue you wouldn’t want to and is probably one reason why they spun up Tailwind UI). Additionally, even if W3C deprecates CSS referenced by Tailwind, browsers rarely completely remove support for it. So, I’m having a hard time understanding exactly what your issues are with Tailwind other than you just don’t like it.

  • Thanks.

    I feel like the creator put pretty solid counter-arguments out years ago, that are a little more detailed and less … dismissive.

    https://adamwathan.me/css-utility-classes-and-separation-of-concerns/

  • Jonathan Sparvath says:

    Tailwind has many things going for it (especially in the idea of being a design system implementation), but I think it’s riding the hype train quite hard right now so I appreciate your article with critical thoughts about it!

  • Jignesh R says:

    Hi Johan,

    I have just started learning Tailwindscs few days back and before jumping into it I started analysis of it’s real benifits and some Pros and Cons. At the initial level it seems very promising to me but after reading your articles I am not able to take a decision that how utility-first CSS framework can be a big trouble.

    https://laravel-news.com/11-benefits-of-tailwind-css

    After reading your article I have found following few scenarios where Tailwindcss is not fitting well as per your opinion.
    1) When There is a large scale project or a complex project
    2) When there is a time for major HTML/CSS code change/rewrite in the project.
    3) when there is a change in CSS API.

    From the above given points I am agree with the 3rd point, but not clear about the other two points because now a days most of the projects are building upon JavaScript frameworks like Angular, React or View.js. In this frameworks we use componenet based structure, for example cards. That can be a single component and it can be used multiple times in the large projects. I think this can be managible even if we use such CSS frameworks. if there is complete HTML change then we can easily create new component variation also.

    Also if possible, can you please provide some simple code examples to elaborate your thoughts in details on following points. That will be helpful to understand the drawbacks of this framework properly.
    1) “Tailwind’s coding style creates its own problems, which you then have to solve”
    2) “I’m especially wary of this situation where you have one button that has 6 HTML classes, and then another button that has 5 HTML classes, and another one that has 7 HTML classes. And they’re all different combinations. Now you have to do some serious digging through the codebase to try and bring everything in line, and you’re going to be comparing which of the three buttons have class overlap.”

    your further guidance will be very helpfull for the developers like me.

    Thanks,
    Jignesh R

Leave a Reply

Your email address will not be published. Required fields are marked *