DEV Community

loading...

Why Tailwind Isn't for Me

jaredcwhite profile image Jared White ・9 min read

I've gotten into more than one heated argument on the interwebs lately over Tailwind CSS. I'm not proud of this. I don't like being at odds with anybody. I think the folks building Tailwind are talented and nice people. But at a pure technical level, I simply don't like Tailwind. Whoever it was built for, it was not built for me.

And in one sense, that's fine. There are tons of web technologies out there which I'll never use. Doesn't mean they're bad. There are plenty of groovy tech stacks to go around.

The problem I keep running into however is this increasing popular sentiment that Tailwind is the future (man). It's the way things should be done. In other words, it's opinionated and it inspires a cadre of evangelists. Again, on a certain level, that's fine. Rails is very opinionated, for example, and I love using Rails.

But Tailwind definitely throws down a gauntlet. I'll quote directly from creator Adam Wathan highlighted right on the Tailwind website:

“Best practices” don’t actually work.

I’ve written a few thousand words on why traditional “semantic class names” are the reason CSS is hard to maintain, but the truth is you’re never going to believe me until you actually try it. If you can suppress the urge to retch long enough to give it a chance, I really think you'll wonder how you ever worked with CSS any other way.

Challenge accepted.

I've tried it. And I've used it. A lot. A project one of my largest clients has me developing is built on top of React and Tailwind. So whatever you may come at me with, you can't accuse me of not giving Tailwind the good ol' college try.

Still not my thing. At all. In fact I have some real concerns about Tailwind, and what I find supremely frustrating is whenever I raise these concerns, I get immediate pushback from die-hard Tailwind fans who accuse me (in so many words) of just being a fucking idiot. As a programmer who has worked full-time in the web industry since the late 90s, that just doesn't sit right with me.

So since Twitter and Hacker News comments are apparently poor mediums for technical conversations of this magnitude, I will now attempt to outline the very real reasons why Tailwind is not for me.

Reason 1: Tailwind promotes ugly-ass HTML.

This first reason is an aesthetic concern, yet it's intimately related to real technical challenges which I'll outline shortly. But at the very least, I hate the way utility-css-only HTML looks. Hate, hate, hate it. Adam even acknowledges this head on when he begs us to "suppress the urge to retch long enough to give it a chance…". This is a tacit admission that writing markup this way initially seems ugly and weird—but somehow we'll eventually just "get over it" because the benefits are so great.

After a year of writing Tailwind, I haven't gotten over it. Sorry folks! You'll never get me to appreciate this:

<div class="min-w-0 flex-auto space-y-0.5">
  <p class="text-lime-600 dark:text-lime-400 text-sm sm:text-base lg:text-sm xl:text-base font-semibold uppercase">
    <abbr title="Episode">Ep.</abbr> 128
  </p>
  <h2 class="text-black dark:text-white text-base sm:text-xl lg:text-base xl:text-xl font-semibold truncate">
    Scaling CSS at Heroku with Utility Classes
  </h2>
  <p class="text-gray-500 dark:text-gray-400 text-base sm:text-lg lg:text-base xl:text-lg font-medium">
    Full Stack Radio
  </p>
</div>
Enter fullscreen mode Exit fullscreen mode

Now I already hear many of you screaming at your computer screens to tell me "dude, just use @apply if you want to keep your HTML clean! Problem solved!" Well, that is a potential solution, and in fact that's what we've done on the aforementioned project. Much of our HTML is oriented around component-scoped class names (fairly close to BEM in concept) and thus we use @apply extensively. But that brings me to my next concern.

Reason 2: @apply is fundamentally incompatible and non-standard (and largely unnecessary).

This is where a lot of Tailwind fans get tripped up and keep on arguing with me over and over again, so I'll try to explain this as clearly and obviously as possible.

  1. @apply mt-3 in a CSS file only works if you use Tailwind. It requires the presence of Tailwind in your build process. If you remove Tailwind from your build process, that statement doesn't work and your CSS is broken.
  2. While it's true you can take the generated output CSS of a site and use that without Tailwind, it's typically a bundled compilation of dozens if not hundreds of small CSS files scattered around a codebase (if you write CSS-per-component files like we do). It's not something you can count on for source code.
  3. Therefore, it's simply the truth that CSS files built for Tailwind are non-standard (aka proprietary) and fundamentally incompatible with all other CSS frameworks and tooling. Once you go Tailwind, you can never leave. (da da dum 😱)
  4. And as an added bonus, writing all your CSS files with @apply everywhere basically means you're not learning and authoring CSS. You're authoring Tailwind. No matter how many times you write @apply flex, that's not the same as writing display: flex.

Now I realize most of us aren't in the habit of trying to swap out CSS frameworks on projects on a regular basis. But believe me, I have done this! I'm on a client project right now where we're migrating from Foundation to Bulma. While it's true that it requires updating a bunch of HTML and some of the stylesheets in use, rest assured any custom bits of styling we wrote before will work again without hassle, because when you write plain ol' CSS (or even Sass), it just works no matter what.

And while @apply seems cool on the face of it, it ends up becoming an enormous crutch. For example, I like the way Tailwind makes writing styles using CSS Grid techniques pretty straightforward. Unfortunately, after having done so, I still don't really understand Grid syntax itself. I remain ignorant of the open CSS standard.

As for why @apply in the grand scheme of things is largely unnecessary, that brings me to my third point.

Reason 3: Tailwind's focus on design systems and tokens could mostly be replaced by CSS Custom Properties (aka variables)—which IS a standard.

People initially like Tailwind because it comes out-of-the-box with a nice design system and lots of tokens you can tweak (colors, font sizes, spacing, etc.). It's easy to get good-looking results quickly.

The problem is that all these tokens are defined…in JavaScript. A CSS framework. Using JavaScript for its design tokens. In 2021.

I hate to break it to you, but all modern browsers support this thing called CSS Custom Properties. You can define design tokens once at the :root level as variables, and utilize them everywhere. You can even modify them in real-time while the site is loaded, or overload them in particular parts of the DOM tree. And they work great with web components. More on that in a moment.

So for example, in Tailwind you can write class="mb-8" and you get a margin-bottom: 2rem style applied. But guess what you could do instead? Define :root { --spacing-8: 2rem } in your stylesheet, and then write margin-bottom: var(--spacing-8) anywhere you want. As in literally anywhere: a stylesheet, or a JS component, or even a style= attribute directly in HTML!

