If you're building something where your unit of organization looks more like a "page" than super fine grained elements, Tailwind will just make things messy and a higher-level library or writing your own CSS would be a better approach.
I don't think this changes much with AI - right now most models are not terrific at maintaining a huge degree of context in larger projects, so I could absolutely see an agent implementing CSS that works well in isolation but breaks due to the cascade. Maybe that changes in the future, but right now if anything I think Tailwind is well suited to AI because it enforces a certain locality to the changes you're making (i.e. you can reasonably assume that a `text-red-500` class on an element isn't going to break things elsewhere and wll do what it says on the box).
Buttons should be `button` tags. Links should be `a` tags.
This isn't really straightforward with the HTML `button` tag so `a` tags that appear like buttons are common. I'm not really proposing something out of the ordinary here, nearly every high level component library that has a built in "button" will support applying them to `a` tags and most likely have that as their primary example of a button.
Regardless of how that specific one looks like, I wouldn't like if it were replaced by a <button>.
[0] Things have probably changed, but Svelte 5 was a litmus test for how recent the data cutoff was for an LLM model. Meanwhile React _especially_ has no reason to change how the framework is used by coders. Thus there's years of scraped tutorials and sites for how to throttle a function or what-have-you in React.
If someone knows what they are doing and is allowed to make a decent UI library, you can have very clean, efficient styling without libs.
So at least that part is not so amazing.
>inb4 the web should be reserved for text documents and hyperlinks and user styles rule and running anything close to applications on the web was a mistake
>inb4 TUI
People like to feel superior to others, so dunking on people using a popular technology tickles some pleasure centers. Considering another viewpoint gets in the way of that, so other viewpoints are ignored.
CSS modules are also a solution to the above, but, once you get rid of cascading, and you have one stylesheet per component, it is again unnecessary to have the one stylesheet in a different file from the one component. There's no benefit to splitting a component and its styles into two files; it just makes it more annoying to edit. So we move the styles essentially inline, with either Tailwind or CSS-in-JS.
grid dup 1.0 scale wind-x drop cols-4Tailwind is just conceptually bunch of css inline styles repackaged as classes for performance boost. It was always just shifting complexity around, but offered an immediate perception of productivity gain, because you could just make a quick edit here and there.
I used it for months at s previous job and spent a lot of time googling “how do I write X CSS property in Tailwind”
1: https://www.zolkos.com/2025/12/03/vanilla-css-is-all-you-nee...
2: https://www.joshwcomeau.com/css/subgrid/
3: https://railsdesigner.com/custom-elements/ (more Rails specific)
I recently had to help out on a vibe coded project that inherited Tailwind (I think from Figma). It was a short assignment so I didn't dig that deep, but I don't understand the value prop of Tailwind. There was no obvious improvement over what I've been used to; in fact, it seemed a lot messier.
(Any complex grid model for example. If you haven’t used grid template areas try them out! They are by far my favorite way to do complex responsive layouts)
If you are styling a site, there's no competition at all. But if you are styling a single interaction-heavy page, it's a lot less work to put all the formatting inline instead of thinking about a descriptive structure and write your CSS around it.
Apparently a lot of people design entire sites one page at a time.
I am a big advocate of classes, not inline styles spaghetti.
The only contact I had with it was due to FE teams jumping into it, however every couple of months is something else anyway.
Styling radio buttons, working date inputs, forms that POST automatically, typeahead and server-side validation (eg username already taken.)
You spend all your time reinventing the wheel and then it's buggy and looks like a dog. To say nothing of the inevitable poly fills and Cross-Browser issues.
Tailwind does ultimately compile down to CSS. Anything you can do in Tailwind can be done in CSS.
What problems specifically do you see with server-side rendering?
Two different worlds and often two different kinds of developpers
Having a separate backed and frontend allows for separation of duties: the client is responsible for the user-facing content and the backend deals exclusively with data flows. This allows to have several independent clients, machine to machine connections etc.
Whatever arrives to the server is untrusted, and the API (or other interfaces) only verify raw data (a JSON for instance) and there are excellent (backend) frameworks for that.
Mixing the visual content with the data is possible (Django does that for instance) but from a security perspective less efficient (again - separation of duties). It also requires to possible double the work to have an independent API.
In other words, if you separate the two you do not care about security on the front at all, and the one who builds the back can focus on their expertise.
A major point of components is having a single place where you can change design and behavior across your entire codebase. With LLMs you can with very good accuracy tell them to change the look of "all buttons that are confirmation buttons in modals but not in the login modal". Doesn't matter if there are 10 such buttons or 1000, it'll take a few seconds for them to accomplish it.
I'm slowly forming the opinion that nowadays having copy-and-pasted "components", with their whole HTML just repeated over and over, is now a much less worrying thing in a codebase. Even a bloated CSS can be quickly fixed by a LLM.
And like you said: you eliminate the build phase entirely and frontend development ergonomics goes back to how it was in the early 2000s.
I'm not a huge AI person but it's obvious a host of great tools we've built to deal with our challenges would not have been significant if we had LLMs.
A very crude example: to translate code into a different language someone would need to write a tokenizer, a parser, an AST and finally a translator. All significant engineering challenges, taking days to finish. Nowadays you can tell an LLM to do that in minutes.
Staying on topic: we could easily argue that CSS wouldn't have existed if LLMs existed and could create table layouts from pasted images in a chat window.
Sure an LLM can update every table in your project every time you make a change, just as a human could. It also can make mistakes and burn significantly more tokens doing so. Or, it could update one component with a one line change and be done. Easy to verify, less change for mistakes.
Engineering is a discipline and does not change dramatically with the advent of outsourcing it to agents.
Do people do that on a regular basis? The most recurring case of building the above is when someone wants to include a language into a project and they usually don't bother with a translator. The second case is when transpilling (ex. LESS to CSS) and that's more akin to macro and other preprocessing tricks than a full blown translation.
> we could easily argue that CSS wouldn't have existed if LLMs existed and could create table layouts from pasted images in a chat window.
All current UI and Document conceptual models include styles (either as direct properties or separate models) and some does include named styles. The novel aspect of CSS is the Cascading concept, not the styling part. And cascading is obvious when we consider that there was supposed to be user stylesheets for documents. I read epub and user stylesheets (created from UI) is crucial.
They can now.
https://www.checklyhq.com/blog/agentic-rewrite-nodejs-to-go/
You could still develop frontend in the old way. Most browsers are very good at supporting old websites. But on one wants that.
I have never seen a single website where performance downgrade was caused by HTML.
However I’m not advocating for it, just making the case that it’s very hard to do worse than we’re currently doing with front end development.
Yes it makes it insanely easy to refactor duplicated logic into shared and tested modules. Why would you go the other way?
If one wants to do server side rendering with HTML templates like the old days there is no real “components” way of doing it (although declarative Shadow DOM is coming along). Partials are not the same thing.
In that scenario some repetition can give you the benefit of keeping a clean stack with no client side rendering.
CSS is really good these days. Scope rules and the new selectors available mean you actually have to work hard to mess things up like you did in previous eras
(Abstractions are still very useful to LLMs)
(I much prefer the scoped CSS model)
I do think Tailwind is overused, but it obviously has some legitimate use case.
Try using Tailwind with server-side rendered frameworks like Rails or vanilla HTML, and you will find yourself repeating the same stuff over and over again. Basically anytime you want e.g. a <button>, you have to repeat all the inline css for it. (There are plugins for Rails that adds "components", but I found it very clunky)
With AI I guess you could skip it, but I like to understand the code.
I can't be the only one who has no idea what this means. Do you mean high-abstraction classes that are bound to the DOM arbitrarily, and are difficult to change later? We've had 25+ years to see how that doesn't scale.
Do you mean Tailwind-style functional CSS written by hand? What would be the point of that?
I think there's a lot of technologies that are in the category of categoric mistakes to use, but popular enough and inert enough that they don't get dropped. There might be lots of different explanations depending on the technology, a lot have a reason the moment they are released.
Many third party tech becomes de facto standard at some era, then those features get incorporated into more mainstream tooling like browser css, and at that point the third party tooling exists only because of inertia and adds little value, but increasing supply chain and complexity risk.
Another example on this category is Axios which was essentially a cleaner DX for XMLHTTPRequests in client side javascript before Fetch, and later a polyfill for it. Nowadays it's a blunder to include it, but it ended up in stackoverflow answers and training sets, so it's "safe" to include. Developers that are on the bleeding edge drop it, but those that are catching up and reading tutorials or classes from the previous era, or using llms, use the previous era cutting edge technique, because they aren't evaluating all the parameters of a choice, they are just going with whatever someone else is using, which causes a coalescing around certain central technologies like React, Kafka, Redis.
Regarding Redis, my understanding is that it was popular during one era where databases were used to store state because we didn't know any better and people were moving fast and breaking things, so it was developed as a sort of hack to move things from a database into memory. But nowadays people just use it wherever (because it really is the most blank microservice you can think of, you can slap it anywhere), and people implement it off the bat.
There's lots of more specific reasons why bad third party deps persist. In python there's a library called python-dotenv, which is a categorical mistake to include, but people do it because they come from Node where it is a standard practice. But there's the standard nuance that in Node, it is a built in, but in python it is a third party dependency from a random dev (no offense to the dep dev, only to the users of the dep). It literally is replaceable by calling Open(), write(), and close(), but there it is with a lot of downloads waiting for the day it is targetted and hacked to capture millions of .env files. Personally to me it would be strike 1 of 2 if someone imports it, but there you go.
Yes, there's no reason to import tailwind, it adds nothing, the idea of:
red-color { color: red}
big-text { font-size: 14px}
<p class="red-color big-text"></p>
Is as laughably low quality, the application of a technique in form but not substance, similar to Getters and setters in Java, effectively making every property of a class public, but formally implementing the private keyword and access through functions. 100% a categoric mistake to implement.
I've written elsewhere of a phenomenon where devs avoid lerning a base technology, and rather end up learning a tech on a higher abstraction that ends up being a 1-for-1 of the lower tech, but it gave them the hope that they could avoid doing that learning, and they only drop into that realization by piecemeal ala "we did it not because it was easy, but because we thought it would be easy", and the only cost is that they learned the non-portable version of that technology and now they are vendor-locked professionally into that technology so they will spread it to the next repo, company, job posting and so on.
There's analogues in biology of inert viruses or parasites that don't materially cause damages, but their lifecycle depends on the host. Although to some extent, by competing for resources they always cause SOME damage, but it is important to notice that not all viruses or damages are highly damaging, that's a rare situation of very extreme pathogens, it isn't very beneficial to kill or damage your host. In fact many may be commensal, but there is a tendency to harm.
In conclusion, I think tailwind and such pre-ai slop-deps are more popular because they are inert viral parasites rather than because they provide value.
Much like saying, "there's never a need for Scratch, it's just a way for my kids to code fun things without worrying about semicolons."
Tailwind is a design library for people who don't want to learn or write CSS. They don't technically need it.
It's ironic because standard software development practices advocate isolation.
That said, Tailwind is not as powerful as plain old CSS. It's a bit like training wheels for CSS—you gain some guardrails and prevent newbies from messing things up too badly, but you also give up the power modern CSS affords you.
E.g. I invite you to look at some of Ahmed Shadeed's (https://ishadeed.com) layout tutorials, or whatever you find on the CSS-Tricks/Masters.dev homepage today. It's bonkers what you can do with modern CSS!
Tailwind gives you access some of what's possible with CSS today, and they keep adding support for new features, but it's impossible to express everything CSS is capable of using only utility classes. At some point, you need to write some actual real CSS code.
So personally, it's a tradeoff for me. If I really want the full power of CSS, I use plain old CSS. If I'm willing to trade power for the convenience of Tailwind's built-in design tokens and guardrails, then I pick Tailwind.
Both approaches are good and valid. The right choice is context dependent. With And with LLMs in the mix, it's not that hard to port a Tailwind codebase to plain CSS, or vice versa, so picking the wrong thing doesn't have as high a cost as it used to.