Design Systems Hot Takes

Defending Tailwind

7 min read

If you told me 2 years ago that I was going to write this post, I would have scoffed at the thought. But after a deeper think related to the current state of affairs when it comes to development today, I get it.

The thumbnail used to promote the Code & Pixels podcast says it all.

Me, with the text "I don't like Tailwind"

I’m very confident that there will never be a time where I will consciously choose Tailwind to style my work. I have too much experience working with CSS to need it. But it wasn’t until a few years ago that I had a wake up call about the gap between my skill and the skills of over developers around me.

Hiring greatness

When I worked for Compass, I was a part of practically every frontend engineering hire starting at my 3rd week of employment there. I’ve attributed it to the person asking me the CSS question disclosing that it took him an entire week to do what I did in 20 minutes during the interview. He had nothing left to ask after blowing away any doubt I’d be able to do the job.

In that time, we created what I can only describe as the best frontend engineering team I’ll probably ever see. Every person with the title frontend engineer was solid in CSS. No problems centering elements, boxes aligned nicely, and responsive design was at the top of everyone’s mind. I was still involved to kickstart newer projects with complex layout needs. The use of CSS grid on the listing page is still largely of my doing, now over 6 years old.

I got a rude awakening when leaving Compass, when new my senior director told me that I was an outlier. I didn’t know how true that was until more recently.

More important

What I’m realizing now is there’s more of a focus on just getting it done. An engineer gets a design thrown over the fence and their job is to “just do it”. The organization wants to get it done as quickly as possible. This means there’s a lack of care in things that are deprioritized. When the majority of full stack engineers are trying to wrangle CSS, it isn’t important to them how to organize that CSS. They just want to set this color on that element and move on to more important things.

That’s where Tailwind comes in.

What’s “good”

There’s two “good” things about Tailwind which I’ll address separately in paragraphs below:

  1. You don’t need to think of a name for the thing that needs styles.
  2. There’s no context switching between the file where you write styles to the file where those styles are being applied.

I can definitely agree that naming something .wrapper is awful. It gives no context to where it is or what it is doing. It’s often a challenge to think of a good name to provide a hook for these styles. Especially styles that are being used once and in a project that might have multiple teams trying to name their .card and having things conflict. Going the small step of namespacing the class names manually is too much forethought, so we’d rather complain that CSS is broken.

Should we assume that a good engineer would have good naming skills? We could, but in most facets of programming you don’t need to because of scope. There’s countless main() calls out there. The feature of CSS being global causes headaches for folks looking for scoped styles. Overrides bleed into unintended places and it becomes a big hassle in the way of more important work.

This is a reason why libraries like Styled Components got so popular. The component you created in React was created from declaring styles.

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: #BF4F74;
`;

Boom! You’ve created a component and added the styles in one shot. And because of the hashed classes underneath, conflicts are avoided. I actually agree with this approach. This is a component-first way of thinking. The reuse is in the components, not in the styles. It’s a lot of the reason why I adopted Shadow DOM early on. Tailwind does this too, just with more native syntax in HTML.

This makes way into the second point of context switching. Historically, you’d have to create something in your HTML structure for you to then hook into with CSS styles in a separate file. You’d have to remember what that name was in order to style it. On top of this, you’d also have to switch to the rendered result to see what the effect was. There’s a lot of screens to switch in between just to see what’s happening. It makes a lot of sense to reduce that by writing everything in a single file.

I have empathy for this since recently writing Astro components. All the code for an Astro component is in a single .astro file. I’ve noticed that I tend to write a few lines of HTML, maybe even less of JS, but the large majority of the file is CSS. It’s clear to me that much of the time spent coding in frameworks is CSS and engineers are going to want to reduce that time.

With Tailwind, a person can write the HTML and CSS in one shot. Is it a lot of HTML now? Most likely, but we can tuck that away in the code editor. The organization of styles is wired to the organization of the HTML. It’s a two-for-one special and that must be better, right?

What’s bad

Responsibility and scale.

The naming convention that Tailwind uses forces the engineer to be in charge of when a CSS value gets applied. If you want to have a theming system that can change between different values, what color-red-500 means can’t change, it must continue to be a red. It’s entirely possible that the color-red-500 is not appropriate for the new context, and some engineer must go in and add new logic with some new class name.

I don’t know about other folks, but I don’t want my role to be reduced to responding to every call for a new cosmetic change in the interface. Adding new logic to juggle these classes. I want the folks who ask for the change to do it themselves. Tailwind’s naming convention won’t let you do that. Maybe this is job security, but its also looming technical debt.

If you did want to have flexibility with some class naming convention, classes would need to be named semantically. As in, what the purpose of the thing is, not how it is styled since that could change over time. The purpose of the thing doesn’t change. Unfortunately, creating custom names is precisely what Tailwind recommends to avoid.

Need a small utility to nudge something? That’s a deeper problem with either the design or the engineering of the component. That should be addressed with revisiting the composition, not by adding a utility to pummel the style into submission. But we don’t have time to discuss that. Add the utility, it’s what they are there for. We’ll fix it later never.

The fact is folks who use Tailwind aren’t thinking about responsibility and scale in this way. They are thinking how can I push this out as fast as possible. And Tailwind is great for that. Educating on best practices is always hard. In the year 2025, I still see people struggling with responsive design techniques. So setting color-red-500 permanently isn’t going away tomorrow and the marketing of Tailwind is going to make that exceptionally difficult. We can hope that folks realize this sooner rather than later.

But I guess you could just have AI rewrite it now so no problemo?

Avoiding tokens