While the story gets a little murkier once you start looking at how to accommodate responsive breakpoints and so forth, nevertheless the principle here is that Tailwind uses a non-standard JavaScript-based build process for its design system at a time when you can build design systems using syntax that's native to all modern browsers.

Speaking of what's native in modern web browsers…

Reason 4: Tailwind forgets that web components exist.

This is perhaps the biggest knock against Tailwind. It seemingly was conceived and promoted in a world where web components don't exist. Tailwind CSS is completely unusable within the Shadow DOM. Some enterprising developers have come up with solutions where select bits of Tailwind styling can get injected into components through a build process, but it's definitely a hack.

Meanwhile, there are ways to build web component-based design systems today where global theming and component styling via the Shadow DOM (and exposed Parts) all work together. Again, you can do all this based on technology that's built-in and native to all modern browsers. And before you shrug your shoulders and go back to your React or your Vue, bear in mind that web components are not only an integral part of the HTML/CSS/JS spec today but are increasingly at the heart of further advancements to browser technology (for example how advanced customization of form controls might work in the future).

Tailwind in this respect is no more helpful to you than Bootstrap or Foundation or any other CSS framework written years/decades ago. (Even my beloved Bulma! 😢)

Reason 5: Finally, Tailwind encourages div/span-tag soup.

I almost included this in the previous point, but it really bears its own conversation. I have become convinced by now that using <div> and <span> tags everywhere in your markup is an anti-pattern. We live in a world where custom elements (aka <whatever-you-can-dream-of>) are fully supported and enabled by modern browsers. There's virtually no reason you're forced to write <div class="card"></div> when you can write <ui-card></ui-card>. And in fact it's quite possible to use custom attributes along with elements to write extremely expressive markup that—compared to ye markup of ol'—looks quite futuristic!

Take the Shoelace web component library for example. Here's a button:

<sl-button type="default" size="small">
  <sl-icon slot="prefix" name="gear"></sl-icon>
  Settings
</sl-button>
Enter fullscreen mode Exit fullscreen mode

And here's a modal:

<sl-dialog label="Dialog" style="--width: 50vw;">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  <sl-button slot="footer" type="primary">Close</sl-button>
</sl-dialog>
Enter fullscreen mode Exit fullscreen mode

Note that this isn't JSX. This isn't XML. This isn't some kind of fancy-pants template language you have to convert to ordinary HTML.

This is HTML. This is what modern markup can look like.

Compare that to an example from Tailwind's home page:

<button class="hover:bg-light-blue-200 hover:text-light-blue-800 group flex items-center rounded-md bg-light-blue-100 text-light-blue-600 text-sm font-medium px-4 py-2">
  New
</button>
Enter fullscreen mode Exit fullscreen mode

Ewwwww. 🤢

There is a future world of HTML/CSS/JS (and in large part it's here already) where you can write bespoke Grid/Flexbox layouts quickly and easily with vanilla CSS, set up design tokens with CSS variables, utilize a well-architected web component library like Shoelace (or even mix 'n' match two or three), and end up with a website/app that looks amazing and works quite well—all without needing any of the many megabytes of Tailwind utility classes that you then need to purge to get your performance back down to manageable levels.

In other words, Tailwind's main selling point (besides rapid prototyping via utility classes) is its attractive design system—yet the way it implements that design system really kind of sucks! (Incompatible with web components by default, only minimally leverages CSS variables, doesn't encourage custom elements/attributes with relevant scoped styling…)

Which begs the question: how does Tailwind enable us to "build modern websites" exactly? On a pure technical level, I honestly don't see it as being much of an improvement over Bootstrap. And Bootstrap at least provides an open-source component library for free. If you use Tailwind, they ask you to pay for it.

Conclusion: If you like Tailwind, use it! But don't try to convince me it's the future.

Listen, we can go back and forth on the relative merits or problems with any technology. There are definitely some benefits to choosing Tailwind, most notably how you can go from blank page to fancy-pants design quickly by simply hammering out a bunch of div tags with utility classes.

But after over a year of experience with Tailwind and weighing the pros and cons against other approaches to HTML, styling, and component-based web development in general, I'm thoroughly convinced that Tailwind does not represent the direction I wish to see the web head in as a whole. And apologies to all the Tailwind fans out there, but you just don't have a compelling argument that will convince me otherwise.

And that's why Tailwind isn't for me. YMMV. 🙃

Discussion (170)

pic
Editor guide
Collapse
jfbrennan profile image
Jordan Brennan

That was a great post dude. I experimented with Tailwind and share some of your same opinions, but you might be dismissing it a little too much. A balance of components and utility classes is what I’ve found to work well, especially for larger orgs that share a common design system.

I start with custom HTML tags and scale up to Custom Elements when JavaScript is needed. I implement a complete, but minimum set of styles for the component and leave room for customization with utility classes. My favorite example is an alert box:

<m-alert type="success">I’m a green box</m-alert>

<m-alert type="warn">I’m an orange box</m-alert>
Enter fullscreen mode Exit fullscreen mode

That component only requires a little CSS to style its tag (the m- prefix is for my library) and attribute, e.g. display block, some padding, border radius, and colored background per type attribute. I add an icon according to the type with :before and the result is a CSS-only component with a much more meaningful API that uses the same standard HTML constructs as native elements.

A component’s default style should be perfectly acceptable on its own, but should leave itself open to customization and that’s where a big collection of utility classes comes in handy. So for example, maybe bigger text and more padding is needed for a specific alert instance, just add some classes (but nothing to the extent that Tailwind requires):

<m-alert type="success" class="txt-lg pad-all-lg">I’m the same green box with bigger text and more padding</m-alert>
Enter fullscreen mode Exit fullscreen mode

I like this because it’s familiar and meaningful markup with no magic, no hacks, no dependencies, no boring div and span, and minimal to no classes. This alert component is usable everywhere and is compatible with any framework because it’s just CSS.

And like I mentioned earlier, one day when the alert grows up and needs JavaScript to support some new feature, like auto-dismiss after a given number of seconds, it can evolve into a Custom Element with no breaking changes or shifting paradigms:

