DEV Community

Gravy59
Gravy59

Posted on • Updated on

Tailwind CSS: A critique

Tailwind CSS has grown increasingly popular for its utility-first approach to styling HTML. While it offers several advantages, such as design uniformity and extensibility, it also has its set of challenges, including lack of abstraction and repetition. This essay aims to offer a balanced perspective on Tailwind CSS, providing insights that could guide your decision to use it in your projects.

Design Constraints

One of the most appealing features of Tailwind CSS is its design constraints. These constraints enforce a uniform look and feel across all components of a website.

Extensibility & Configuration

Tailwind provides a configuration file that offers incredible flexibility, allowing developers to create any number of CSS properties. It even supports Just-In-Time (JIT) compilation, enabling the use of arbitrary values directly in your markup, similar to inline CSS.

Preflight

Tailwind's Preflight is a robust normalization feature built on top of modern-normalize. It harmonizes default styles across different browsers and offers a multitude of other features, such as easy-to-set borders and a reset of browser styles. Preflight is so useful that I would consider using it even in projects that don't utilize Tailwind.

Lack of Abstraction

Tailwind CSS does not abstract away from the underlying CSS, often resulting in class names that aren't much better than using inline styles.

Repetition

The framework falls short when it comes to creating reusable components. As illustrated in the following code snippet, the utility classes can become quite verbose and repetitive.

  <div class="mt-3 flex -space-x-2 overflow-hidden">
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
    <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt=""/>
  </div>
Enter fullscreen mode Exit fullscreen mode

Tailwind does acknowledge this shortcoming but offers solutions that are not entirely satisfactory. You can either adopt another framework, which might not be desirable for everyone, or use their @apply directive, which doesn't fundamentally solve the problem.

Note

Since writing this I've discovered that using Tailwind's plugin for prettier sorts valid tailwind classes, allowing repetitive classnames to be easily compressed when using gzip or brotli. This means that verbose class names have virtually zero impact on the end user or SEO.

Conclusion

Tailwind excels in rapid prototyping and in its capacity to adhere to a design system through extensibility. Its Preflight feature is notably excellent and could benefit more projects. However, it does struggle with verbose and sometimes unmaintainable class names, leading to inflated HTML file sizes.

Thanks for reading! If you take one thing away from this essay, consider incorporating modern-normalize into your future projects for more consistent cross-browser styles.

Latest comments (25)

Collapse
 
dinodonga_76 profile image
dinoDonga

One of the most appealing features of Tailwind CSS is its design constraints. These constraints enforce a uniform look and feel across all components of a website.

Totally agree. Tailwind "provides" a well thought out design system for free.
If you read this and you haven't read the "Refactoring ui" book by the creators of tailwind - i higly recommend it (first two chapters are free). However you are not limited at all and can use any arbitrary value and even css variables with the bracket syntax w-[420px] w-[var(--custom-width)].
It is built with customization in mind. Our designers are able to export design-tokens from figma. We then pipe them through style-dictionary and out comes a js file that you put into the tailwind config as a preset. The presets themselfs can take in other presets - so if you are building a whitelable solution for a product you just provide the brand specific theme by passing in the brand name via the .env. Inside your IDE you get autocompletion for all the values added to the config thanks to intellisense. You can choose to completly remove the defaults provided by tailwind or just add on to them. Usually the global variables named by the designers dont clash. So i end up with something like "w-space-2x" (space is holding our own custom scale).

Repetition
The framework falls short when it comes to creating reusable components. As illustrated in the following code snippet, the utility classes can become quite verbose and repetitive.

i find this example to be very bad faith. If you build something like that imagelist you reach for reuseable components. even vanilla html offers that functionality

<template id="component-template">
<img "class="inline-block h-12 w-12 rounded-full ring-2 ring-white" alt=""/>
</template>

sure in this case you need javascript but even if you abstract the utility classes into your own class with @apply you still have the repetition of the html and now you dont have a single source of truth anymore.

Collapse
 
kkm000 profile image
Cy "kkm" K'Nelson

Every time I hear the word β€œframework,” my hand involuntary reaches for the Reset button. When it is qualified by the adjective β€œopinionated,” it's the 7lb hammer and not the Reset button, for it's ideology sensu primo pushed upon you.

