Example: https://www.nikolailehbr.ink/blog/realistic-button-design-cs... shows an "old fashioned", 90's are back-in-vogue, 3d button.
Tailwind CSS for it becomes
<button
class="relative cursor-pointer overflow-hidden rounded-md border
border-neutral-950 bg-neutral-900 px-3 py-1.5 text-neutral-100
shadow-md inset-shadow-2xs inset-shadow-neutral-600 transition-all
before:absolute before:inset-0 before:bg-linear-to-b
before:from-white/20 before:to-transparent hover:bg-neutral-800
active:bg-neutral-950 active:shadow-none
active:inset-shadow-neutral-800">
After
</button>
I got eye-strain and headaches after taking over maintenance of a tailwind based website where the original developer had left the team. The class declarations are so huge with 15-30 class names in one line that you always forget where you are. Incidentally, also the top-voted discussion: https://github.com/tailwindlabs/tailwindcss/discussions/7763Now, you re-invented Tailwind... but in your own proprietary way that nobody else understands.
Tailwind takes the inverse approach. The example posted by the parent above is completely unambiguous - what you see is what you get, and it's done the same way everywhere Tailwind is used. You can read the component's styles and understand what it should look like without having to do a bunch of look-ups, or alter classes which might impact other areas.
So now instead of memorizing a bunch of custom classes like `pad-left-20` (because you didn't discover the `leftp-20` your colleague added 8 months ago)... you now have a standard way.
Can you tell me, without looking at the implementation, what effect `btn-atc-primary` applies when the user hovers? No... you cannot.
It's less important to have "clean" looking markup than it is to be unambiguous in intention and implementation. If you read a component that uses Tailwind classes, you know - without any doubt - what it does and what it looks like. That's pretty cool...
Bonus is that you actually know how to use CSS
(1) General CSS, global effect, like basic font size
(2) semantic components on my pages, like a special kind of list or something, that I give a well thought out name. These CSS definitions are always scoped, obviously to ".my-class-name something" selectors. They cannot affect anything that is not inside a semantic component. It is very simple.
(3) layouts/containers, that contain the components, like some flexbox or grid or something that behaves a certain way, which I also give a meaningful name
(4) A theme, a CSS file that contains CSS variables, which have values used in the other layers. Often when I need to change something, I only need to change my theme.
I do not understand, why web developers en mass are unable to cooperate with each other to develop their semantic units for CSS and then stick to those, instead of sprinkling stuff everywhere and using !important to make shoddy work. If they disagree about the semantic units, then that is the same problem as we have in any other software development arguing how to box things. Furthermore, the semantic units should be part of the design language of the business. The designer should realize "We have 3 kinds of buttons. Each has its own set of rules." and then what is easier than making 3 CSS classes? -- I am sorry, I do not understand where the difficulty is with just using CSS. Someone please, please explain to me, what is the problem in this day and age.
Eons ago I was at a job where we dynamically generated css on the server (php in that project). Anyone would have said: what about separation of concerns? Now everyone says it is best practice to have components with css, html and (gasp) Javascript all in one unit, because someone, for ex. Facebook said it (React). And the truth is it is very useful, and it is another way to structure frontend code.
My point is: it is a spectrum and it can be useful to change the styling right there in the browser inspector. That works with tailwind the same as css. And with components, you can hide the repetition, although the browser will still have the repeated classes. Is this better than having css on another file with maybe some unused classes? I have done both and I can see each has pros and cons.
https://chromewebstore.google.com/detail/atomic-css-devtools...
<button
class={cn(
// layout & positioning
"relative cursor-pointer overflow-hidden rounded-md",
// border & background
"border border-neutral-950 bg-neutral-900",
// padding & text color
"px-3 py-1.5 text-neutral-100",
// shadows
"shadow-md inset-shadow-2xs inset-shadow-neutral-600",
// transition
"transition-all",
// background gradient positioning
"before:absolute before:inset-0",
// background gradient
"bg-linear-to-b from-white/20 to-transparent",
// hover
"hover:bg-neutral-800"
// active
"active:bg-neutral-950 active:shadow-none active:inset-shadow-neutral-800"
// disabled
"disabled:opacity-50 disabled:cursor-not-allowed"
)}
>
After
</button>
<button style="css blob">After</button>
Why make a bunch of css classes when it looks to me like the "style" will be roughly the same amount of code and readability.You can also change styles through javascript if you really want to.
It just seems like we've lost the entire reason for css in the first place when every css attribute gets turned into it's own class.
I only sprinkle in the very occasional tailwind class via UnoCSS. I don't have the fire in me to dig trenches for either side of this battle that some people want to make all this into.
No, but they will change things like their constants ("bg-neutral" or, in this case, the spacing constant).
You’re neglecting the design system work that goes into tailwind classes. I don’t particularly like its choices (and generally consider tailwind to be a minimal local maximum for a set of tradeoffs deserving of faint praise), but they’re something, and in an engineering culture that won’t adopt a better mindset or better tooling, it’s something that solves a few problems well enough.
I feel like I've been nerd sniped trying to figure out what you mean by this.
I have ML engineer brain, so to me any small change from a local maximum is an IMPROVEMENT (because we try to minimize loss functions).
Your reading makes more sense.
I like to format in logical chunks per line, for example here's a pretty complex grid setup that is easy to adjust in the template:
----
<section className="
w-full
px-8 py-16
md:px-16
">
<div className="
grid
gap-2
auto-rows-[minmax(0px,1fr)]
grid-cols-[repeat(1,minmax(4rem,1fr))]
grid-rows-[repeat(1,minmax(4rem,1fr))]
md:grid-cols-[repeat(6,minmax(6rem,1fr))]
md:grid-rows-[repeat(3,minmax(6rem,1fr))]
xl:grid-cols-[repeat(8,minmax(6rem,1fr))]
xl:grid-rows-[repeat(2,minmax(2rem,1fr))]
justify-center
">
----
<RetroButton variant="primary" />
button {
position: relative;
cursor: pointer;
overflow: hidden;
border-radius: 0.375rem;
border: 1px solid #0a0a0a;
background-color: #171717;
padding: 6px 12px;
color: #f5f5f5;
box-shadow:
inset 0 1px #525252,
0 4px 6px -1px rgb(0 0 0 / 0.1),
0 2px 4px -2px rgb(0 0 0 / 0.1);
transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
button::before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2), transparent);
}
button:hover {
background-color: #262626;
}
button:active {
background-color: #0a0a0a;
box-shadow:
inset 0 1px #262626,
0 0 #0000;
}
Sure, separating the code into a CSS file means it's not in your HTML but that doesn't mean it doesn't exist. Similarly you can move the button out to a component and reference that if you want to hide the code.I find it easier to locate things in the HTML broken into components because the style is co located with the object being styled. I find it much more difficult to navigate through CSS and find out what classes are doing what exactly. Especially when a project grows over a long period of time and different people write CSS differently (and throw a few !important's in to make sure they can meet a deadline).
Each to their own though.
Anyway, to me this question is kind of like asking "Is it easier to break your writing into paragraphs, or write everything in one long block?"
Like, what would you think if I formatted my comment like this?
> It's not quite apples-to-apples because the Tailwind code is using design tokens and the CSS is not. You should (for example) replace the long `box-shadow` value with `var(--shadow-md)`. Anyway, to me this question is kind of like asking "Is it easier to break your writing into paragraphs, or write everything in one long block?" Like, what would you think if I formatted my comment like this?
Why aren't the companies building design tools solving this?
This is a "why don't they just make self-driving cars" question. The answer is that there are too many edge cases.
We're entering a phase where natural language becomes the main interface for html and css development. Companies like Vercel, Wix, and Framer are integrating AI to turn design prompts into working UI components.
This is only the beginning. Within 2 to 3 years, domain-specific language models, trained specifically on frontend code, are expected to become common.
Regarding Adobe, they never really understood developers.
Sure that html looks a bit messy, but once you write it once you're never looking at it again. In your view files you're just writing.
<Button>Click Me!</Button>
Or perhaps injecting variables etc. <Button color="red" style="outline">Click Me!</Button>
I'm not really trying to argue that Tailwind is better or worse, I'm just saying it's a valid way to do things and there's nothing inherently wrong or flawed with it.There are simply so many decisions when choosing how to implement CSS in a project—where should we put the files, should they be component based or global, what preprocessor if any, etc etc. With Tailwind, you don't have to worry about any of that.
2. syntax checking
3. language servers.
I think this is an L take. Tailwind is a solid replacement for 99% of the CSS you'll have to write. Its not a replacement for components. It makes components better.
Just wrap that monstrosity in a component; at that point I'm not sure why it matters. Have you ever looked at the component definition for an @mui/material button? Its a cthuluian insanity. The people who maintain that library likely need therapy every week just to remain functional humans. And, I thank them deeply for their service, because I so rarely have to look at those innards; they just work from the outside.
For a lot of pages, you're often only adding maybe 1 to 5 styles to each element to change some flexbox settings, add some margin, set a background color, or change a font. With the traditional CSS approach, you're forced to come up with class names for each element and put the styling into a separate file, which makes iterating on style and layout really laborious compared to Tailwind.
That's a much bigger benefit to focus on rather than looking at how (single element) buttons are styled. It's not like the CSS for styled buttons is less verbose or even pretty either.
Just use CSS modules. Just use CSS modules.
You specify like 4 approaches:
1. Tailwind - apparently awful 2. Regular stylesheets - also not good 3. Inline Styles - some flaws 4. CSS-in-JS - minimal flaws when using vanilla-extract
Reading through the article, my first thought was that you really like vanilla-extract. When I got to the conclusion section, and the first line is "I like vanilla-extract" I wrote the comment.
Nonetheless, even after finishing that section, without knowing better, I would assume that vanilla-extract is what I should be looking at further.
Since the style sheets are extracted into CSS and do not contribute to JS bundle size, I'm not sure visceral CSS-in-JS reaction is justified when it comes to vanilla-extract. Tailwind is still basically CSS-in-JS, but more of a hybrid of CSS-in-JS and CSS-in-JSON. Could you elaborate on your frustrations with CSS-in-JS as it pertains to vanilla-extract, the one CSS-in-JS library I recommend?
export const myStyle = style({
display: 'flex',
paddingTop: '3px'
});
I would much (much!) rather write normal CSS and have some build process extract the class name for me, like with CSS modules: https://github.com/css-modules/css-modulesIn the end its just another library that if you learn it well it will work for you and it has as many downsides as any other library ever used.
By the way the article seems a bit like an advertising for vanilla-extract library.
Use tailwind-merge and never worry about this again. No affiliation, just a happy user.
> Tailwind decided to burst on to the scene and become the default styling solution for all modern web development despite taking each and every one of these problems, exacerbating them, and applying them to places that didn't have them before
If Tailwind is so bad and has literally no upsides then how on earth it managed to take the frontend world like a storm? In authors eyes frontend developers have to be either stupid, or ignorant, or both if they decided to choose such a bad solution en masse.
To me (and probably many others) using Tailwind is all about speed and reducing file hopping. If I ever need to adjust style of anything it's always trivial - just locate the component and adjust the classes. Writing new code is so much faster because I don't need to think about what classname to invent for every single html element I would like to style, I don't need to create separate definitions, files etc. for them. For my particular workflow it's a huge win.
> If you misspell one of these plain strings your editor is not going to tell you. You'll just have to find out once you get into the browser. Did you notice that txt-blue is wrong, and it should actually be text-blue? No? Get used to it, you'll make that mistake daily (...)
This is just plain wrong. You can install Tailwind autocomplete, checks or eslint rules in literally any editor/IDE of your choice and you will never have an issue of misspelling the classes. Most solutions will even autocomplete your custom classes defined in your Tailwind config.
Please read the entire article before commenting. I have an entire section on this. I don't think frontend devs are idiots and never said they were. Tailwind offers global constants configuration out of the box, which is the most important problem people are looking to solve, which is why I think such a flawed library has so much success.
> To me (and probably many others) using Tailwind is all about speed and reducing file hopping. If I ever need to adjust style of anything it's always trivial - just locate the component and adjust the classes. Writing new code is so much faster because I don't need to think about what classname to invent for every single html element I would like to style, I don't need to create separate definitions, files etc. for them. For my particular workflow it's a huge win.
Part of my point is that this already exists with inline styles. In react for example you can set up some plain JS constants and import them to do inline styles. And you get the benefits of type checking and other JS features. That's not a perfect solution either, and it's not one I'd endorse past hobby projects, but my point is that Tailwind just takes an existing pattern and makes it worse.
> This is just plain wrong. You can install Tailwind autocomplete, checks or eslint rules in literally any editor/IDE of your choice and you will never have an issue of misspelling the classes. Most solutions will even autocomplete your custom classes defined in your Tailwind config.
These are okay but trip over themselves and cause issues if you have any plain CSS class usage, which is necessary in certain situations in my experience.
The best part? I can just tell AI what I want, and it spits out exactly what I want, even all the goofy framer-motion animations. If something's off, I can tweak it without ever opening a CSS file.
Massive respect to the devs behind tailwind. I don't care what your blog post says, you'll have to pry it from my cold, dead hands.
Claude can write CSS and HTML together in my templates, without needing to reference back and forth. It’s very, very good at writing tailwind.
Perhaps Tailwind is the worst of all your worlds, but it’s better than Bootstrap and Bulma and all the other stuff I genuinely tried.
I don’t get why people can’t just let Tailwind users be happy. Like, I’m not writing blog posts about how terrible it is to write HTML in JavaScript for React…I just don’t use it. Live and let live!
I don't use Tailwind but what usually happens is that it creates a great Tailwind mockup that I'm happy with, and then I tell it to port it to Bootstrap (which involves a mix of Bootstrap classes and custom CSS) and it always looks worse, heh.
So Tailwind + LLM support is pretty big advantage.
Then you can say "Tailwind4 has major changes, please review using context7" and it'll sort out some of that.
I dislike Tailwind and don't use it, but I wonder how many papercuts like this come down to just not knowing CSS very well.
I can now design.
As someone who spent 20+ years as a jack-of-all-trades / full-stack developer, specialising in back-end and database skills, this has largely been... confusing.
Before, I couldn't even make plain text work. Totally hopeless, I didn't have the eye for things.
Now though, now I help my kids lay out their homework to be more visually pleasing. It's bizarre.
(caveats: while I can put together a visually pleasing and consistent websites, I'm not saying that design is easy, nor that designers don't have talents way above my own. I view this more like an enthusiastic amateur at the piano rather than having become a concert pianist.)
I know of one other dev who's experienced the same. I'm keen to learn if there are more of us out there.
Whether it should be important is another question, but it's a simple fact that it is.
Is there any project using React that uses something other than Tailwind? It is the default for many projects, especially those that are new.
Aesthetically, it was well deserved. There is a reason why component libraries build on top of Tailwind.
But every time I touch the framework, I miss Bootstrap. Tailwind is an aesthetic masterpiece, but the code quality feels like a regression. BEM, Bootstrap, Material. Different beasts, even jQuery UI, but Tailwind would be something I might consider, not a masterpiece, judging by the code.
Many many words I've read trying to convince me why I shouldn't be having a good time using it, yet here I am more productive than ever thanks to it. Less experienced devs are by default funnelled into writing code that's easy to understand, only looking at one file, as opposed to people trying to do cute tricks where styles could be coming from anywhere in the project. It's SO much easier when the styles are on the component I'm looking at and I don't have to cross-reference between files. Plus people sticking to increments of 0.25rem instead of everyone using their own preferred units is huge.
When you work at a big company you can't expect everyone will write nice CSS. But Tailwind plays a huge part in making sure everyone writes something that's much more easier for the next person who has to read it.
> yet here I am more productive than ever thanks to it. i have first hand experience with most of the css frameworks. heck, even wrote a custom scss one at one point, but eventually there's a simplicity (admittedly to a fault at times) to tailwind that just makes you more productive.
> Plus people sticking to increments of 0.25rem ins this is another really good point that doesn't come through as much. Tailwind also does a fantastic job of picking the right defaults which 90% of the internet won't do.
from the OP's original piece
>> it demands the developer who installs it set up a config file that lays out all codebase-wide style constants: colors, margin sizes, fonts, border radii, etc.
... or importantly, it sets up a solid set of defaults.
- the styling is colocated with the markup - sensible defaults - avoids rule hierarchy/inheritance - minimal JS at runtime
Disadvantages:
- build step and configuration - dynamic styling complexity
I don't think that's a bad tradeoff. And we're talking about styling on the web, here. So there are no good solutions. But there is a bad solution and it's CSS-in-JS.
Tailwind can actually be decent for templating systems and it’s likely why it’s shipped with Phoenix for example.
But for React components, it’s definitely a step back from CSS-in-JS, which gave you style encapsulation out of the box. Not sure what’s up with the knee-jerk reaction against it in the comments. With vanilla-extract (mentioned in the OP) or PandaCSS, there is no runtime at all.
Just components that defined their structure and style, in a readable way, with less abstraction than Tailwind.
Here is the portion of the documentation which talks about this: https://tailwindcss.com/docs/functions-and-directives#apply-...
In my own project, I moved to Tailwind recently and found @apply to be very useful. I experiment with the inline styles and move them to a CSS class when (and if) needed. Additionally, the Tailwind CLI translates the states like hover: and active: as well. I personally have found it super convenient.
It's because, as ugly as a long line of inline classes can be, it's easy to know exactly what styles are being applied to an element. Especially when there are more than 1 or 2 devs writing styles.
Unless a project has really messed up, it is trivial to see what styles are being applied to any element.
It feels like I have to have a mental model of CSS, tailwind, React Native stylesheets and nativewind-specific conventions to actually accomplish anything. It's terrible.
I agree with the author that the only actually good part is having a single, easy-to-use list of global style constants.
Everything else sucks.
> There are only two kinds of languages: the ones people complain about and the ones nobody uses.
So yeah. Amen.
The latest Tailwind version lets you even style with data: attributes: https://tailwindcss.com/blog/tailwindcss-v3-2#data-attribute....
Using Tailwind I get a world-class CSS expert (Adam Wathan) helping me out. The actual raw CSS doesn't go nearly as far in the DevX direction.
Example: https://onejs.com/docs/web/tailwind#quick-example
Without TW, that snippet may need to take 3x more lines.
---
My major issue with TW at the moment is that I use TW in a non-browser environment (Unity), so TW3 is fine since I can tweak everything with JavaScript. TW4 shifts everything to CSS, gives zero workarounds, and my setup crumbles.
And in 5 years, I'll still be doing CSS, not slowed down one bit. My old projects will still work. My new projects will get out the door on time and budget. And the new front-end devs who met me during a back-end discussion meeting will continue to assume I don't know anything about front- end and will still pressure me to switch. And my team will still be called in to clean up after them when they fail to deliver.
"Did you really say bundler?" If you dont use a bundler this is a fair point. But I'm confident you do use one so adding tailwind is ~3 lines of code.
Tailwind literally only becomes usable for large scale applications once you have combined base tailwind, postcss, a number of tailwind plugins, custom themes, tailwind-variants, tailwind-merge, clsx/classnames, some custom written typescript typing tools, and a number of editor and linter plugins.
People here saying "It works well with AI" is such a wild take for so many reasons, not least of which is just straight up admitting that you don't mind your code being unreadable garbage because you aren't reading it anyway.
The `@apply` syntax glazing here is wild too - you're literally just writing CSS at that point. Just use vanilla CSS. Like that is all this is: ```css .select2-dropdown { @apply rounded-b-lg shadow-md; } .select2-search { @apply rounded border border-gray-300; } .select2-results__group { @apply text-lg font-bold text-gray-900; } ```
The only "good" thing about tailwind is that right now the rest of the ecosystem is woefully lacking for a number of reasons and tailwind has the benefit of being used by a ton of random projects so you can find tools and references to make it better. But it's not good.
As long as the job of "web developer" is about assembly-line delivery of features, it will continue to be strategic to choose tools that are worse for experts and better for amateurs.
export function MyButton({ className }) {
return <button className={clsx("bg-red-500", className)} />
}
<MyButton className="bg-green-500" />
But that doesn't really work, because ultimately we've just provided both the bg-red-500 and bg-green-500 classes, and we're leaving it up to the precedence rules in the browser CSS engine (which I'm sure are consistent, but that feels like a no thanks from me)So instead I end up doing something like:
export function MyButton({ bg = "bg-red-500" }) {
return <button className={bg} />
}
<MyButton className="bg-green-500" />
But that also feels unsatisfying, because while that prop is named 'bg' you could literally provide any tailwind class you want through it; there's no way to constrain it to only allow tailwind classes which are functionally interchangeable with bg-red-500.Anyone else run into this and have a nicer solution?
In Tailwind you could probably replicate this with custom variants or whatever they're called.
> I think the most important factor in Tailwind's success is that it does one thing very correctly: it demands the developer who installs it set up a config file that lays out all codebase-wide style constants: colors, margin sizes, fonts, border radii, etc. Writing individual styles that do not use a pre-configured constant from the config file is clunky in Tailwind. This is a good thing, an unironic win for Tailwind. More than anything else, this is what a large codebase with multiple frontend devs needs: a rigid set of global constants that everyone is strongly incentivized to use.
I’m still considering its merits, but it at least goes beyond the “How I learned to stop worrying and embrace tailwind’s standardized soup” vs “I can’t stand this, it goes against every organizing principle I’ve found useful” familiar dichotomy.
I have some ideas, like how the dead code elimination + granular but consistent classes lets you build novel components that are still consistent with your UI which might be essential for making a 3rd party component library work.
I've been using Bootstrap since it came out 15 years ago but it never developed a 3rd party component library. I assume because it doesn't have the same sort granular building blocks for building novel components.
Then again there are also component libraries like https://ui.mantine.dev/ that don't use Tailwind.
I sadly never really looked into these modern options since when I want to build something, the last thing I want to do is dick around with a whole new UI solution vs Bootstrap muscle memory.
https://adamwathan.me/css-utility-classes-and-separation-of-...
Everything is a tradeoff. I don't miss Tailwind in my day job, but I would certainly if I had to work on the type of project where I last successfully used it.
Except, it does now. Presumably because of the popularity of Tailwind, Bootstrap has all of these inline utility classes, and LLMs absolutely love to spam them across your code. It’s really annoying when you have a stylesheet with a few dozen well-scoped lines, and the LLM just starts bashing inline styles everywhere.
Most devs on this website work on aplications in big teams maintaining projects for years where css is component scoped and there is time and there are quality checks.
Now when you work in agency you come to a extremely visualy complex product website or magazine. You haven’t seen the codebase for a year (or never) and you need to change little thing and add new section to homepage… all without breaking any other part of the website. And while you have 5 hours budget.
In that context Tailwind is best middleground. It establishes system to follow and it is selfdocumenting. Everyone who can read tailwind instantly knows whats going on. And it is much better than inline styles because you can make it responsive and not get to specificity hell.
People here bash funtional css thinking that those who like it don’t understand css. I’ve worked both in agencies and on products and sorry writing css for a CRUD app forms is piece of cake compared to css for beautiful product site where every part is unique and you are required to have deep understanding of how browsers render so you can exploit that inverted position sticky so some ui cards align just right in performant way.
What i am saying is there are many kinds of websites and people who like Tailwind might just be in very different situation and sometimes they might even know way more css than those who hate it.
Why is "class spam" bad? Argument? Go read the CSS spec. There's nothing telling how developers/designers how they should abstract their classes.
High abstraction classes are, I'm convinced, a holdover of the early days of the web when everyone thought XML and XSLT was going to take over the world.
The great thing about functional CSS (especially Tailwind) is that I can see exactly which styles are being applied, immediately, anywhere I want in the DOM. I don't have to consult a separate stylesheet or refresh my browser. There's no context-switching and everything gets built 3-4x as fast.
hover:(bg-red-500 text-white font-bold)
and it compiles to separate classes.It's very configurable for better or worse, and the VSCode extension is better too.
Sometimes I use CSS-in-JS if a component system demands it, like Material. I don't mind it as much as others do, I like that there's no CSS concatenation weirdness. But otherwise I feel quite happy with a component file and a style file.
For Tailwind fans (or even detractors), what's the main benefit I'm missing?
1. Speed - you can get stuff done quickly without making problems globally.
2. Self documenting - both in visibility of html but also because of system/config it forces. People understand to use only values (spacing, sizes, colors) in the config or document that they had to add something and why.
3. CSS specificity is not issue.
4. Performance - straight simple classes are very performant. Complex selectors and pseudo classes can be surprisingly taxing without people realizing it. Thats how functional css started btw
structure and locality instead of style purity.
Traditional CSS gives you basically nothing in terms of hierarchy or organization. You end up writing long, awkward selectors like .checkout .summary .item-title .price, which still aren’t that readable. And if you’re tracking down why there’s 4px of padding instead of 6, good luck figuring out which of the five CSS files is messing with it.
Tailwind flips that. Putting the styles right on the element means the structure is obvious. You don't have to context-switch or go hunting through a bunch of files just to see what’s going on. It’s all right there.
Yeah, sometimes it gets verbose. Especially when you’ve got a div with 20+ classes. But when that happens, you’ve probably got something reusable anyway. And Tailwind actually shows you that. If you copy and paste the same set of classes to another element, you’ve just signaled that you’re repeating yourself. That’s your cue: take the extra 60 seconds and extract it into a component or class. With vanilla CSS, that signal is way more opaque. People end up not writing composable styles at all. Instead, they name every div, then name every element inside the div, just to avoid writing selectors like .component div h1 span—which is even harder to reason about than .component .title .icon.
And honestly, writing Tailwind is just faster. Typing px-4 is way easier than padding: 0 4px;, and I don’t have to stress over naming a class that won’t collide with anything else. Naming things sucks. Tailwind helps you skip that whole mess and just build.
Another bonus: Tailwind is easy to delete. You don't get tangled side effects or some random override breaking stuff in weird ways. Kill a class and you know exactly what changed because it’s right there in the markup.
"it does one thing very correctly"
... so there's at least one worse world then
If you use any component library, most of your CSS should be within the component library, not in your application. Components can have variants, such as primary and secondary buttons, for example. This adheres to the DRY principle. Your application can also have reusable components, which helps minimize the use of CSS and HTML. This adheres also to DRY. You can also build your own Component-Library.
You can apply the same programming principles and organizational patterns to HTML and CSS as you would to any other code. This becomes much easier if you separate the view from the rest of the application.
Even if we consider small codebases, the examples OP shows seem to ignore Zip compression and how it works, which is ironic for someone who is arguing about performance as if it could be measured only in bundle size…
Bytes transferred, sure, but that's not what OP is talking about
As someone that used Tailwind for years at this point, you're right about why its so popular. To me it was just a better version of Bootstrap. Its defaults look nice, its color choices are nice. I would also mention that their docs are top notch and Tailwind UI makes it really to just copy and paste some decent looking basic components.
As someone that spends most of their time on backend, I just don't really care that much about CSS. I think that's something that's lost with all the bikeshedding about it to me its similar to higher level language vs something closer to the metal. So when people say "just use CSS" its like someone telling me I should write in C rather than Ruby. No thanks, I don't have to - why should I?
I'm really suspicious about the performance concerns brought up in the article. Does the author think that long strings are really such a huge issue for the size of the JS bundle or processing time in the browser?
From my experience it's much easier to refactor the styling of a page using tailwind than modifying CSS. Needing to name a bunch of classes in HTML to reference in CSS adds a lot of obfuscation and it can be difficult to come up with those names. When the structure of the HTML changes the CSS inevitably breaks. I find that it's easier to keep the styling working when using tailwind.
I think that the value of keeping styling DRY is overblown. The author gives an example of some tabs that all use `font-medium` and how bad it is to need to modify all the uses at once. This seems like a non-issue to me. If you see the web page you would immediately see the issue if you forgot to change anything. Alternatively, you can still use `@apply` and classes if you don't want to repeat yourself. Even further you probably want to create reusable components anyways.
Tailwind is very easy to debug. I've never had an issue understanding why styling wasn't working when using it. In CSS I found that getting the styling right was a constant struggle.
The author mentions the issue of setting the text to red and blue in the same class attribute. He compared it to using `!important` in CSS, which is so far from the truth. You can immediately see the issue if the color isn't what you want. Inspecting the element in the browser would immediately show you that you set the text to multiple colors.
Tailwind solves a bunch of problems for me. Components are more self-contained. The class names are much easier to remember that the underlying CSS. The set of attributes you can use are focused to a small set of usable features. Animations are much easier. Responsive designs are easier to create. Flexbox (the most useful part of CSS) is easier to use. I work with a number of older developers (very smart folks that just haven't done web development) and I have a much much easier time getting them proficient with tailwind than CSS.
I strongly believe that tailwind is better for individuals, better for teams, better for beginners, easier to set up, and easier to maintain. If you're already a CSS expert or if you have an existing design system I can imagine that you wouldn't want to pick up tailwind. But for most websites and most teams I expect tailwind is a great option.
Just wrong. The whole point of 1:1 style:class mappings is that you don't have hidden structures relating the DOM to your stylesheet's class abstractions.
If you're going to talk overhead, talk about all the abstractions and hidden structures you have to learn in a codebase written in the "correct" way to do CSS. It's just incomparable.
Tailwind's class names are trivial to learn for anyone that already understands CSS. It's minimal overhead compared to learning CSS. "Learning it twice" is completely inaccurate.
So, I truly agree with this blog post, and I even wanted to rant the same things showing that you should not compare tailwind against classic CSS stylesheets from 2010, but against how different solutions looks like using different tools the community seems to have thrown away or forgot.
And I know is not relevant to your comment, but I will add that "but you will have to maintain more files instead of just a single HTML file" is not a valid argument for me. If that were something good, then just put everything into a single giant server.<your_language> file + separated lib dependencies.
We want maintainable easy to follow and understand files. Having a clean html with no cluttering + intuitive CSS styles being applied where we expect them and looking exactly as we expect them while reading the html, is the big win for me, and Tailwind definitely does a step back on this, especially when other tools can achieve that. (Lastly, we have set multiple projects with different tools, and the amount of "wtf"s we've seen with tailwind is way higher than just styled components to give one example)
I agree that the only good thing Tailwind achieves, is setting a default convention on having a single config file where you can set everything that you will need on every design across all your html files
If it did, we wouldn't have 15 years of Sass and Less and Stylus and CSS Modules and Styled Components and BEM and SMACSS and Tailwind. I have been through it all and Tailwind is the easy winner both for productivity and maintenance over 5 years.
I sort my cutlery by knife/fork/spoon, not handles and implements.
> (Tailwind) demands the developer who installs it set up a config file that lays out all codebase-wide style constants
> I believe this is where Tailwind has succeeded and other libraries have struggled.
This isn't why Tailwind has "succeeded," in my experience.
Until Sonnet 3.5, LLMs were pretty awful at creating any sort of UI with normal CSS, but were partially usable with Tailwind due to the colocation of styles with elements. Even with Sonnet 3.5+, Tailwind seems to perform better than other CSS solutions (though the gap is getting smaller with each new model release).
The author calls this out, but with the wrong conclusion:
> Lastly, Tailwind has also been buoyed by being by being the default styling that just about any LLM or vibe coding tool will produce [..] Plenty of developers started using jQuery because the first StackOverflow result in their search explained how to solve that problem with jQuery.
Maybe the reason jQuery was so popular wasn't due to recommendations on StackOverflow, but it's prevalence on StackOverflow was due how easy it was to adopt?
Tailwind has "succeeded" for the same reasons it's excelled with LLMs:
- colocation of styles with elements
- decent defaults (i.e. bg-sky-600, font-bold)
- simple solutions to common challenges:
- responsive design (lg:flex-row)
- dark mode (dark:text-white)
- conditional state (hover:bg-red-600 focus:outline-violet-500
Obviously, Tailwind, just like other CSS-in-JS solutions has it's problems/limits, but it's easy enough to fall back to normal CSS for these one-off solutions.I now use Tailwind everyday, and it's no longer the worst thing in the world.
Cursor IDE uses those navy-100, red-100 absolute colors, instead of using text-destructive, etc (for light/dark mode support), and it is also difficult to tell it does otherwise. So I need to fix this manually, and it is quite time-consuming.
jackdh•7h ago
There is also nothing stopping you mixing and matching.
pier25•7h ago
Yes, common sense. Having multiple approaches to solve the same thing tends to be a bad idea.
hugeBirb•7h ago
pier25•1h ago
jackdh•6h ago
And yes while it's obviously not ideal to have a hundred competing libraries in your code, you can create what works for you / your team.
pier25•2h ago
Huh? Tons of people write vanilla CSS which is getting better every year.