loading...

Tailwind is just a mess

rtivital profile image Vitaly Rtishchev ・2 min read

Let's look at Tailwind examples and discuss why would anyone can choose it over traditional css modules or css-in-js solution.

Example from the documentation with regular css:

<div class="chat-notification">
  <div class="chat-notification-logo-wrapper">
    <img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="chat-notification-content">
    <h4 class="chat-notification-title">ChitChat</h4>
    <p class="chat-notification-message">You have a new message!</p>
  </div>
</div>

<style>
  .chat-notification {
    display: flex;
    max-width: 24rem;
    margin: 0 auto;
    padding: 1.5rem;
    border-radius: 0.5rem;
    background-color: #fff;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
  }
  .chat-notification-logo-wrapper {
    flex-shrink: 0;
  }
  .chat-notification-logo {
    height: 3rem;
    width: 3rem;
  }
  .chat-notification-content {
    margin-left: 1.5rem;
    padding-top: 0.25rem;
  }
  .chat-notification-title {
    color: #1a202c;
    font-size: 1.25rem;
    line-height: 1.25;
  }
  .chat-notification-message {
    color: #718096;
    font-size: 1rem;
    line-height: 1.5;
  }
</style>

If you were asked to edit this code, it would be clear to you that:

  • you work with chat notification
  • it includes title, content, image and message

You will have this information event with only one css file.

With css modules we can achieve better structure and maintain elements meaning:

<div class="chat-notification">
  <div class="logo-wrapper">
    <img class="logo" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="content">
    <h4 class="title">ChitChat</h4>
    <p class="message">You have a new message!</p>
  </div>
</div>

<style>
  .chat-notification {
    display: flex;
    max-width: 24rem;
    margin: 0 auto;
    padding: 1.5rem;
    border-radius: 0.5rem;
    background-color: #fff;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
  }
  .logo-wrapper {
    flex-shrink: 0;
  }
  .logo {
    height: 3rem;
    width: 3rem;
  }
  .content {
    margin-left: 1.5rem;
    padding-top: 0.25rem;
  }
  .title {
    color: #1a202c;
    font-size: 1.25rem;
    line-height: 1.25;
  }
  .message {
    color: #718096;
    font-size: 1rem;
    line-height: 1.5;
  }
</style>

Now let's look at tailwind:

<div class="max-w-sm mx-auto flex p-6 bg-white rounded-lg shadow-xl">
  <div class="flex-shrink-0">
    <img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div class="ml-6 pt-1">
    <h4 class="text-xl text-gray-900 leading-tight">ChitChat</h4>
    <p class="text-base text-gray-600 leading-normal">You have a new message!</p>
  </div>
</div>

What are we working on? Well it is something largely rounded with box-shadow, maybe some card?

With all this utility classes elements lost all meaning and are now just a markup. It is impossible to tell what purpose this code serves.

So if you use tailwind, please share why you choose it over css modules or css-in-js and how you are able to maintain the mess that gets produced with all this utility classes.

Discussion

pic
Editor guide
Collapse
eddieaich profile image
Eddie Aich

I personally agree with Vitaly, but you should do what works best for you. I like the semantic approach and you could roll your own Tailwind that is more semantic if you want. Ex:

.button {
  border-radius: 5px;
  padding: 5px 10px;
  display: inline-block;
}

.button.blue {
  background-color: #3366CC;
}

.button.small {
  font-size: 0.8em;
}
<span class="button blue small">Submit</span>

Edit: Added due to comments below - an accessible button while still using a span tag

<span aria-label="Submit" class="button blue small" role="button" tabindex="0">Submit</span>
Collapse
rtivital profile image
Vitaly Rtishchev Author

Please, do not use span for buttons 😢

Collapse
eddieaich profile image
Eddie Aich

Do what works for you. span works for me in many cases, or button, or i.

Collapse
markohologram profile image
Marko A

But in this example you explicitly tie .blue and .small to button, without making it obvious at first while looking at HTML.

I can read this HTML and think to myself "Hmm, is this .blue color: blue, is it a class that I can apply anywhere I want, is this .small font-size, or maybe height? And so on...".. Then I go and use .blue somewhere else and see that it doesn't work and then I go check CSS source and see that it's only tied to .button. Then you go make a card component and want that same background color and you create .card.blue and duplicate the same background color. Now you have a class .blue that seems like it's something generic, but it's actually really tied to some elements you have and you also increase specificity by using .class.class selector for no reason.