Historically, most of the successful frameworks has never been called so, neither by their authors nor users. In fact, of successful AND self-termed frameworks, I can probably name only .NET. I've been in the trade for 35 years.

In terms of project planning, If you have to use a framework self-termed or widely accepted to be a β€œframework,” multiply your time estimate by 10. This is my standard.

Oh, and I never wrote a Web site in my life, or heard about this this Tailspin framework, or how it's called. Even less had any reasons to use it. My statement is general.

Collapse
 
fanreza profile image
Muhamad Jamil

Im always use tailwind for Vue project, usually I use the framework too like daisyUI, and I agree about the bloated css class names that's why I use @apply

Collapse
 
yawaramin profile image
Yawar Amin

I mean sure, if a developer doesn't want to adopt the approach that Tailwind recommends, then they will have an unsatisfactory experience. Tailwind is not for everybody, not everyone wants to adapt to its recommendations. That's OK, but painting that as a downside of Tailwind is like saying that a downside of tea is that you like coffee better. Doesn't really make sense.

Collapse
 
acode123 profile image
acode123

Good points.

Collapse
 
machy8 profile image
Vladimír MachÑček

Hi @gravy59 ,

I have posted Best Practices for Utility-First CSS right now and saw your article.

I understand your points, but in the same time I disagree with you.

Even though I am the author of Stylify CSS, which is an alternative to Tailwind, I will defend the Utility-First CSS "base" here.

In my humble opinion, I think, that a lot of "bad things" about Utility-First CSS (Tailwind, Tachynos, others), simply come from "laziness" and the "simplicity of utilities usage" and a lot of them can be avoided by simply thinking things ahead, splitting the code and using the tool features correctly.

You can style any website that is using Tailwind with pure CSS, but let's face it, writing CSS isn't easy. It is for a small blog. But when the project starts to grow, you can get even 200+ css files. It's hard to maintain, clean, and compose together to get the correct bundle for the page, selector names often makes no sense and not even bem will help you, there are lot of duplicates, etc...

Every tool have its cons but utilities solve a lot of issues, not just fast prototyping and small size.

But I understand that not everyone likes to see a lot of classes in one attribute.

Collapse
 
brense profile image
Rense Bakker • Edited

Not to mention their solution for repetition: use a framework... A lot of UI component frameworks make using tailwind obsolete.

Another major drawback of tailwind is that it does everything at compile time, making it impossible to access your configuration at runtime, something that css-in-js solutions do support. For example in emotion you can access the entire theme object at runtime. Because tailwind classes are made during compilation you also cant use any dynamic values. For example, if you have to make a white label app... You'd have to use css variables and manipulate those with your own custom JavaScript which is extremely obscure and sucks for maintenance.

Collapse
 
gravy59 profile image
Gravy59

You actually can access the config at runtime - just importt your tailwind config as an es module.

Collapse
 
gravy59 profile image
Gravy59

There's also twin.macro

Collapse
 
pengeszikra profile image
Peter Vivo • Edited

When I make simple HTML without any CSS, just enough to import tailwind cdnjs and that is enough for joy all advantage, for example: tailwind sticky test - I make one css for <p>. Much fare easiest solution, and I never need to think about class names.

your example in react:


export const ImageList = ({imageList = []}) => (
  <div class="mt-3 flex -space-x-2 overflow-hidden">
    {imageList.map(({src, alt}) => 
       <img src={src} alt={alt} class="inline-block h-12 w-12 rounded-full ring-2 ring-white" />
    )}
  </div>
);

// on img tailwind class is uninportant parameter so I put the end of line.

