A Tailwind Post

A discussion about Tailwind always gets heated. It's one of those polarising topics in web dev. Tabs vs. spaces. Utilities vs. semantic CSS, decoupling vs. tight coupling, etc.

I made a mistake and recommended an article today about negative aspects of Tailwind without providing further context why I agree with the article.

Here are a few roughly collected points on my/our own quest with Tailwind. We have been playing with it for at least three years at Kirby. We built the latest version of our website with the same utility naming system and we also made massive experiments in the background to move our admin panel to Tailwind or better a Tailwind-like system.

Disclaimer: If Tailwind works for you and makes you more productive, this is absolutely great. Sharing a negative article about it does not automatically invalidate your choices. We had a lot of fun with it as well and I think it's important to move beyond black-and-white thinking here.

It's also important to note that our experience with Tailwind's utility-first approach had completely different outcomes for two completely different projects. The overall experience for our website is still mostly positive after 2+ years – although I would move to a different system if we relaunch it one day. The experience for our admin panel is that it didn't work at all and we abandoned the plan to use it there.

Let's start with a few things where Tailwind excells in my opinion and why I think it works for so many people:

  1. The docs are great and extensive and it mostly takes away the need to have your own docs for your team or for new freelancers for example. You can simply send everyone to the Tailwind docs to onboard new folks. Someone who already knows Tailwind can start with an existing Tailwind project right away. Coming back to a project instantly feels familiar. Those are huge benefits.

  2. The naming conventions are mostly great and consistent. That's basically the Prettier effect. You don't have to make up your mind as a team or individual. You can simply use what's already there.

  3. The basic design decisions are pretty decent. It has the same Bootstrap smell to it if you don't adjust the defaults, but it is very easy to do so and it can feel very custom quickly. Again, this is a huge win for a team. Especially if you mostly work with developers.

  4. It definitely feels very productive and mind-blowing at first to apply your styles directly in HTML. There is a certain charm to that, which I totally get. Combine it with something like Alpine or HTMX and you basically stay in your HTML code forever.

  5. I get why the tight coupling of HTML and CSS feels like you are more in control compared to the scary cascade. Especially for those who are not super deep into CSS. It can definitely help to reduce unwanted side effects and again this is a big plus when you work with multiple people.

Now to the parts that really didn't work for me personally:

  1. The marketing is off-putting at best. I know that this is not a valid reason against the technical parts of the framework itself. But their way to discredit years of CSS knowledge and pretend that Tailwind is the new standard is just plain wrong. I'm sorry but there are just too many fantastic people in this industry who put their hearts and souls into foundational work for decades in this field and you can't kick them in the ass like that. It's just disrespectful.

  2. Tailwind moved from a utility system to a full-blown framework and as such it is caught in the trap to solve everything. Their claim to be the better way to write CSS leads to a point where they need to replicate any CSS feature or otherwise they would fail their own claim. But this leads to a monsterous system that just keeps growing in ways that feel weird at best. While media queries, hover states or pseudo-elements all have solutions in Tailwind, they all feel like they have been shoehorned into utility classes to fulfill their own claim. They don't seem to me like actually well-designed choices that are better than writing native CSS. But that's just me.

  3. For our website, I keep finding myself writing custom grids, media queries and other stuff in native CSS on various occassions where Tailwind just doesn't cut it and it always feels like a weird hybrid world that isn't right. When I start adding custom CSS rules, what do I include? Just the custom stuff that does not feel appropriate for utilities? Do I still use the Tailwind utilities for anything else or do I mix in a few of those rules in there as well while I'm already at it? Whenever I discussed this with other Tailwind folks, it turned out that everybody handles this a bit differently. There you are in a place where you need to know CSS and Tailwind well enough to make good decisions for the future, but the mixture of both worlds just leaves you with more questions than answers. You are also back at the same old questions. How do you keep this maintainable in the future? How do you document this well for other folks? What's the team-wide standard for custom CSS like that? Etc.

  4. Tailwind is completely dependent on template engines or components. If you cannot make use of partials/snippets, loops and other ways to DRY up your HTML, Tailwind is a mess to write and maintain. Unless, you use @apply and break their own best-practices. They honestly even explain how to use multi-cursors in your editor to get around editing multiple elements with the same utilities in their docs. Even if a project can rely on template engine features or a component system, this is a clear weakness in my opinion. I've ran into multiple points where it was just tedious work to refactor utilities in multiple elements after a new design choice and where I wished that I could just switch to plain CSS. It pushes you to move way too many bits and pieces of HTML into their own partial or component, even when it is sometimes just too much. They talk about premature abstraction in various places when it comes to "traditional" CSS. But in my experience you often end up with premature abstraction in HTML with Tailwind.

  5. When you work with client-side components, you basically write CSS-in-JS with Tailwind. By adding all those utilities to your markup, you blow up your JS bundle significantly. We tried that for the Kirby Panel and the size difference was huge. With every additional feature, the problem would have gotten even worse. It was in fact so bad that it was the main reason to abandon the idea completely. I think at this point, performance experts agree that more JS is always a bad idea, no matter how far you optimize and compress it.