This isn't really "rolling your own Tailwind" because you didn't make these classes generic, you just coupled them with this single element and they are not usable outside of it.

I haven't really used Tailwind that much, but purpose of it is to give you these single purpose classes that you combine (compose) into something that you want. Yeah, it might not seem clear what you are looking at when looking at HTML at first because of the learning curve and overhead of remembering what all those classes do, but neither is just

<button class="btn blue small">Allegedly Small Blue Button</button>

By using Tailwind, you only use what you need and don't repeat css properties multiple times for no reason

.button.blue {
  background-color: #3366CC;
}

.card.blue {
  background-color: #3366CC;
}

.avatar.blue {
  background-color: #3366CC;
}

...

And after using a build step, you strip out all unnecessary CSS and you are only left with what you truly used, without repeating stuff, leading to a smaller more optimized bundle.

It's also easier for browsers to apply a single class to 50 elements, instead of those 50 elements being targeted by many different classes that all apply mostly the same properties. You also avoid higher specificity because you don't combine .class.class selectors.

As I've mentioned, I personally don't use Tailwind, but I still like some general purpose classes, especially for padding/margin. It makes building UI easier, you make spacing consistent and you don't repeat yourself writing margin-bottom: YYpx a bunch of times for multiple elements.

I haven't yet switched fully to utility first, but I do see the benefits of it. Personally, I combine BEM with a healthy dose of utility classes. I do partially like utility approach because it allows you to iterate fast and adapt design as it might change. It also allows me to prototype faster. We've all had designers change stuff on us quickly and maybe "mess" things up for us in our "perfectly crafted CSS", but when using utility classes I can easily overcome it and all of a sudden those "sudden changes" in UI design don't really stress me that much.

Collapse
eddieaich profile image
Eddie Aich

In your example, we would simply untie .blue from .button in order to use it more generically.

.blue {
  background-color: #3366CC;
}

or you get a bit more detailed:

.bg-md-blue {
  background-color: #3366CC;
}

.bg-lt-blue {
  background-color: #D6EBF2;
}

The point is, it's up to you. You get full control of semantics and how generic you want it to be.

Collapse
joshuaamaju profile image
Joshua

I'd like to know understand what circumstances you'd use a span instead of a button.

Collapse
eddieaich profile image
Eddie Aich

Sure, here is a JSFiddle to illustrate my point - jsfiddle.net/aichforhuman/u1gtqcsv/

Why does this svg not display when inserted inside a button element?

<!-- sad button -->
<button>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M0 15.878 l12-11.878 12 11.878-4 4.122-8-8-8 8-4-4.122z"></path>
  </svg>
</button>
Thread Thread
rurickdev profile image
Rurick Maqueo Poisot

Well, if you add the same "btn" class to your button it works, I don't see why you will skip the HTML5 semantic and make your code harder to read just because "it works"

proof

Thread Thread
joshuaamaju profile image
Joshua

I guess this is just an issue with you not understanding html and css properly. Not trying to be rude here.

Collapse
collingskenny profile image
Kenny Collings

I always thought the point of tailwind was to write less css. It's a replacement for bootstrap and other similar libraries (which also suffer from the issues you point out).

Collapse
andi23rosca profile image
Andi Rosca

Apart from what other people have said, I want to say that in my opinion it is not CSS's purpose to instill semantic meaning to HTML.
The purpose of CSS is to style stuff, and for me tailwind is a much more painless way to write and to read CSS. I can infer at a glance how something will look like just by reading the classes.

And generally CSS and HTML feel very coupled to me, gone are the days when a website is mostly just data and CSS is just specifying a few things to make the data more tidy. For better or for worse websites have more complex design needs, and you sometimes need to write extra html tags just to make a certain design possible. So to me they are very intertwined and makes sense to keep them in the same "place".

Semantic meaning lost from using utility classes can be gained back either with attributes such as data-chat-notification or when using a component based library like React, Vue etc. through component names.

There is also the @apply directive for when you need to create reusable styles like cards, notifications, sections, etc. So tailwind gives you best of both worlds imo.

Collapse
gktim profile image
gkTim

I like tailwind because I don’t need to switch files so often and styles are written pretty fast when you get used to it. With a plug-in you have also autocompletion in your ide . With small UI components in your JS framework you know what it should do. I don’t get the point of css-in-js but this is another story XD

Collapse
starptech profile image
Dustin Deus

You didn't understand the idea of a utility framework like tailwind. It doesn't replace CSS-modules, CSS-in-JS, BEM, or any other methodology. Over time it's a blessing to work with because you write much fewer custom selectors. This results in a standardized way of how base styles (font, grid, margin, padding, color) are applied to your components. This makes your components more readable and maintainable.

