I see you have a .button, cool! So did you load the entire context of your project into your mind, and calculate every possible iteration of kind, size, color etc this button may have? And once you did that, did you come up with a semantically correct naming scheme that is clear and will not succumb to the inevitable .button_checkout_special_page_cta_widget a particular page will end up requiring?
No? Neither did I. I stopped thinking about CSS entirely almost a decade ago. Thanks Tailwind.
We need to talk about two use-cases separately:
CSS for design systems is fine. You can define a .button[data-variant=”primary”]. It’s intuitive, performant, makes sense.
CSS for application code is terrible — specifically layout & typography. The different kinds of flex/grid layouts are tightly coupled to the dom structure. Tailwind is a great fit here:
<li class=”flex flex-col”>
<div class=”font-medium”>Title</div>
<div class=”text-sm”>Subtitle</div>
</li>
When you remove layouting concerns and maybe typography. You actually get close to the separation of concerns that CSS was initially aiming for. HTML determines arrangement and css determines the design system (colors, borders, shadows etc...).I havent seen this discussed a lot. The old Radix team had a short note: https://www.radix-ui.com/blog/themes-3#the-best-of-both-worl...
I was very much in the Tailwind camp, and simultaneously loved it for its local reasoning and disliked it for how its long class names messed up my components. It has always felt like a bolted-on hack to me but I couldn't argue with the benefits.
Turns out, CSS Modules comes with the same benefits while feeling much more like a natural extension to the web platform. The only thing I thoroughly miss is functions and directives, which hopefully will land in browsers through the mixins proposal in not too long of a time.
As a 26+ year frontender, years ago I was against it on so many levels. Until I tried it.
Never looked back. Also, inheriting projects with someone else’s code is fine. Just a quick look at the central config and you’re good to go.
What is funny with your article is that you talk about tradition, when actually these traditions are based on assumptions which are themselves based on beliefs like : "separation of structure and style are a good thing".
But this belief is only true if the language to describe the structure is html or js which creates not reusable pieces of style/structure.
But when you go for a different approach where everything is functions and strong types (ADT), like when you use haskell for exemple, this debate is over, because you handle EVERYTHING in your code, and stop fragmenting the truth into opaque and dissociated worlds (html, js, css, database, glue, docker, ...).
Saying "unless you use @apply" invalidates this article for me. Everyone knows that @apply only exists as an escape hatch, it is not supposed to be used, except for when it's necessary like for compatibility with libraries that declare their own classes that you need to override.
The most commonly repeated point in the article is "escape hatches bad" and I think that's completely absurd. Of course you want escape hatches, otherwise when you do hit an edgecase you get stuck with no solution. That's exactly what would force you to throw away that approach and pivot. Tailwind doesn't have that problem because it was designed to include escape hatches.
Users deciding to use those escape hatches instead of sticking to a design, isn't Tailwind's fault.
Show me one non-trivial project with classic, well-organized CSS usage and clean semantic HTML. It just never works out. There were attempts like BEM, which looks promising at first sight but is awkward at some points and only works if all devs strictly follow the convention. We all know how this usually works out. I once was that semantic HTML and css classes guy too, I completely changed my mind. In the end, we want to efficiently build a clean and shiny website. No real user looks into the (unminified) HTML source code to appreciate those awesome, sophisticated semantic CSS classes.
I tried to use Tailwind in a hobby project couple years ago. I havn't updated my CSS skills since 2010 or 2015 -- I certainly missed grid layout and some other new features.
Quickly, I realized Tailwind is documented on top on CSS, no way around. This makes you keep in mind both.
No idea why is Tailwind even a thing.
Tailwind requires a certain mental shift. That typically comes on a project where you feel like "there needs to be a better way", then you read https://tailwindcss.com/docs/styling-with-utility-classes and feel like BINGO!
Using @apply totally KILLS Tailwind - that's the clearest tell that the author didn't have that moment yet. Hence the other arguments in the article that feel somewhat petty.
This is written from a point of view of somebody who never went through the progression of CSS to CSS preprocessors to component-based frontend libraries. Tailwind is an answer to all the problems that become apparent in CSS in that progression.
Ergonomics for hand composition are less relevant now. The most important trait is how does a long-lived project perform when written by a coding agent.
I’m curious if anyone has any takes on the different approaches to agentic styling.
I’ve tried Tailwind for this without further steering, and after a while the agent starts adding unnecessary utility classes everywhere. Then I taught it to micro-Ralph eval all its utility classes using screenshots and this works ok but burns a lot of tokens. Treats the symptom and not the problem IMO.
Maybe with the right set of constraints there’s a subset of Tailwind that composes better? I went back to vanilla CSS for now, on the hope that it will readily port to whatever the best thing ends up being later.
> It breaks the separation between structure and design
I have a problem with this argument because CSS _itself_ breaks that separation.
`border: 1px solid red;` is design, but `display: flex;` is structure.
In my opinion, it makes sense for display properties to live in the HTML because they are by their very nature very tighly coupled to the markup.
Though I appreciate that making this distinction creates a separate problem of your team having to exercise good judgement (and consistency) in which CSS belongs where.
IDEs can autocomplete tailwind classes but eventually you just memorize them, also with AI assisted coding I don't even write the classes anymore just small adjustments like changing pt-3 to pt-4, AI implemented dark mode in a small app I am working on in one shot, I think tailwind being in the training data helped tremendously with the task.
Strongly disagree with the point of not learning CSS. Bootstrap and the sorts that were en vogue before were terrible abstractions.
All the mental models required to be good at writing proper stylesheets are trained nicely with Tailwind
What's so strange with Tailwind users is how insistent they are to impose it to codebases they don't know. I've never seen this even with vue vs react.
People that didn't write lines of code on this project, or so.
A few months later, your codebase has very long lines of HTML with classes that encode a whole programming language as strings separated by spaces, and barely no component anymore, nor semantic CSS tags.
CSS Modules are a better solution. They compile classnames into unique identifiers, so there's no need for BEM or SMACSS conventions:
After trying a Tailwind-adjacent library myself (UnoCSS) I came to the conclusion that it's awful, but it's the least awful among worse alternatives. Having gone through BEM, I have no desire to go back there. Layers, modules, and scoped CSS seem like they could address a lot of the pain points in semantic CSS that TW was trying to, but I've never used any of those in anger save for Vue's emulation of scoped CSS.
I'm not a frontend guy so take this with a pinch of salt, but for a few personal projects I've done my approach is now "ai pls generate css". I thought it would be inconsistent and broken, but if you tell it to care about accessibility and you're willing to clean up a bit it's not that bad and you end up with very compact transfers after minification.
I tend to like simpler/textual interfaces though, admittedly this won't be everyone's cup of tea.
I've used a lot of tailwind professionally over the last few years and I agree with this article - it is spot on.
I never even use tailwind in my own stuff, even though I think that small devoper-led side-projects are where it is a good fit (and the reason for it's popularity).
God help me, somewhere around 2016 I started using some alpha version of Bootstrap 4, and I just keep using that same version for all the basic stuff underneath my own SCSS for whatever I'm building. It's absolutely fine. A lot of it is displaced now by grids, but for flexbox stuff and modals and pushing rows and columns it just works. Very easy to remember class names and extend it. Thankfully I don't work for a company that cares about using the latest thing.
The only really critical things I need in scss are scaling typography and variables representing colors for iteratively generating class names and gradients and transparencies and stuff like that. By "scaling typography" I mean, I just have a loop that writes .scaling-typography-[a]-[b] where a and b range from 1 to 16, and the class that's generated scales from [a]em to [b]em between a phone size and a desktop size. In a rare case where that doesn't just work or it doesn't look good on a tablet or something, a couple specific rules extending xs- or sm- or whatever will cover it.
I'd appreciate a way to disable the real-time chat on this website, I don't see a way on mobile. It's currently full of racial slurs.
> there is a ceiling of basic classes you have to internalize
I think you mean floor. Floor is a minimum you need. Ceiling would be the maximum you can achieve.
Below this floor you're not useful. There's a ceiling to how much you can do with no code tools.
My (certainly not unique) take is: As with almost everything, choose what you use based on context. There is a very wide range of websites and web applications you can make. For some of those tailwind is a good fit. For many it is not.
For my own projects, I have been perfectly fine with just one large global CSS file, extending it just when I have to. But I could this being maybe tedious when working in a large team.
Tailwind didn't happen by chance - it was the culmination of many other failed philosophies for addressing a problem with CSS when it gets large.
Centrally the problem is this: large CSS requires a lot of active effort to prevent it becoming spaghetti code.
Writing your own CSS can work in solo projects or small projects but Tailwind was designed for large projects, teams and developers with an irrational fear of CSS.
This discussion again. We've been having it for 6 years now and Tailwind remains popular. Lindy's Law tells us it will remain popular for a while longer.
Many people are productive in it, others hate it. Carry on.
Coming from Qml, reading css/html is mind numbingly hard.. Haven't seen any other come even remotely close to the readability of Qml. I am talking generic UI development, not just for browsers.
15 years and I thought someone would have by now the neat syntax that Qml has.
If anyone knows an alternative, do tell. Very much looking forward to it.
I've seen one of these posts every 6 months for the last 5 years and not a single one has figured out components yet.
I switched from Tailwind to plain CSS with a UI library and it honestly made my site look better. Probably because it's much harder to spam backdrop effects.
My personal experience (for what it's worth): I've used Tailwind across various project of different sizes for about one year then simply stopped using it. I just don't see any real advantage to use it over regular CSS.
> It is hard to read
The hilarious thing here is that without JavaScript, the code is white-on-white, because some of the colouring is added on pre[class*="language-"], but the language-css class is only added to the pre element by JS.
Honestly, I found managing a Bulma codebase significantly more of a pain in the ass than Tailwind. Tailwind offers the expressiveness of actual CSS with the ability to make quick changes on the fly. As someone who has spent a lot of time working in CSS frameworks over the years, it is one of the better abstractions of its kind.
I sort of feel like this argument is trying to put a train that is already halfway across the country back in the station on the East Coast.
The argument basically boils down that in non-component approach (so not a modern web framework) it is harder to reason if you use tailwind classes. The rest is some serious bikeshedding, imo.
I personally started with atomic CSS framework (that was before Tailwind, we had our own) and it has both ups and downs, but overall it is really nice to use once you learn it, and it is not really hard to do so. It does not do anything to enforce variables, but that's why you need to roll either your own layout primitive components or some classes.
Tailwind is great. Not sure why you'd use anything else at this point.
In my opinion, the main problem with Tailwind is that it attempts to replace all CSS. Tailwind classes are often non-semantic and don't align well with CSS property names. Where pure CSS can be used, the code will be cleaner and more readable. It's important to remember that HTML has a "style" attribute, and it's often much easier to define a style block directly in the "style" attribute than to use a fragment and Tailwind's cumbersome CSS classes.
Pre-llm era Tailwind was great for the backend people who did not want to learn CSS. Nowadays agents got this. Openprops might be a good option even with agents, at least for me it works well. Not a surprise that Tailwind popularity dropped significantly.
I think the another llm-victim is React. For a while it was great to have a heavy framework that somewhat standardised webdev's JS approach. Astro just become the default goto lightweight option that is great to quickly build a nice simple UI.
The problem isn’t tailwind, it’s css.
It always has been css.
That being said, it’s a hard problem and I don’t have a better idea.
Tailwind seems to at least fix or move the problem in the right direction.
I wish it would at least be kept out of UI frameworks.
I agree with all points and they are the reason I'm very selective which project uses it and which doesn't.
It did give me some ideas for my own stylesheet writing though. The colorsystem is something even non-tailwind projects will have now.
I think having an unmoderated chat directly on the page certainly is a choice…
I’d be rather interested in a recommandation that don’t comes with de usuals pits of others solutions and comes with a the actual css code that will apply to the element displayed in autocomplete while editing the html / jsx.
Not a big fan of vanilla tailwindcss too, but (from the perspective of someone that needs the job done) "just use modern CSS" as the author suggest is even worse.
Honestly I tire of this debate. Both sides have good points have both choices are valid.
I don’t particularly like Tailwind because I know and like CSS. It’s powerful, Tailwild stands in the way of me doing what I want with it. But in this thread you’ll see people saying “it’s great, I haven’t had to learn any CSS in years” and that’s not an invalid viewpoint.
Tailwind is to CSS what React is to the DOM, if not even more so. If you’re working on a ton of boilerplate UI it lets you get the job done without having to learn the core technology being used. For better or worse that’s where the industry is today.
All I would say is that solely Tailwild folks owe it to themselves to look at modern CSS sometime. The arrival of variables in particular is a gamechanger and makes things like palettes and theming far more intuitive than it ever used to be. When I see how Tailwind handles dark mode I cringe. With raw CSS you set a color palette as variables then override them with a media query. The element itself doesn’t need to have anything added.
if you don't like this.
.button {
@apply py-2 px-4 bg-indigo-500 text-white font-semibold rounded-lg hover:bg-indigo-700 focus:bg-indigo-700;
}
why not write it like this? .button {
@apply
py-2
px-4
bg-indigo-500
text-white
font-semibold
rounded-lg
hover:bg-indigo-700
focus:bg-indigo-700
;
}My counter-argument: I can copy any cool-looking Tailwind element I find on the Internet into my own project and it'll look exactly the same.
I am building a version 2 of my FCSS framework. https://github.com/meerita/FCSS2 check it out, fork it, use it, you're welcome.
I think tailwind helps. It is same as AI prompting, but in a deterministic and fine-grained way. Bottom line - it reduces the time to build.
We already tried the fully semantic web in the days of XHTML, where the document is purely semantics and CSS purely transformation.
It didn't work in practice, because people want to structure their document based on how they expect it to be presented.
My understanding is thay HTML5 was built on the understanding that language purity just went in the way of productive compromise.
This is such a bikeshedding debate. While you don't recommend it, projects with Tailwind work. Over years. You can onboard new developers to it, able to contribute productively immediately. Likewise, you can pick up work after months or years and don't have to remember or rediscover how your styling layer works.
The conventions and class names come really naturally fast, and you can always look it up. It's just not as a big of a problem people make it.
But the most ridiculous part of the article I found the cascade complaint:
Yes, this does not work. Why should it?! There is not a single use case where this is a good idea! In classic CSS, you might want to override something based on modifier classes, but that is just not a thing with Tailwind! If you end up programmatically layering class names, you're looking at a code smell. Instead, you want to use attribute or state modifiers, like `aria-hidden:opacity-0`.