export const App = () => {
  const imageList = [
    {src: "https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" , alt: "-- something meaning full --"},
    // other images, but mainly this list coming from some database
  ];
  return <ImageList imageList={imageList} />;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dinodonga_76 profile image
dinoDonga

I would go one step further and extract the image into its own component that accaptes the src & alt.

Collapse
 
theredfish profile image
theredfish • Edited

My brain works best with backend development but I should say that Tailwind CSS is the only frawmework allowing me to build beautiful UIs. It's very logic in its approach and coupled to a framework like NextJS it feels like perfect to me.

Everything is a component, each component has inline CSS. For common things like titles, images, etc... I use the @apply directive. Works like a charm, the feedback loop is really fast and unless you have important size constraints I can't see how Tailwind CSS is an issue.

But as I said, I'm more a backend developer than a frontend developer so not an expert at all!

Collapse
 
etienneburdet profile image
Etienne Burdet

I agree that their solutions to repetition are flimsy at best… but they also highlight the fact it doesn't really solve the problem it claims to β€”as you point out.

If you need Svelte or Vue (or any form of templating) to factor repetitive patterns, this days it means you also have css scoping anyways. Meaning you can just name your stuff carelessly… if you name it at all. I mean, I hardly use classes in Svelte, I just go straight for nodes 🀷 And then, you just jump the problem one level up: you don't name your css classes, but you have to name your components.

And then again, you build fast, but how maintainable is it? Want to change all spacing in cards. You look for .card-spacing? Of course not,Β you hunting for mx-4… ho, unless that one that was an mx-2 with a px-2 and that one that looks like a card but is panel… "We should put names on things, no?"

Collapse
 
danwalsh profile image
Dan Walsh

Interesting post, @gravy59, but there are a few points that I feel need clarification or expansion to give everyone "the whole picture".

I originally started writing a comment, but it got a little long, so I moved it to a post instead:

Collapse
 
gravy59 profile image
Gravy59

Thank you! I appreciate your insight - I also love tailwind but I try to see both sides of an argument. I didn't realize you could gzip html!

Collapse
 
danwalsh profile image
Dan Walsh

I'm glad you enjoyed my post reply! I'm always a little worried it might be taken poorly. So glad we can have productive conversations on dev.to! 😊

Yep, gzip is awesome, but not something (as a frontend developer) you typically have to worry about, as your sever-level configuration should have gzip compression configured and enabled.

Collapse
 
bcostaaa01 profile image
Bruno • Edited

I’d say that Tailwind CSS is definitely not my favourite CSS tool, but I’d point two advantages of the tool anyways:

  • speed of development, since it’s a library of pre-defined classes and you can create designs quickly.

  • consistency, since it reduces the need of custom CSS styling.

I’d say, the configuration is the worst part of it.

Still, CSS can be so powerful and so much more fun to work with, as long as you invest the right amount of time to it. There are far cooler tools to use like Styled Components, for example :)

Collapse
 
acode123 profile image
acode123

I personally believe that traditional CSS is a lot better, it's not that hard, and it's more reliable.

Collapse
 
yoquiale profile image
Alex

Disagree, Styled components feels like it's counter-intuitive and it's an unnecessary bloat.

Collapse
 
bcostaaa01 profile image
Bruno

Could you please elaborate on that? I mean, it’s not the easiest tool to learn either, but it does offer more reusability when you compare it to building CSS modules in a React project, but that’s just my opinion. Would be keen on hearing about others’ perspectives as well!

Thread Thread
 
gravy59 profile image
Gravy59

Styled components are 12.42kB min+gzip on base, so a bit large. I do like how it is similar to tailwind in that your styles aren't decoupled from html, and there is twin.macro for tailwind. I also think you should check out Stylify from @machy8, which is a cool hybrid of the two.

Thread Thread
 
bcostaaa01 profile image
Bruno

I understand your point and reasoning!

Thank you for sharing your thoughts and for the recommendations!πŸ™‚

Collapse
 
abutahermuhammad profile image
AbuTaherMuhammad

you're right.

I was about to mention configuration. It's still something that bothers me on every project...

Collapse
 
ben profile image
Ben Halpern

Good post.

I agree with the general thoughts here, also feel like Lack of Abstraction is probably the most surprising delight here. The shallowness of the abstraction makes it such a good layer for so much work I've done with it.

I'd say my biggest agreement here is the repetition part here. Tailwind gives you a lot of rope to hang yourself with, and I think the right way to avoid problematic repetition is whatever you come up with yourself, Tailwind isn't helping much!

Collapse
 
naucode profile image
Al - Naucode

Great article, you got my follow, keep writing!