You code can also be written as:

<style>
  .chat-notification {
    @apply font-bold py-2 px-4 rounded;
    // .......
  }
  .logo-wrapper {
    @apply flex-shrink-0;
  }
  // ........

  // component specific code
  .logo {
    height: 3rem;
    width: 3rem;
  }
</style>
Enter fullscreen mode Exit fullscreen mode
Collapse
fmctaggart profile image
Fraser McTaggart

I really like tailwind! I love that I can swap between projects and change things quickly without having to remember what I named things. Or spend time thinking about what to name things. I use single file components so if something is reused, it is put into a component.

A couple of things that also make me like tailwind: It's faster than writing CSS from scratch. Great padding, margin, size, breakpoints and font classes that scale correctly. It means it's easier to keep a design consistent. I don't want to have to think about these things for every application I make.

As for other CSS frameworks. I've used foundation and bulma for a few projects. I felt like I was fighting against the defaults to ensure the site matched the design.

The other reason that I enjoy using tailwind is that I have a figma template with all of the tailwind defaults. This means I can design and translate this into code with minimal fuss and time.

But to each their own. Whatever works for you and your team. I agree that it can feel messy, but clients care about the result, not the source code!

Collapse
johnkazer profile image
John Kazer

But isn't the original question about readability and understandability for others? Is fine for you to write quick code now but doesn't help anyone else now or later?

Collapse
fmctaggart profile image
Fraser McTaggart

One of the points I mentioned was that is was easy to swap between projects that has tailwind, as you don't have to figure out class names etc. If someone has worked with tailwind on one project, then they can easily use it any other tailwind projects. I'd argue it is easier than bootstrap or foundation, because the utility classes are fairly close to CSS property names. However the classes have inbuilt spacing etc.
I found it quicker to learn and much easier to understand others code when editing. That is one of the first things I mentioned and one of the main reasons I use it!

Thread Thread
johnkazer profile image
John Kazer

Fair enough, although I guess is a bit like using Vue vs react, not everyone knows how one or the other works. Personally I'm still working up to applying tailwind in the functional elm-like framework hyperapp.

Collapse
pablopuga profile image
Pablo Puga

Just to add my two cents here, the Tailwind documentation gives some examples about how to extract and reutilize components ( Extracting CSS components with @apply) so you can have both styles of using CSS.

Collapse
ferueda profile image
Felipe Rueda

I used tailwind for a project a couple of months ago and didn't like it tbh. What's interesting though, is that for the project after that one, I used vanilla CSS and found myself constantly wanting to go back to tailwind and wishing I had those custom classes already set up for me instead of doing it myself from zero. Go figure 🤷‍♂️

Collapse
tao profile image
Lewis Lloyd

with all due respect, this sounds like a misunderstanding of CSS frameworks, and not of Tailwind itself.

i came to this conclusion based on two things:

  1. your opinion that "utility classes elements lose all meaning" is just a case of what you're used to. plenty of devs are the other way around. some will understand both.

  2. frameworks are an opinionated implementation of frontend styling. you're not meant to have full control at the low level.

CSS frameworks are used to abstract basic concepts as utilities. everything from rounded corners to breakpoints is ready for you, allowing you to implement complex design in a safe and maintainable manner.

see these points from the Tailwind docs for some more answers:

dev-to-uploads.s3.amazonaws.com/i/...

dev-to-uploads.s3.amazonaws.com/i/...

Collapse
rtivital profile image
Vitaly Rtishchev Author
  1. There is no logic at this point. If you can understand what styles are applied to an element does not mean that you can understand what this element is. This is the main concern with tailwind.
  2. I've never mentioned that we lack control or anything like that.

When you get more experience with larger projects you will see that maintaining mess like tailwind output does not worth the effort on the long run. It has only one use case – you write styles once and then never return to them.

haynajjar profile image
haynajjar

when add role=button attribute to a html tag (div ,span,a...) you will solve the accessibility problem

Thread Thread
christianblos profile image
Christian Blos

"Do what works for you"
Can you explain why button or a doesn't work for you? Or if it works as well, why you choose span over button? Just interested what's the reason

Thread Thread
eddieaich profile image
Eddie Aich

Sure, here is a JSFiddle to illustrate my point - jsfiddle.net/aichforhuman/u1gtqcsv/

Why does this svg not display when inserted inside a button element?