<m-alert type="success" autodismiss="4">The same markup, but now I’m a JavaScript-enabled Custom Element</m-alert>
Enter fullscreen mode Exit fullscreen mode

I’ve a built a couple design systems following this pattern and the result has been good. I think it’s the balance that really makes the difference. Something like Tailwind is just as extreme as something like Material-UI and neither in my opinion offer the benefits of the custom tag and attribute pattern.

Collapse
oncode profile image
Manuel Sommerhalder

This is definitely the way to go. I had to take over legacy projects (just css and vanilla js) and each of them had growing utility classes mostly to make life easier by not having to write a variant class for every little change. But every project had different classes, different names. They felt incomplete, insufficient variables, hard to remember and not easy to switch between projects. This is where tailwind shines by providing consistency through all projects and being able to use your design tokens (margins/paddings etc.) with css classes. Then you put over custom elements for reusability and to bake in basic js and accessibility. And then vue/svelte/react for handling state, app structure etc.

Collapse
jaredcwhite profile image
Jared White Author

I love this. Great examples! I also like how you described a kind of progressive development methodology there. That's very much how I like to think these days as well.

Collapse
pepalinha profile image
pepa-linha

I don't think it's a good solution, even it's often used. If you add styles like txt-lg, pad-all-lg then you can get a lot component variations. You will lose track of which component variants are used in your code.

Collapse
jfbrennan profile image
Jordan Brennan

But are “a lot of component variations” the result of this method or do they actually originate with the designer? The designer. So, it’s a question of how to implement the designs in a clean and maintainable way.

Thread Thread
pepalinha profile image
pepa-linha

It is that point. How can you be sure it's from a designer? If you are a designer and you create a component that has two variants - success and failure. Then you can write code like this

...
...

So, everything with custom utility class (text-large, padding-large, border-radius-small, ...) is bad from me because that variant with custom class was not designed and developer invents :-) Then the consistency is broken. But it's just my point of view as a designer and developer in one.

Collapse
rizary profile image
Andika Demas Riyandi

Do you have any repo that you can share? It seems I need to follow this style.

Collapse
jfbrennan profile image
Jordan Brennan

I use M- ("em dash"). It's open source, so feel free to use it, fork it, contribute, whatever:
m-docs.org

Collapse
layzee profile image
Lars Gyrup Brink Nielsen

You had me at

<button class="hover:bg-light-blue-200 hover:text-light-blue-800 group flex items-center rounded-md bg-light-blue-100 text-light-blue-600 text-sm font-medium px-4 py-2">
  New
</button>
Enter fullscreen mode Exit fullscreen mode

Good luck redesigning that button across a site or application.

Collapse
oncode profile image
Manuel Sommerhalder

the idea is that you put it into a vue component or css class with @apply for reusability

Collapse
layzee profile image
Lars Gyrup Brink Nielsen

Thanks for your reply. What would that look like?

Thread Thread
oncode profile image
Manuel Sommerhalder

your component:

<template>
  <button
     class="button"
     :class="{
      'rounded-md': rounded,
      'bg-light-blue-100 text-light-blue-600 hover:bg-light-blue-200 hover:text-light-blue-800': blue
    }"
  >
    <slot />
  </button>
</template>

<style type="scss">
// use apply seperately or all in one line
.button {
  @apply flex items-center text-sm font-medium;
  @apply px-4 py-2;
}
</style>

<script>
export default {
  props: ['rounded', 'blue']
}
</script>
Enter fullscreen mode Exit fullscreen mode

in your app:

<BaseButton rounded blue>Button</BaseButton>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
layzee profile image
Lars Gyrup Brink Nielsen

Thanks for sharing. This looks cleaner, but I'm still wondering why this would be considered more beneficial than CSS Custom Properties except maybe for Internet Explorer support.

Thread Thread
oncode profile image
Manuel Sommerhalder

You can also use custom properties with Tailwind. Imagine you get a design (system) and have to implement it. You get some colors, margins, paddings, font definitions along with the design. You edit the tailwind configuration file:

module.exports = {
  theme: {
    colors: {
      primary: 'var(--color-primary, #000000)',
      seconday: 'var(--color-secondary, #333333)',
    },
    text: {
      hecto: 'var(--text-hecto, 14px)',
      octa: 'var(--text-octa, 16px)'
    }
    // margins, paddings
  },
}
Enter fullscreen mode Exit fullscreen mode

Now you will a get all (soon to be) needed (responsive) utility classes to build your components with, which are also themeable with css custom properties: text-primary bg-primary sm:bg-secondary border-primary hover:bg-primary....

It gives us a unified language where frontenders don't have to think about naming classes anymore and use a consistent language across different projects. You and others working on that project can easier implement the design, because ideally you now the class names by just looking at the design/figma. With vscode autocompletion and tailwind config viewer, it makes developing a joy. :)

With just custom properties, the class names are not set, different names across projects. It's not ensured, that the custom props are used by every developer when adapting new features, no responsive prototyping, and and and.

Collapse
ritikpatni profile image
Ritik Patni

I already experienced the pain, and it's not worth it

Collapse
karimmaassen profile image
Karim Maassen

And that's why Bootstrap killed the industry. This convoluted way of putting styling in HTML where it has absolutely no business is driving me insane.

Collapse
markpieszak profile image
Mark Pieszak

My thoughts exactly 💯

Collapse
pavelloz profile image
Paweł Kowalski

I have one word describing revolutionary approach in software development that might help: extraction.

Collapse
hyperpress profile image
John Teague

You had me at WebComponents and ShadowDOM. The creator of @apply revealed that it was poorly conceived. And I agree. I don't have a dog in the CSS FW fight because I don't use them. There are obviously lot's of folks that are fond of Tailwind, and that's fine. But I want to stay as close to the platform as possible. More generally, I think humans mostly overcomplicate CSS, and I frown on apps that need to build half the internet to function. My 10 cents.

Collapse
swyx profile image
swyx

nice post Jared :) I won't try to convince you but for those who want an opposite viewpoint this is my take on Why Tailwind. I'll link to your post from mine, for balance.

Collapse
jaredcwhite profile image
Jared White Author

Thanks for sharing, these are well-considered points.

Collapse
shaileshcodes profile image
Shailesh Vasandani