Here are a few more specific parts for Kirby:

  1. The Kirby Panel has a plugin system. Our users can build their own fields, UI elements and components to extend and customize the admin panel for their clients. We basically provide them with our own UI framework for that. We found that this introduced a major issue: We cannot use purge to remove unused utilities. The plugins come after our build step. This means that we would have to guess which utilities would be needed by most plugins and then provide them as-is. How would we even make that choice? Do we go with all utilities and send a gigantic CSS file to the users? Would we provide a subset and add utilities on demand? How would we decide if a new utility is useful for a lot of use-cases? If we keep adding new ones, do we end up with a similiarly gigantic CSS file one day?

  2. We have a lot of users, who add their own custom CSS file to the Panel to apply the branding of their clients or make their own tweaks. With Tailwind, you lose semantic selectors and thus the tweakability with custom stylesheets is basically gone. In theory, we could still add them just as a bonus, but this felt completely wrong again.

  3. To provide a reliable UI framework that would hopefully be lasting for a long time left us with the question if we should maybe mix traditional CSS with some Tailwind utilities. But how would we decide where to use what? Would we use utilities just for margins, grids and other stuff that would help to arrange components and then use traditional CSS for the components? Where do we draw the line? How do we document all that for our plugin developers to make sure that they also use it in the same way?

In the end, the benefits of Tailwind kept vanishing, to a point where we still see a useful, but also very tiny subset of utilities that could work for us. Does this leave the Tailwind fans unhappy who want all of the Tailwind power if we add them? Probably. Would it really solve some pressing additional issues? Hardly.

After all this time experimenting and working with utilities, I see their appeal in quite a few places. But I also really see where they hit some hard limits.

Tailwind isn't the hammer for all nails. It definitely isn't the hammer for our panel. We gave it a chance though and learned a few really important lessons. We also got inspired in a few really great ways. But I keep coming back to one important part that made me recommend the article, I mentioned earlier. Utilities aren't a new thing. They have been invented and explored long before Tailwind came along.

I think we can only grow as developers if we play around with multiple ways to achieve our goals and then decide what works best. It is easy to get excited about a certain way, but most of the times it really takes a few months or maybe even years to see the real pain points. CSS has become so so good in the last years and I just love to try to keep up with all the new and great ways to create interfaces with it. I don't really want to bind myself to one way here. But that's my point of view. For a lot of freelancers and agencies, Tailwind solves some really valid problems and that's valuable. But it's imho important to not lose sight of other options and it's especially important to keep in mind that very smart people have built a fantastic way to style HTML and many other very smart people have been exploring ways for decades how to use it properly and they deserve all our respect.