<!-- sad button -->
<button>
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M0 15.878 l12-11.878 12 11.878-4 4.122-8-8-8 8-4-4.122z"></path>
  </svg>
</button>
Thread Thread
christianblos profile image
Christian Blos

just give the button the btn class in your jsfiddle and it works.

<button class="btn">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M0 15.878 l12-11.878 12 11.878-4 4.122-8-8-8 8-4-4.122z"></path>
  </svg>
</button>
.btn {
  display: inline-block;
  width: 48px;
  height: 48px;
  cursor: pointer;
  user-select: none;
}
Collapse
diamondback15 profile image
diamondback15

That's a great explanation. TailwindCss (w/o @apply) is shortcut that can hurt you on the long run.

I'd go a but further with Components

<S.ChatNotification>
<S.CNLogoWrapper>
<S.CNLogo src='...'>
<S.CNContent>...
...

I use classes or component names as a way to comment and structure my code. Tailwind is great to code as quick as possible and in a hackathon

Collapse
abeidahmed profile image
Abeid Ahmed

I understand your point that with css classes, you know what you are really working on, in your example it is the chat notification component. But say that you have a similar component called login notification component with similar styles, then would you repeat the classes or would you make another css class with .login-notification? If you repeat the classes then it will have the same problem that you mentioned in your article.
What I am trying to say is that tailwind solves this problem. You do not have to be specific with your classes, just use data- attributes if you want to identify something.

Collapse
rtivital profile image
Vitaly Rtishchev Author

name collisions already solved with css modules, you can make as many .login-notification classes as you want

Collapse
leastbad profile image
leastbad

Bootstrap 4 CSS + Bootstrap 5 JS = amazing

It has the utility classes I have muscle memory for, flexbox support, sane, semantic components that are highly familiar to most developers, and a massive ecosystem of themes and plugins for me to draw upon, because I'm a developer with really embarrassing design skills.

That said, I'm always a little shocked when I see designer-minded folks who appear to have no problem with throwing 25 classes on their elements. There was a time when people took pride in making their HTML markup beautiful.

Collapse
victorandcode profile image
Victor Cordova

It's true that you lose some semantics when using a utils-based approach like with tailwind. However, this can be compensated with components. A large portion of dynamic webpages uses UI libraries like react so this could be moved to a component and it would be easier to understand where we are.

Collapse
rtivital profile image
Vitaly Rtishchev Author

Well for me it's even worse, as meaningful components are usually at lease twice as large as an example. And you get all this gibberish in classnames. Combine this with some developers that prefer div over any other tag and you get a very nasty unreadable combo with any ui library.

Collapse
johnkazer profile image
John Kazer

I still don't really get the need for CSS at all, other than historical implementation decisions from a different time. So my intuition is that there isn't a perfect solution to using CSS, probably we would have to create a new authoring system that doesn't need it.

Collapse
fmctaggart profile image
Fraser McTaggart

You don't get the need for CSS? How do you make your website's look nice? I'd be interested to see an example..

Collapse
johnkazer profile image
John Kazer

Well obviously you need CSS now, but what I mean is that if you designed a system for creating modern sites and apps from scratch you probably wouldn't implement CSS. So debating the optimum approach to using CSS as we have it is a bit redundant because there isn't an optimum. It's doing a job it wasn't really designed for.

Collapse
dance2die profile image
Sung M. Kim

Maintainability concerns at the bottom of your linked seems to address it a bit.

I'd actually like "Explanation" (Reference: documentation.divio.com/#about-the...) on how to make it more maintainable than the tiny "Maintainability" section though.

Collapse
juanfrank77 profile image
Juan F Gonzalez

I totally agree with this, Tailwind just makes things look like a mess. Fortunately, their creators agree that it can be off-putting if one comes from a different UI framework and is not used to "utility classes".

Still I don't see it being useful in large scale projects for these reason. It might be cool if people use for their personal projects or for quick prototyping other than that... well...

Collapse
applicafroguy profile image
Sivuyile Magutywa

It is very useful, in fact, your project will be much maintainable. some of my projects have less than 100 lines of CSS code and it's actually easy to add semantic if you want to. you have to understand tailwind is a utility, not a framework.

Collapse
joshuaamaju profile image
Joshua

I raised the same issue in a tweet recently. It's crazy. I was like, what the hell does mr-* mean. Although now I do know what it means. But seeing a bunch of classes that were not clear at first glance, was disturbing.

Collapse
jasonfritsche profile image
Jason Fritsche