I've written my own CSS for most of my projects and tried Tailwind for a small one a while back. It never really sat well with me and I was never able to really figure out why, but I think your article really puts it into words very well. The fact that we have technologies like WebComponents that are already super well supported just makes the div soup that is Tailwind feel so unnecessary. Of course, no hate to those that use Tailwind; after all the beauty of the HTML and CSS standard lies in its very flexibility.

Collapse
silvenon profile image
Matija Marohnić

FYI, you can totally configure Tailwind to use Custom Properties as color values instead of hex:

module.exports = {
  theme: {
    colors: {
      purple: 'var(--color-purple)',
      yellow: 'var(--color-yellow)',
    },
  },
}
Enter fullscreen mode Exit fullscreen mode
Collapse
jaredcwhite profile image
Jared White Author

That is legit good to know. 👍

Collapse
otijhuis profile image
Okke Tijhuis

It's what I do as well. My tailwindcss config uses CSS Custom Properties for basically everything; colors, spacing and so on. That way I don't have to use @apply, I just use the custom properties instead. I also get consistency between tailwind and my own CSS. Change the property and everything gets updated. It also saves me from manually writing all the utility classes that I want. Like you I don't like tons of classes in the HTML but having to write your own classes whenever you just want to change something simple, like the margin or padding, is annoying as well.

This way I get utility classes with consistent naming (which are already documented) and I can still use regular CSS whenever multiple styles belong together. To me it's the best of both worlds.

Thread Thread
silvenon profile image
Matija Marohnić

Your method sounds great, looks like you found what's working for you, it's a good example of successfully mixing technologies 🙂 Regarding sharing config values, in case you didn't know you can also do that with the theme() directive as well, but of Custom Properties are dynamic and native, so probably a better choice. 🙂

Thread Thread
otijhuis profile image
Okke Tijhuis

Thanks :). The theme directive is indeed an option but I prefer not using any tailwind directives whenever possible.

In the end everything is about figuring out what works for your situation and what trade-offs you're willing to make. No technology is perfect. Something might be a perfect fit though, if you don't care about any of the trade-offs that were made. Unfortunately I see many people picking technologies based on hype, preferences or emotion alone.

Collapse
moopet profile image
Ben Sinclair

Agree.

Reason 1 and 2 get quoted to me when I try to argue my side, but really I don't see the benefit. If you want to make a rule for header > nav that uses things like big-white-text in its @apply rule, that's fine, but you can already do that with preprocessors like Sass. Tailwind being able to do things that we've been capable of doing for years isn't a selling point as far as I'm concerned - people don't really do that in the real world, and its selling point is that it's so much easier to do things in the terrible way everyone actually uses it.

Reason 5 is the big one for me.

I’ve written a few thousand words on why traditional “semantic class names” are the reason CSS is hard to maintain

I don't understand this. I've seen other FE developers in my various jobs make a total div-soup mess with inconsistent, non-semantic and redundant class names being the hooks for everything and I've managed to rewrite them into something semantic in fairly short order. If you need everything to be in a succession of wrappers I think that's a massive smell that you're doing something wrong.

Collapse
vahlcode profile image
Valentine Elum

Writing my own CSS is for me. There is this joy I derive from it!

Tailwind still an awesome project if you can deal with the reasons above. Some of them can be addressed by the creators though.

Collapse
destler profile image
Destler

Forget about Tailwind, let's talk about utility classes:

  • Reason 1:

    Can you show us how you would write the Tailwind example that you have given without utility classes and without simplifying it (HTML, CSS, media queries) that is easier to read and maintain?

  • Reason 2:

    Agreed, but you can abstract your components with static site generators, monoliths, web components or JS components. There is no need for @apply. The more experienced users in Tailwind's Discord channel will tell you not to use it. After that you are also fully independent of Tailwind and just use utility classes.

  • Reason 3:

    Please rebuild your example now with custom properties and style=. How many “hate”s would you write for this? :D

    JS component or stylesheet is fine, but show me as I said with your example how this would be easier to read and maintain.

    And yes, you can use Tailwinds “non-standard JavaScript-based build process” to build the CSS. You can also build it by hand. The outcome is both native to all modern browsers, but Tailwind is faster. Btw. Sass etc. has also a non-standard language-based build process by your definition.

    Regarding custom properties, you can use them in Tailwind and I am not happy they are not integrated, but we are talking only about utility classes here ;)

  • Reason 4:

    Can you show us your example with web components (full code) that is easier to read and maintain?

  • Reason 5:

    Your comparison is not fair at all, you compare a components html with Tailwinds CSS. Put the Tailwind button in a component and you have exactly the same. Show the whole source (HTML, JS, CSS) that is required to build exactly the Tailwind example that you have given.

Furthermore you said in the comments:

My concerns kick in after the rapid prototyping phase and you're having to maintain a large production codebase over time.

