Tailwind CSS has taken the frontend development world by storm over the last few years. A utility-first library of CSS classes, it promises a new way of styling that's more consistent, maintainable, and faster than writing CSS directly. And for the most part, it delivers on that promise.
By using Tailwind you're almost guaranteed a single source of truth for all the values you use throughout a project. From typesets to spacing to colours, everything is defined in a single place. Which means that your code stays consistent and you aren't making things up as you go.
This was Tailwind's biggest idea, and the greatest benefit of utility-first CSS as a concept: compose don't create.
Tailwind achieves this with an extensive library of CSS classes to style everything. The idea being that you no longer write any CSS of your own, you compose predefined classes like lego pieces for every single property.
Developers new to this way of working often have a knee-jerk reaction just from looking at example code.
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Button
</button>
There's no denying that Tailwind is hideous. Its creator acknowledges as much right on the project home page. But that's just pedantry, and if the Tailwind way of doing things really was the panacea to all our problems then it would be a very small price to pay.
The Problem
The problem with this approach isn't that its ugly, or bloated (Tailwind purges unused classes), or that "you might as well write inline styles" (you shouldn't). It's that in order to apply a consistent set of values with classes, you also have to create classes for every conceivable set of rule:value pairs in CSS, even where it adds no value at all. So you end up using classes like .block
rather than writing display: block
and .text-center
rather than text-align: center
.
Of course you can mix Tailwind's more useful classes with regular CSS. But then you're breaking the Tailwind style-by-classes abstraction, and you have to maintain two seperate styling touchpoints for every element.
"So what?" you might ask, what's wrong with just using those classes rather than CSS? It certainly saves some keystrokes. Here is where Tailwind introduces new problems it shouldn't have to solve in the first place.
Reinventing CSS
Tailwind has to reinvent everything regular CSS can already do. Media queries, pseudo elements, selectors, and states. All of it now has to fit into the classes-only paradigm.
Tailwind achieves this with what it calls modifiers. By prepending Tailwind classes with md:
they will only apply above the md
breakpoint. By appending hover:
a class will be applied in a :hover
state. And so on.
Each of these tools is a poor facsimile of the functionality gaps it has to fill. Want an :nth-child
or ~
sibling selector? Back to CSS. Want to target the devices between two breakpoints? Back to CSS. Want to target children of an element? Back to CSS. You get the picture.
Of course you can go back to CSS to do any of these things. Lovingly coined "bailwind", almost every project will need at least a little custom CSS when Taiwind's classes and modifiers just don't cut it. But then you're back at breaking the Tailwind abstraction, and giving yourself maintenance headaches.
And if this is already a given, then why use pointless classes like block
when it adds no consistency or maintainability value over writing display: block
in CSS, other than a few saved keystrokes? Because while gap-filling classes like this don't add value, they do add a new Domain Specific Language (DSL) to learn on top of the CSS we all already know.
Class soup
The thing every critic of Tailwind yells at first, its enormous class strings. Yes, they're ugly, but who cares. The problem isn't a surface-level developer perfectionism one. It again comes back to modifiers.
Every rule that applies to a modified state needs its own class with its own modifier. Unlike in CSS where these states and pseudo elements are naturally organised into logical blocks, Tailwind's modified classes can very quickly become a huge, difficult to maintain mess that has to be carefully teased apart line by line.
Take a contrived example of the button we had in the intro of this article, with an icon of some sort added to a ::before
pseudo element, and less-than-ideal attention given to class ordering.
<button class="relative before:absolute bg-blue-500 hover:bg-blue-700 text-white before:left-2 font-bold before:text-sm py-2 px-6 rounded before:top-1/2 before:content-['\f00c'] before:-translate-y-1/2">
Button with icon
</button>
Of course in this particular example the icon would be better placed as a real element inside the button, but the point stands. Without (and even with) careful ordering of classes, these jumbles very quickly become a maintenance nightmare.
JIT to the rescue?
Tailwind's new Just In Time mode compiles just the classes you use on the fly, rather than pruning back a goliathan stylesheet after the fact. It allows you to use modifiers everywhere out of the box, and most importantly write arbitrary values right in Tailwind classes, like margin-[100px]
.
This is another language feature that was added to Tailwind's style-by-classes DSL in order to fix problems it introduced itself. And while arbitrary values mean you don't have to break out of Tailwind's paradigm as often, they also diminish the core value that Tailwind provides — a single source of truth for a whole project. Taken to its logical extreme Tailwind JIT is really just reinventing CSS, bit by bit.
The Solution
As I said at the very beginning, Tailwinds' central idea is a very good one — a low-level, utility-driven design system to get rid of magic numbers and bring consistency to your CSS. The problem was the implementation.
Thankfully CSS now has the same solution as every other language to consistent values: variables. CSS variables, or more properly CSS custom properties, are fairly new to the language but already adopted by every major browser, and used extensively in Tailwind's own internals.
For example, Tailwind's .p-4
padding utility could be rewritten like this
:root {
--p-4: 16px;
}
.button {
padding: var(--p-4);
}
And since we no longer have to write separate classes for every rule:value pair, we can greatly simplify our utility-first design system. We could have one set of size variables that can be applied to any part of padding, margin, width, height, position, etc. Without needing separate utilities for every combination of every property.
:root {
--size-2: 8px;
--size-4: 16px;
}
.button {
padding: var(--size-2) var(--size-4);
margin: var(--size-4) 0;
}
And since variables are part of the platform, they have a native runtime. We can interact with CSS variables using Javascript, and update them dynamically. This makes things like reskinning a whole interface for dark mode possible with just a couple lines of code, without introducing any new utilities or tools.
function enableDarkMode() {
document.documentElement.style.setProperty(`--color-background`, `black`);
document.documentElement.style.setProperty(`--color-text`, `white`);
}
So why don't we, instead of reinventing the styling paradigm altogether, just abstract all the values in an interface into a single source of truth by putting them in CSS variables that can be used anywhere, in regular old CSS, without all these new problems?
By Example
hyperfocalHQ / pollen
The CSS variables build system
Pollen
The CSS variables build system
Pollen is a highly configurable library of CSS variables for your next design system. It lets you write faster, more consistent, and more maintainable styles.
Made and maintained with ❤️ by the fine people at Bokeh.
Features
- Robust library of well-considered, style-agnostic CSS variables
- Fully configurable and extensible with CLI build tool
- Zero setup required to get started
- Responsive with configurable
@media
and@supports
queries - Lightweight, human-readable output if you ever want to move away from Pollen
What it looks like
Pollen's design tokens can be used to build any project. They're easy to customise and extend and they don't require preprocessors, class naming conventions, or non-standard syntax. Generate an entirely custom design system with a simple config file.
How it works
1. Configure your design system
pollen.config.js
module.exports = (pollen) => ({
output: "./pollen.css",
modules
…Pollen is a standards-driven CSS library that came from this line of thinking. It takes Tailwind’s core ideas and reimplements them as a 0.85kb collection of plain CSS variables. It can deliver all the key benefits of Tailwind without reinventing how we write CSS, thanks to the tools we already have in the web platform. Since it’s just plain CSS it can be used anywhere in any context and in any way.
But you might not need it
Full disclosure: I wrote Pollen. But I'm not trying to sell you on adopting it. I'm trying to sell you on the ideas behind it for your own work.
Pollen is just a collection of hopefully useful CSS variables, translated from Tailwind. But If you already have a solid design system with sizes, typesets, colours, and the other shared values of an interface, then you probably don't need Pollen, and you certainly don't need Tailwind. Write them in one place as CSS variables, and use them everywhere. That's the way out of this insanity.
Bring consistency to CSS by getting rid of magic numbers with variables. The other problems of CSS (deep composition, leaky inheritance, performance optimisation) aren't solved by Tailwind, nor by CSS variables. But they are made harder by the new DSL Tailwind introduces. At least by sticking to regular CSS you have all the other patterns and tools we as a community have been working on for the last decade at your disposal, without any gotchas.
Top comments (110)
Once again, if you don't like X technology, don't use it. But you don't need to look down on it. If you've created a library with a new paradigm, why not focusing more on the possitive side of it?
I literally opened the article praising Tailwind’s core ideas, and how it solves some real problems. But I also think it has some real issues that many people might not consider until they’re maintaining projects at scale with it. If Tailwind fits your use case then go for it, it’s a solid library by some clever people
Well said Madi. Thanks for the article, I'll certainly keep this on my reading list in case I do come up against your started issues.
Personally, so far, I love using Tailwind. I just find being able to do my CSS within my markup to be great for productivity, but doesn't feel like 'cheating' because I feel like I still have to understand all of the CSS that it's applying.
I also don't mind having to break out of the abstraction to do some custom CSS in specific cases - for me I don't particularly mind having a long list of utilities such as mt-3, border-2 etc - if it keeps that basic stuff from creating one or, perhaps multiple, large CSS files. It keeps what is in the custom CSS more focused.
Like I said, I haven't really felt like it's limited me so far, but I'll keep this post in mind if I ever feel like it does!
Because attacking the leading competitor is the easiest way to pitch something new. Conveniently enough, the author left out any any mention of cons & issues Pollen has over Tailwind. Or how it reintroduces decades old issues traditional CSS codebases have dealing with writing semantic CSS class names, organizing CSS, and handling specificity issues.
But yeah, this was a weird, if not cheap way to introduce Pollen.
Hey I really don’t know why you have this axe to grind. For a start nothing about using CSS variables implies needing semantic CSS class names, how you should organise CSS, or deal with specificity. There are so many solutions out there for all of those issues, and variables work in all of them. It is literally just CSS.
And as I have said maaany times, you absolutely don’t need to use Pollen, it isn’t some crazy new framework. It’s literally a collection of CSS variables. If you find it useful then awesome, but what I’m trying to get across is that the platform already has a solution to the value tailwind provides, and the style-by-classes abstraction introduces a lot of difficult problems that often don’t become apparent until you’re using it at scale.
Sorry you didn’t find it helpful.
You can't expect people to not elaborate on the important details you omitted.
Using CSS variables might not imply it, but moving away from a utility-first CSS paradigm like Tailwind's absolutely implies you now need to worry about that. Yes, there are many "solutions" to these issues, but they are far from a silver bullet that magically makes all problems go away. Writing semantic CSS class names will still be hard, CSS code duplication will still be a challenge, and you'll still need to come up with a strategy for avoiding CSS specificity issues. Not to mention you now have to factor in the decision making towards how you will solve each problem. And like you said before, a lot of this also doesn't become apparent "until you're using it at scale". And yet, you don't acknowledge any of this in your article.
Then perhaps don't write an article title and pitch that sounds like you're created this crazy new framework that solves all of Tailwind's issues?
Great. But you're not doing a very good job at this by omitting pros and cons from both sides. And no, praising Tailwind at the start of the article isn't quite what I mean. I know you had fair reasons to omit a lot of information and important considerations when choosing Pollen over Tailwind (since you clearly have experience with Tailwind) such as that a lot of things should be "obvious" to people that have used Tailwind. However, not everyone reading this has used Tailwind in the past. And all you end up doing is spreading missinformation for the sake of pitching your new library.
Tailwind fan boy right?
Nope, just someone who prefers conversations stay neutral and unbiased.
Though calling someone a fan boy just because you disagree with them instead of providing valid counter-arguments could be considered immature and I'd advice against it if I were you.
Sorry to say this Sergei, but your whole comment is biased towards tailwind.
I think the author is merely providing his opinion on a technology and proving an alternative approch to people new/existing to the CSS landscape and are looking for solutions.
If you disagree with the author's opinion then don't worry about it and keep doing what works for you and that is okay!
You literally went crazy about someone criticising tailwind. It's difficult to reason with people at that point, so fan boy would do.
So if the author conveniently omitted important key information from the comparison it's called "providing her opinion". But when I include the details she omitted, it's called "biased"? Something tells me people now throw around the term "biased" too lightly nowadays just to describe any time they dislike it's content.
The author provided his opinion in a biased way by omitting key information when it suited their pitch for a new library, I'm not sure how anyone here is missing it. And by omitting information, it does nothing but spread missinformation to people new/existing in the CSS landscape about Tailwind and it's alternatives.
I'm aware Tailwind is not a silver bullet either and it absolutely has it's downsides. However, for the comparison to be fair, it needs to include all valid pros and cons from both sides, not just the ones that suit the author writing it.
Madi only stated what everyone who uses Tailwind already knew. Also, the comparison was necessary as Pollen was "inspired by Tailwind".
It's LITERALLY the concept of Design Token.
Pollen has absolutely nothing to do with Tailwind. She's using Tailwind to get attention to her framework, nothing more.
Nothing can ever be improved if it's not okay to think critically.
If you don't like X article, don't read it. But you don't need to look down on it. If you've learned a library with a paradigm, why not focusing more on writing about the positive sides of it?
^such a narrow-minded approach to anything in life.
The metaphorical snake eating its own tail
How can you write something so compelling when I've put so much effort to get semi-comfortable with Tailwind?! 😉
It's often so long between the projects where I use Tailwind that I'm constantly on their docs (they are great by the way). But it's adding an extra step for me. I know how to do something in CSS but I'm using the docs to find what the utility class is for what I want to do. I've been most happy styling when I've used SASS modules. But it's no denying that regular CSS variables are greatly appreciated!
Thanks for the write-up! I'm gonna stare into the mirror for a bit and see if I'll just yeet a dependency where regular CSS makes more sense 🤔
Yo! Stop shitting all over the comment section folks. As some of you said, if you don't like it, don't use it. Stop taking place for a framework as if it was a person. IT'S A TOOL. And as it is with tools, new ones come out, new ones will be similar to old ones and some will be more or less effective. It's all about what you need to do. Just then you should think of how to do it. And in times like these where everything is overly complicated, there's no place to say no to something that's trying to make our life just a little better. If you think this post/framework is valid, great! If not, why even bother?
I myself love tailwind and won't stop doing so, because It does most things better than most if not any other framework I've used before (And I tried most already). I will consider Pollen too though. I love CSS variables since they came out, and if Pollen makes sense in that regard, there could be projects where it would make sense to use it. I love new tools. And you should too. 🤓 And Pollen and Tailwind kinda share the same principles and ideas anyways. Less is more.
Thank you 😌
I really didn’t mean to start a flame war. Tailwind is a solid framework written by some very smart people. I think it introduces some problems, especially at scale, but if it makes sense for someone’s use case then awesome!
I completely agree, I would say more: Tailwind never was a solution.
Using Sass modules properly gives you the same source of truth and theming capabilities without messing with the structure on a much more maintainable way.
For me, Tailwind is so absurd that you need to learn the entire CSS API in a wrong way that makes you able to work only with tailwind projects while learning the proper standard API from CSS and the capabilities of Sass leads you to a top tier front end dev.
The refactors when using tailwind due to re-designs are just hilarious and I bet it will die before it reaches the successful status as a tech. That and the bloatware in the structure are the main reasons for which it will probably die sooner than later. Imagine having to edit like 300 html elements changing some classes to another ones just to fit the new design. I'll leave in a blink 😆
For reference, currently there's only 1 job available on LinkedIn that mentions "Tailwind".
"much more maintainable" is debatable. Since CSS class name organization has been a staple issue for decades, something Tailwind does away with. Your HTML may be less messy, sure, but you are moving the organization problem to your CSS files where it can become an even worse nightmare.
Tailwind doesn't do away with reusable classes, they still allow them in the shape of components. And should you be building a SPA with React, Angular or Vue, you would simply change that component affected. If you need to update "300 elements" for even a small design change, you aren't using Tailwind correctly.
Then again, I don't blame you for thinking this. I had the same reaction as you the first time I learned about Tailwind CSS. And only after seeing it in action in a production project did I finally start to understand its benefits.
Along with a SPA library like React or a framework like Angular, Next or Vue I use CSS modules with a good architecture on it.
There's a theme.scss that exports the color-related stuff, a grid.scss that does the obvious and a global.scss that exports utilities and similar stuff. All the color-related stuff is declared inside theme (this allows for a quick reaction to design color changes) and what belongs to each component is inside the component directory as component.module.scss
it's just a matter of good architecture and experience. Of course if the devs are generating dummy classnames constantly it will be a mess but there's a nonsense on comparing the worse use cases as there's no tech to deal with that.
Of course you can shape reusable components in tailwind and tailwind at all can be fine till it's not, and the "till it's not" happen when there's a refactor needed due to a redesign, it increases the chances for something to go wrong with design context (the same component looking different depending on the page it's shown).
My opinion is on the main hand due to an analysis and POC did 2 months ago comparing tools for a changes proof code, long story short: custom implementation using Scss inside Next.
Also the team having experience on that is a keystone but also the tech community and similar projects that applies it. As I've said, 1 job available with the keyword "Tailwind", there's a dark future for it in my opinion; just good for people that hate CSS so they learn it the bad way, the same when you add chicken with the vegetables to a kid so he don't cry 😆
Bingo. A POC is often not enough to get a good enough idea of something. And like I said before, I don't blame you for feeling this way. It's absolutely a big change in terms of paradigm. And people, by nature, fear change (sometimes irrationally). Many things we take for granted today as "the best practice" took generations to receive widespread adoption. Only time will tell if this is another of those moments.
If anything, this is a terrible way to gauge a product's viability. All major libraries started this way. And if the State of CSS global survey teaches us anything, is that Tailwind is quite new, and yet is having exceedingly high adoption numbers thanks to it's record-high user satisfaction and interest to learn it. I guess we'll find out in the coming weeks once the State of CSS 2021 survey comes out to see how much Tailwind has grown in 2021.
It is a good article. It is sad some fan boy try to resolve their soul and ego problems here in the comment section. You should write more article and don't worry about these kind of people. Keep it up! Your photos are awesome btw :-)
Thank you so much!
Ah another week another "I don't like tailwind because" article. There are a few valid points but after about 18 months of using tailwind in several projects, I can say I'd find it pretty frustrating to go back to css or css-in-js again. Tailwind just makes styling fast and easy.
If anything, the verbosity of tailwind's class combinations works perfectly with view frameworks (React/Vue/Angular/etc) to abstract your styled elements into semantic components.
Anyway I'm not here to try and convince you to like tailwind. Some people love it, some people hate it. That's fine. Unless you're joining a new team where they use it extensively and you have to just suck it up and get on with, I suppose 😂
That’s totally fair! I won’t deny tailwind helps speed up prototyping etc, and it’s not so much “I don’t like because X”, it’s that after using it in my new team where we do use it extensively, there are a bunch of issues that a lot of people might not realise until they’re at scale. Like it’s not that tailwind is some horrible stupid framework, I spend the first chunk of the article praising its ideas. It just has limitations to be aware of, which I think have better solutions in CSS. But if it works for you then awesome, it’s a solid library.
And for everything that is coming for my throat saying I didn’t present a balanced argument, I mean, the article was already huge, and look at the hype around tailwind presenting it as the panacea for our problems and a new way to style things. Tailwinds own rhetoric certainly doesn’t mention any limitations 🤷🏻♀️
YES! This guy gets it
I just created an account here just to tell you how much I love this article. I agree with almost everything you said and I had most of these thoughts ever since I first tried tailwind. It blows my mind how every youtuber praises tailwind without mentioning the downsides.
Edit: I have nothing against tailwind. I just believe you should be aware of both good and bad sides of a framework/library/tool before you dive into a large project.
I’m so glad you found it helpful!
Because naming is literally one of the hardest problems in development? Why do you need a name in the first place?
Also, Tailwind still allows you to use your own names with
@apply
.Does no one actually read the Tailwind docs before trying to shit on the framework?
Tailwind isn’t the only way not to name things. I use css-in-js myself and much prefer that way of doing things, but that’s neither here nor there.
And yes I’ve used tailwind extensively and read all their docs. I seem to have really upset you I’m sorry if the article came across the wrong way
Software engineering is full of narrow-minded people who can't stand a critique.
Thanks to the people that dare to critique the status quo is why we can advance, I personally always felt like Tailwind will become the next Bootstrap, it's popularity is incredible as allows people who don't know CSS to ship.
Nevertheless I have use it and I understand it works in some scenarios, but I also prefer to use a similar approach by using the incredible tooling, APIs and new specs that CSS has, thanks for the article @madeleineostoja
For all who thinks this is a way to sell in Pollen, read the article:
Full disclosure: I wrote Pollen. But I'm not trying to sell you on adopting it. I'm trying to sell you on the ideas behind it for your own work.
Good points and valid arguments in my point of view.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.