I thought I was just old and set in my ways by not preferring Tailwind over regular css. Thanks Vitaly!

Collapse
gsarig profile image
Giorgos Sarigiannidis

While I understand why someone would want to use a CSS framework, I never managed to end up using one and after a few projects with Bootstrap, I decided that plain CSS with a preprocessor is what suits me best.

The messy HTML, the difficulty to use it on some more complex designs, and a large amount of unused code were my main issues. The most important of all, though, is that I realized that I was spending time learning the framework instead of learning actual CSS, and the gain from that wasn't that big after all. (Here is a more detailed presentation of my views on the subject).

Collapse
whoisryosuke profile image
Ryosuke

I’ve been having the same issue. I’ve used utility CSS in the past for modifications/overrides on top of OOCSS classes, but never used it to style the entire app.

It seems excessive, disorganized, and counterintuitive. While being modular and “optimal” (when paired with Purge), its reduced things like code readability. Takes 5 mins just to parse all the props mentally (and tie them to DOM nodes), when I could attach them to a class name mentally. I could see the argument for the complexity of The CSS mental model, but this is by far the solution.

And anyone who says to use @apply — no. At that point you might as well use CSS properties and CSS vars (custom properties). It’s crazy watching people go all the way into a CSS file just to still write utility shorthand that’s a few characters shorter than the original CSS syntax.

And wrapping the Tailwind in a React/Vue/etc component is sweeping a mess under the rug. You still have to deal with that mountain of utility classes sometime.

I am a big fan of “utility style props” (aka Styled System, xStyled, Restyle) which are basically like Tailwind combined with React props, but more efficient with CSS in JS managing class names and chunking CSS.

Collapse
smcalilly profile image
Sam McAlilly

It can be a mess. I think it just depends on preference and how you use it. From your example, you could create custom utility classes and name the custom classes to what you want, like "chat-notification" etc so that you have a sense of what they're doing.

It's also a good library because it's well-documented. I'd rather confront that than whatever undocumented mess the frontend designer two years ago left behind.

Coincidentally I just posted an idea to try to do cleaner Tailwind + React: dev.to/smcalilly/clean-react-with-....

Collapse
bias profile image
Tobias Nickel

i this week also asked this question in an other tailwind article.
dev.to/tobiasnickel/comment/15783

the reply was, that you use tailwind directly with angular,reactc,vue,... components. you are in a notifications-component file. So the notifications chat notifications css class is not needed.

but It is still very hard for me, to buy into these css frameworks or css processing tools. because it is very powerfull in chrome to click at an element, change the css in the devtools and save the css. It feels pretty much like designing and less like writing styles.

Collapse
xwero profile image
david duymelinck

The only case I would consider Tailwind in production is when you give a cms user a lot of layout flexibility. It creates a lot of classes in no time in a structured way.

It is also good for hackatons were you want a fast setup and speedy changes.

It looks like a lot of people use it to create a design without an overview, which is a bad habit. Atomic design gives you an easy mental model how to get a design overview.

I would not say Tailwind is a mess. It solves a problem people have. Just use it wisely.

Collapse
shaijut profile image
ShaijuT

Yes true, What about Bootstrap 4, do you like it ?

Collapse
rtivital profile image
Vitaly Rtishchev Author

It's better, but for me it's crazy to use css framework, it's much easier to write my own styles

Collapse
cwraytech profile image
Christopher Wray

I like tailwind because I can reuse classes much more than if I was writing the css by hand. I do agree it can be hard to read, but the reusability of the css is amazing.

Collapse
unalo_baayriyo profile image
Kamlesh Nehra

Agree, it looks more like bootstrap css classes

Collapse
jpkontreras profile image
Julio Contreras Marchant

try the @apply method

Collapse
jvarness profile image
Jake Varness

Sounds like you might be more akin to something like Bulma that has CSS classes that are named more like components. The classes are much more readable imo.

nektro profile image
Meghan (she/her)

@eddieaich please use <button> or <a> or stop making buttons

Thread Thread
eddieaich profile image
Eddie Aich

@nektro only if you tell Twitter and Facebook to stop using divs as buttons

Twitter button

Facebook button

Thread Thread
rtivital profile image
Vitaly Rtishchev Author

Oh, see big guys are making web less accessible, let's follow their example and copy their mistakes

Thread Thread
eddieaich profile image
Eddie Aich

Those examples are indeed accessible by using ARIA attributes, making the elements focusable, etc. They just implemented them differently without using the semantic options. As I said before, do what works for you, which doesn't mean ignore accessibility.