With utility classes (like Tailwind's) you have a convention for all your developers (easier to maintain), you have HTML and CSS that belongs together in one place (easier to maintain).

You only have a problem when you Copy & Paste html+utility everywhere around in your code and try to keep it in sync instead of abstract it to whatever kind of component you like. But that applies to almost everything you do in programming.

html+utility is keeping together what belongs together. I also hate how it looks, but I think it is faster and more maintainable than jumping between HTML and CSS, inventing class names, keeping different peoples styles in sync and thinking about CSS selectors.

Collapse
jaredcwhite profile image
Jared White Author

Interesting that you think it's my job to prove to you that you can write maintainable HTML and CSS. The burden of proof is on the "utility class" proponents to prove that their methodology is better, and I'm not convinced. 🤷🏻‍♂️

Collapse
destler profile image
Destler

Fair enough, you write an article and give an example that you think is bad and you say that you can do better, without showing us how, and I should prove you wrong...

Challenge accepted! 😎

But first things first: by reading your article I did realize that you have way more experience than me. Also I want to thank you for it, I already learned quite a bit. I am sorry that I directly started this discussion (or monolog) before telling you this.

Since it is really a lot of work without utility classes I reduced your example to just the first child:

<div class="min-w-0 flex-auto space-y-0.5">
  <p class="text-lime-600 dark:text-lime-400 text-sm sm:text-base lg:text-sm xl:text-base font-semibold uppercase">
    <abbr title="Episode">Ep.</abbr> 128
  </p>
</div>
Enter fullscreen mode Exit fullscreen mode

Here is the equivalent without Tailwind:

<style scoped>
  .radio-entry {
    flex: 1 1 auto;
    min-width: 0px;
    --tw-space-y-reverse: 0;
    margin-top: calc(0.125rem * calc(1 - var(--tw-space-y-reverse)));
    margin-bottom: calc(0.125rem * var(--tw-space-y-reverse));
  }

  .radio-entry .title {
    color: var(--text-lime-600);
    font-size: var(--text-sm);
    line-height: var(--line-height-sm);
    font-weight: var(--font-semibold);
    text-transform: uppercase;
  }

  .radio-entry .title.dark {
    color: var(--text-lime-400);
  }

  @media (min-width: var(--media-sm)) {
    .radio-entry .title {
      line-height: var(--line-height-base);
      font-weight: var(--font-normal);
    }
  }

  @media (min-width: var(--media-lg)) {
    .radio-entry .title {
      line-height: var(--line-height-sm);
      font-weight: var(--font-semibold);
    }
  }

  @media (min-width: var(--media-xl)) {
    .radio-entry .title {
      line-height: var(--line-height-base);
      font-weight: var(--font-normal);
    }
  }
</style>

<div class="radio-entry">
  <p class="title">
    <abbr title="Episode">Ep.</abbr> 128
  </p>
</div>
Enter fullscreen mode Exit fullscreen mode

Also you have to define all your custom properties before, if you do not use something like Tailwind.

Of course you can reduce that a little with SASS, but you introduce non-native syntax again and if you use mixins even more complexity. But after that you can abstract whatever you want here, CSS classes, JS components, web components, SSGs, at one point you have to write those CSS rules down to achieve the same result as in your example. You actually do not need all those CSS rules? Alright, then you can probably also simplify your utility classes.

After all, HTML was not made to be read by humans but by machines, but still, my point stands, I think there is no way you can make this piece of code easier to understand for humans without utility classes. You can probably make it better than I did, but not better than in your example. Put that in a component and you have the shortest, best readable and best maintainable code (for what I know).

Now, you do not have to prove anything to me, but then my point stands here and I have good reason to go fully utility classes, because I am going back and forth with this in the last months and also had opposite discussions in the Tailwind channels.

Thread Thread
jaredcwhite profile image
Jared White Author

I appreciate what you're trying to tell me. Look at the verbosity of all that CSS! Why do we have to write all that? Geeez.

But the crux of the matter is I likely would never write so much CSS in the first place for what amounts to a "one-off" bit of content. Is there a way we can compose this content out of simpler, more generic components? If so, we write the components' CSS once with an appropriate degree of customization, and then it's useful in many different scenarios. I have no problem with writing verbose CSS in such a case. (Let's all agree that append-only global stylesheets is Not Good.)

I would also take exception to your assertion that HTML is meant for machines, not humans. I happen to think most programming languages most of the time are in fact intended for humans. That they're machine parsable and executable is simply a bonus.

Thread Thread
destler profile image
Destler

That they're machine parsable and executable is simply a bonus.

Is that why so many programming languages existed already before the computer was invented? Drifting away here, I give you that point.

Look at the verbosity of all that CSS! Why do we have to write all that? Geeez.

This I find not quite fair and neither a good argument after I said:

shortest, best readable and best maintainable code

Furthermore:

Is there a way we can compose this content out of simpler, more generic components?

This is easy to say without giving real world examples. In this case, I would say a radio entry (I just called it this) is a radio entry. Where do you want to abstract this? It is already super simple and still has so much CSS code. Also, where do utility classes stop you from composing content out of simpler more generic components?

If you made bridgetownrb.com, it is using quite a lot of Bulma's utility classes, I bet you already missed one 😛

The Tailwind author also explains quite well why you will probably fail to find those abstractions, make premature abstractions or discard abstractions after components are not as equal as you first thought:
see adamwathan.me/css-utility-classes-...

Finally, the more you abstract the more dependencies you have.

Thread Thread
jaredcwhite profile image
Jared White Author

If you made bridgetownrb.com, it is using quite a lot of Bulma's utility classes

Indeed it is. Goal is to refactor most of those out, especially as a recent sprint converted a bunch of generic divs to custom elements which will be much easier to style.

Collapse
andrewmcodes profile image
Andrew Mason

To me it’s really a shift from Bootstrap. Almost every production app I’ve ever worked on ultimately has a bootstrap theme underneath all of the crap they layered on top over the years. The other issue is the jquery plugins these themes are written on are mostly unmaintained and written in a flavor of JS that causes issues with the type of build systems and tools that are now standard.

You’ve got great points as usual, but the reality is most teams don’t write code like and most developers only know where to find the answers to layout issues vs a deeper understanding of HTML & CSS.

Honestly I view Tailwind as a gateway for most teams. They don’t want to write css but they’ve seen the pain of pulling things off the shelf and want to try something else. Tailwind is great if you’re writing components and it brings them much closer to the type of code you’d like to see vs the opinionated all in one tool like Material and Bootstrap were (I know they have both changed a lot as well).

I do feel this was a bit of an unfair jab though:

And Bootstrap at least provides an open-source component library for free. If you use Tailwind, they ask you to pay for it.

Tailwind has always been very clear that they are a utility library, not a full fledged framework. No one is forcing teams do use it and I think as a community we should be excited they’ve been able to still put out their work for free and make money at the same time. And to to be fair bootstrap sells themes and the bootstrap theme economy is still massive.

¯_(ツ)_/¯

Regardless I think people should definitely judge for themselves whether it will work for them or not and not follow the crowd and I applaud you for doing so.

Collapse
jaredcwhite profile image
Jared White Author

Thanks for your thoughts Andrew. I think for me the disconnect there is I totally get why people like to use Tailwind for rapid prototyping. The thing is, if I'm going to pick a CSS framework to use for rapid prototyping, I don't want to write all my components from scratch, I want them already there for me. If I have to do the work of writing a ton of components, the value of using Tailwind to do so diminishes rapidly. Maybe I would feel differently if I were in the PSD-to-HTML business or something?

Collapse
alexmartinfr profile image
Alex Martin

I understand you aren't interested in the paid optional TailwindUI component library. It's not for everyone.

But it isn't the only way to get ready-made Tailwind-styled components if you want them!

Check this free components repository:
tailwindcomponents.com/

🙂

Collapse
syntaxseed profile image
SyntaxSeed (Sherri W)

Great analysis.
I have yet to come upon real problems with semantic, component based CSS. I've been curious about Tailwind, but it seems to be the opposite of what CSS is meant for.

I think it's totally cool for people to like or dislike a technology & discuss the why. Don't know why people get so defensive about it.

Collapse
bezpowell profile image
BezPowell

I'm 100% in agreement. I've always used semantic CSS, as the message when I was learning was always styling should be separated from content.

Any form of utility based CSS seems to be in violation of that. If I use classes like 'bg-gray-100' and 'text-lg', then later on decide that the particular element is going to be differentiated using some other means I would either have to rewrite all my markup to achieve that, or to re-write the styles so that they now do something totally different from what the name suggests.

I definitely see the benefit of utility classes for rapid prototyping, but the future maintenance burden and loss of semantics seems to outweigh those advantages for me.

Collapse
dansvel profile image
dan

i surely can learn css,, i understand a bit,,

but choosing a color is hard to me,, need to knkw what hex color everytime i need coloring text or background,,

tailwind help me to simplify of doing that,, in same color i can use 9 sub-color

i even dont know what hex is, i can use text-green-400 hover:text-green-600 for my styling my link

if only pure css can do it too, may be i'll learn more,,

Collapse
jaredcwhite profile image
Jared White Author

Tailwind has an awesome color palette. Even Shoelace, the component library mentioned in the article, has adopted colors from Tailwind. I think you could maybe import Shoelace's base stylesheet and use their CSS variables for colors, even if you don't use any of their components! 🤔

Collapse
dansvel profile image
dan

oh,,,
so i can use it like this,,?

import "something"

body{
  background-color: var(--gray-100);
  color: var(--gray-900);
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
jaredcwhite profile image
Jared White Author

Exactly — you'd just reference the relatively small Shoelace CSS file of all the variables. Here's an overview of the colors: shoelace.style/tokens/color

Thread Thread
dansvel profile image
dan

wow,, nice to know,, thanks

Collapse
wout profile image
Wout

Great post, Jared! Exactly my feeling. It made me cringe — such a mess it makes of your html. I did use it in a Rails project for a client and found myself @apply-ing a lot to keep things clean, which kind of defeats the purpose of utility classes.

While not ideal, BEM still works well for me. Especially in combination with view components in Rails.

Do you know Cube CSS? I haven't used it, but it's something I might consider for a future project.

Collapse
jaredcwhite profile image
Jared White Author

I'm not super opinionated about naming methodologies… I think having one is more important than any particular one. Lots of good ideas in CUBE.

Collapse
melissamcewen profile image
Melissa McEwen

Sounds like a lot of the objections I have. To be fair a lot of my other objections were answered in this excellent post In Defense of Utility-First CSS. I'm not sure where I stand now. I know I wish Tailwind had more ability to be able to convert from Tailwind to something else. It's a big reason I didn't buy Tailwind UI even though I bought the Refactoring UI book. I work in devrel where I will often make demo apps but I don't want a lot of stuff in them besides what I'm trying to demonstrate. I don't want to set up a bundler either and Tailwind CSS doesn't work great with a CDN/just as a simple stylesheet because it's HUGE.

For now I've been using a lot of "classless" CSS "frameworks" like Sakura.css.

Also it makes me glad I'm not a front end dev anymore 😂

Collapse
jaredcwhite profile image
Jared White Author

The link there is a good overview of the "pro" side. I think what's really changed the game though is the rise of CSS variables, thereby rendering the objection to inline styles moot. For example, in the section Inline styles allow you to do anything you want. it shows putting font sizes and padding and such in a style= attribute. Everything's hard-coded. That's bad. But now it doesn't need to be hard-coded! You can put style="font-size: --var(size-lg)" or style="color: --var(color-primary-500)" etc. And of course it scales up to regular stylesheets or even CSS-in-JS techniques.

So, in a sense, you can have your utility CSS cake and eat BEM too (or some such methodology), without having to define a thousand utility class names. That's how I like to look at it anyway.

Collapse
gsarig profile image
Giorgos Sarigiannidis

I'm on the same boat. I didn't find Bootstrap fitting for my needs a few years ago and I have the same feelings about Tailwind too (I too, have written my views on the subject in a post of my own a few months back).

My main concerns with such frameworks, besides the spaghetti HTML that you mention, have to do with the fact that they can become an obstacle if you want to implement something more complex.

Most importantly, though, you end up learning the framework instead of the underlying technology, so if at some point Tailwind becomes obsolete in favor of a new kid on the block, everything you've learned is useless. Learning actual CSS, on the other hand, will always be relevant, unless CSS itself somehow becomes irrelevant.

I could accept the tradeoff if writing CSS was so hard. It's not, though, and I find no real value into using class="flex" in the HTML instead of display: flex; in the CSS. Quite the opposite, as the latter offers much more control. Especially with tools like flexbox, grid, custom properties, and CSS preprocessors, I find frameworks to be more restrictive than helpful - at least for my needs.

Obviously, in the end each one should choose what makes them more productive, but it's always good to have a view of all the angles before you decide.

Collapse
clekstro profile image
Curtis Ekstrom

If you use Tailwind, they ask you to pay for it.

Please clarify/retract this. They sell a paid product, not the framework. And they've reinvested much time/effort/money back into the framework thanks to the success of that product, which is great for everyone.

Collapse
jaredcwhite profile image
Jared White Author

I was clearly talking about component libraries, as I'd just said that Bootstrap comes with one for free. If you use Tailwind, they ask you to pay for it. 😅

Collapse
clekstro profile image
Curtis Ekstrom

If you want high-quality Boostrap UIs/components similar to Tailwind UI, they ask you to pay for it: themes.getbootstrap.com/official-t.... None of this changes the fact that leaving the "UI" off of the name can make people think they have to pay to use the CSS framework, which is patently false.

Collapse
okikio profile image
Okiki

I feel like tailwind is often misunderstood. To me tailwind isn't really meant for production its meant for fast prototyping and development. If I want to make a clean and effective layout I use tailwind for general layout, I then convert all those classes and @apply to proper css and simplify my stylesheets, this allows for fast layouts and forces me to take a good look at my code when finalizing for production. I generally avoid using web components unless it's absolutely necessary, I use pug when rendering my site, I use pug mixins for components, and sass for styling. If a certain component requires web components, I again use tailwind for prototyping and then optimize the styles and classes for production.

Collapse
tillsanders profile image
Till Sanders

That might be a valid point! But having recently converted a project from Tailwind to vanilla CSS, I don't really see the benefit of prototyping this way.
Though I must admit that might be because I was using Vue Single File Components, so I was essentially only moving Tailwind classes further down in the same file and translating them to vanilla CSS.

Collapse
okikio profile image
Okiki

Tailwind has all the basic utility styles I personally use e.g. padding, margin, background-color, font-size, font-color, etc... making it incredibly useful for prototyping quickly. In order to make tailwind faster I disable all complex styles and switch to using vanilla css for box-shadow, transitions and transforms, making my development process fast.

Collapse
jmau111 profile image
Julien Maury

Very interesting post.

And as an added bonus, writing all your CSS files with @apply everywhere basically means you're not learning and authoring CSS. You're authoring Tailwind.

Undeniably. I think it's a niche market, and indeed, you must know CSS before using it. To me, it's just a tool, lighter than bootstrap, that can help you delivering. For now, I consider it as a nice solution when I lack time.

Nobody forces you, and if you (I'm not referring to anyone here) read and see it at face value, or if you use it everywhere, just for the sake of using it, then it's just hype, then you cannot be more wrong.

Agree with you on the div/span-tag soup too. It is a concern.

Collapse
anthonycook profile image
Anthony Cook

I used Tailwind for a while but ditched it because all my sites/projects ended up looking too visually similar. That’s not to say you can’t make unique websites with Tailwind but most of the time I can look at a website and instantly tell it’s been made using Tailwind. We kinda had the same issue in the bootstrap days, it’s too easy to just stick with the default colour palette, shadows and sizes etc. Kinda kills creativity.

Collapse
steffan153 profile image
Steffan153

Idk, you might be confusing Tailwind with Tailwind UI?

Collapse
anthonycook profile image
Anthony Cook

Just regular Tailwind, people seem to go with the same shade of "Tailwind" purple or the minty green colour (usually on buttons or hero backgrounds). That combined with the more subtle stuff like the preset drop shadows and padding/margin spacing makes it easy (at least for me) to see when a site is using Tailwind.

Thread Thread
steffan153 profile image
Steffan153

Actually, after a few weeks I had the suspicion that Hashnode was using Tailwind. I looked and it was. xD

But based on my judging, the "Write a post" button on dev.to looks kinda Tailwindy, which it isn't.

Collapse
paceaux profile image
Paceaux

I haven't used Tailwind, but I have heard lots of hype. This is incredibly insightful as it's a reasoned criticism. With what you've shared, I can add some additional criticisms.

I've worked in the content management system space for 12 years, 10~ as a developer. I can say that, based on what you've shared, Tailwind wouldn't play nicely with an enterprise CMS.

An enterprise CMS is likely "detached" from the web site/application; they are two separate codebases. The website consumes content from some API or repository where there may be some sort of relationship between content and presentation of content... basically I have one "article" that may look like four different things, depending on where it goes.

I often have a server-side MVC app whose job it is to render these pages, and that app is usually written in .net. This means there's a division of labor where front-end people are writing a "static" version of a component and a back-end person is slicing up the HTML for the MVC app.

Tailwind would not play well for my line of work because it doesn't separate concerns: It's putting presentation rules in markup where they could exist collectively in styles.

A very common scenario I deal with is "conditional content"; if a subtitle is present, the title must look differently (reduced line-height, margin, etc).

The Tailwind approach means that my back-end developer must write logic around my <title> to add some classes, but on account of the presence of a <subtitle>. This means I have to write a clear business rule in my markup to explain to the back-end developer how "conditional classes" work.

Additionally, because Tailwind wants a crapload of classes, this means that, potentially, I have to write a crappierload of documentation to explain to the back-end dev how they're used.

Add to this that, Tailwind's desire to offload all presentation work to markup, this means that my client requesting a change to something "small" like a subtitle requires two codebases to be touched and tested, since every change requires me to update the .net application.

Tailwind seems to be useful for scenarios where the front-end developer owns the markup and there isn't a need for multiple presentations of that markup.

Collapse
tombohub profile image
tombohub

I like to see immediatelly what css is applied to the element. Save so much time going back and forth between files. Dont care if it's ugly. I even made an app to generate color palette for tailwind Palettolithic. That's how much I love it

Also in vscode with addon it goes so smooth autocompletion and CSS preview, not even thinking about it. No need to remember bunch of new tags or other stuff.

Collapse
jaredcwhite profile image
Jared White Author

Separation of concerns. There's a reason we shy away from putting SQL queries in controllers on the backend. We don't put HTTP routing in view templates. We don't put global environment configuration in database models.

Same on the frontend. HTML is about conceptual structure, text and multimedia, semantics, intent, accessibility, behavioral cues, metadata. It's not about color, shadows, typography, borders, outlines, shades, patterns, textures, layering. There's a reason we dropped tags like <center> and <font> and <marquee> (yikes!), while elevating <em> and <strong> over <b> and <i>. There's a reason we don't want to use <table> and related tags to lay out page sections.

The history of HTML has been a slow march towards a better vision of a universal "hypertext markup language". The way I see it, turning it into merely a box of crayons is going backwards, not forwards.

P. S. I like your tool. That's really cool. Any possibility of a :root CSS variables export? 😁

Collapse
tombohub profile image
tombohub

I agree with you on point of SQL, views etc to have separation of concerns when it comes to functional aspect, but I believe in HTML sense it's not so important to have that separation.

When we design website we are making visual part. HTML tags, together with CSS are that visual part. Most likely you are not just writing

and buttons, but they have some kind of visual appearance. So, if person takes one place to look how website looks visually in one place, like web page, then I think it's ok to also see the whole code that affects that visuals in one place.

Me personally, I just find it easier to have it like that instead of scrolling up and down CSS file, especially if it's not my code. Although with VScode addon you can find it easily. We have tools to make life easier for us, we should use them.

Re tool: Thanks man! Yes, I was thinking just the other day I should add CSS export too. Yeah it's on the todo list, I'll ping you when it's finished 😁 👍

Thread Thread
jaredcwhite profile image
Jared White Author

Cool, looking forward to it ☺️

Thread Thread
tombohub profile image
tombohub

hello my friend, as promised, now you can generate css variables from color palette: palettolithic.com/

Thread Thread
jaredcwhite profile image
Jared White Author

This is fantastic! 👏

Collapse
sheriffderek profile image
sheriffderek

Amen.

Reason 1? Regular CSS is just better in every conceivable way than some insane 2009 abstraction layer that's completely unreadable and at odds with modern layout necessities and techniques.

codepen.io/sheriffderek/pen/QWwyJmB

Collapse
sheriffderek profile image
sheriffderek

Go ahead and use it though. I just know that I'll quit writing HTML completely before I ever working on a project with that markup. And that's not with any handlebars or other attributes...

Collapse
dreitzner profile image
Domenik Reitzner

I get your points and personal preference. Very well written.
I do not agree about the web components part though. I'm not so sure that they will have such a strong influence in future web development.
Definitely agree about the CSS vars (I use them not often enough myself, probably because for most projects or clients still want us to support IE somewhat😅).
Personally I'm a fan of utility CSS styles but we shouldn't go overboard either 😉

Thank you for taking the time to express your points! 👏

Collapse
tojacob profile image
Jacob Samuel G.

Tailwind Is perfect until you have to do maintenance. Let's be honest, most of us get to use it because it lets us write community-accepted spaghetti code.

Collapse
tomhermans profile image
tom hermans

chuckles. quite the contrary actually.
At least the used classes MAKE sense instead of John's fantasized classnames piled on Ingrid's, piled on whatshisface who started this project 3 years ago.

Collapse
oncode profile image
Manuel Sommerhalder

haha exactly, I had to maintain large project with weird utility classes that were absolut horror to work with. tailwind brings joy!

Collapse
kettanaito profile image
Artem Zakharchenko

A fantastic overview, Jared! There are so many points that I felt myself when thinking about Tailwind application. Thank you for bringing an alternative opinion.

My favorite one is that a lot of "modern" tooling discourages learning of actual HTML/CSS fundamentals. A good tool teaches you and helps you achieve your goal.

Collapse
viorelmocanu profile image
Viorel Mocanu

Wonderful article, Jared!

Let's face it: most of the people loving utility-first CSS don't really remember csszengarden.com/ and what it stood for back in the day. :) And leaving melancholic issues aside, Tailwind is great for rapid prototyping and developer-heavy teams that need to churn out loads of content super fast with no substantial need for maintenance and future-proofing. What I love the most about it (compared to Bootstrap for example) is the fact it does tree shaking of sorts and leaves only the CSS you actually use in the production package.

But that's pretty much it. For large-scale enterprise-level projects, I would go out on a limb and say vanilla CSS (including variables) + SASS is the only way to go if you don't want to have massive headaches with maintaining everything going forward.

I'd add from experience that with a very particular way of writing all media queries mobile-first (with min-width only) directly inside the element they modify (respecting the cascade) gets you the clarity and atomic precision you need when making highly responsive, complex web apps. The sheer elegance of copy-pasting the element class from the browser inspector into the SASS file and reading all the ways that one element adapts to all resolutions in one scan of the eye is what saved a lot of time on some of the projects I've had the pleasure to impose coding standards for.

Collapse
jaredcwhite profile image
Jared White Author

Good points all around. I should point out that, regarding "it does tree shaking of sorts and leaves only the CSS you actually use in the production package" — you can do this with any CSS framework via PurgeCSS. I think Tailwind popularized the technique because you have to purge when using Tailwind. 😏 But you can purge with Bootstrap, Bulma, or any number of other frameworks.

At the rest of sounding like a broken record, you mentioned churning out loads of content super-fast with no need for future maintenance—so here's the deal: if Tailwind specifically advertised itself for exactly that use case, I'd be cheering from the bleachers. Go to town! Churn out that content, pat yourself on the back, and call it a day.

But to think Tailwind is the solution to long-term, multi-year projects where you want your code to be clean, lean, and mean…it's bananas to me.

Collapse
viorelmocanu profile image
Viorel Mocanu

P.S.
By the way, I found an unexpected (somewhat narrow) benefit of Tailwind or utility-first styles in general, which derives from the notion of rapid prototyping: if you're doing A/B testing using Google Optimize and the like, you need to write exactly zero CSS in order to create a completely different page design "in situ" (read: directly in Optimize's interface) because you can reuse those horrible utility classes and be done with it in the HTML alone.

Collapse
flexdigital profile image
Simon Daley

You're a brave man Jared. 😃

I also expressed some of my concerns about Tailwind CSS on another forum and got flamed. I thought it was well thought out, valid points, but apparently not.

In fairness, I did not trial it as extensively as you, and I always think it's better to "never say never". That plus the fact a very senior colleague of mine who is from a Ruby background has moved to a project that uses this and was telling me of its virtues tells me that I should at least keep an open mind.

But for me right now, I have no plans on using it in my current pipeline.

Collapse
tillsanders profile image
Till Sanders

Great post! What I was missing though is the cascade. Tailwind completely ignores it to make maintaining CSS simpler. I understand where this is coming from, but while the cascade is complicated, it's also the most powerful feature of CSS and ignoring it just leads to redundancy. I completely stripped a project of Tailwind after realising this problem. I had a complex component that would be displayed in numerous different color themes. A font color here, a border-color there, a background-color on focus there. Nightmare with utility classes, but so simple using the cascade.

Collapse
jaredcwhite profile image
Jared White Author

The C in Cascade is a feature, not a bug. Can it be abused? Sure! So can classes and methods in OOP. Doesn't mean we just throw up our hands and run away from OOP. CSS variables are another great addition to the cascading concept.

Collapse
davidhellmann profile image
David Hellmann

Tried it few times and now I think I use Tailwind for my next Projects. Don’t know why Apply is a huge topic here. I use it just for basic stuff (global styling).

I think when you go with Tailwind the only way is to use it mostly directly within the class attribute and not in the css files with apply.

But I can understand your points and for sure they’re not wrong.

Collapse
giorgosk profile image
Giorgos Kontopoulos 👀

Very valid points, had never stopped to ponder much about any of these the last few months while working with Tailwind.

I use tailwind for rapid prototyping and as a sort of design system in my component based workflow and has help me mostly.

But after reading this post I will never look at tailwind the same again :-)