9dev
9 hours ago
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:
<p class="text-red-500 text-green-500">I am some text</p>
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`._heimdall
5 hours ago
I've never found tailwind to help someone who isn't already a reasonably skilled web developer. If you don't know CSS tailwind gets your far enough sometimes, but you have to learn many of the same paradigms as you would in CSS and you can still create issues that aren't as simple as missing "text-green-500".
Tailwind seems most useful for devs who don't want to be responsible for managing multiple CSS files and all the naming conventions that come along with it. They're basically looking for using inline styles after having likely heard for a long time to not use the style tag.
For me at least, any value from tailwind went out the window when frameworks started leaning into single file components and scoped styles. I keep components small enough that generally my style selectors are only using element and attribute selectors, I dodge the class naming issue entirely and still get to use CSS instead of inline styles.
thiht
30 minutes ago
Well yeah, people need to learn the tools they’re using. How’s that shocking?
bko
7 hours ago
> If you end up programmatically layering class names, you're looking at a code smell.
Why wouldn't you? Suppose you have Button class with `btn btn-primary`. The Button accepts className override so you can do <Button className='btn-secondary'>. In this case you use tailwind-merge or clsx. Your Button implementation would be clsx('btn btn-primary', className), in which case right most class name prevails.
Other than that, I agree tailwind is great. It lets you keep things all in one place. Names aren't particularly difficult to memorize and reason about and autocomplete does a great job. Build utility classes or proper components to encapsulate the styling and you're fine. What benefit do I have going to a css file to review my button styling over the actual Button component?
DanielHB
6 hours ago
There is this implicit debate in web application development of cascading+overriding vs non-cascading-no-overriding. A lot of people don't even realize this is where the line is drawn and mix both together. I call them "component-based", "document-based" and "mixed".
Component-based applications are usually better of with no styling cascading and no styling overriding. It makes things significantly easier because components are self contained and don't change in appearance based on styling of parent-elements on the tree (besides physical dimensions available and transparency effects of course).
Document-based applications are usually better of with cascading and overriding. Although no-cascading-no-overriding can work too, but requires more code.
Component-based is quite difficult to do without some tooling (like tailwind or css-modules, etc). Most CSS tooling support both though.
Tailwind implicitly forces you into the component-based paradigm but never explains it properly. There is a reason why people who love tailwind say it just makes things much more manageable. It is mostly because it removes the "mixed" mode that projects silently fall into[1] unless there is some guiding force towards component-based.
However component-based approach is quite doable with many different CSS solutions. Tailwind just _forces_ you into it.
For example CSS-modules component-based just means one .css file per component with only .className {} CSS selectors and not relying on cascading values from above in the tree (a few global rules can solve this).
[1]: Mixed mode as I call it is mixing hyper-local rules with cascading rules. They fall into this mode because that is how plain CSS works without extra tooling and guidelines.
marcus_cemes
9 hours ago
I can see this argument both ways. On the one hand, logically you'd expect the last assignment to take precedence, like the style attribute. On the other, conflicting styles don't make sense.
The random outcome, entirely dependent on the Tailwind generation internals, is the worst of all worlds though, it's just an unfortunate side-effect of relying on cascading sheets to drive atomic styles.
yurishimo
9 hours ago
It’s random sure, but it’s also reliably random. This is one of the few footguns that exist in tailwind and luckily it’s super easy to catch. The IDE plugins will give you a warning if you apply multiple classes that manipulate the same attribute and there is no reason you also can’t catch it in CI.
Is it ideal? I guess not, but there are a lot of weird gotchas and footguns in CSS too. Just because they’re in the language doesn’t make them magically more or less of a problem. For a similar weird mental shift, consider adding @starting-style and the native popover open attribute. All the good tutorials online have a warning about cascade order because as a human reading it, it can seem wrong or backwards.
Kevin Powell’s most recent video about animating display: none covers it if you want a concrete example.
andrewingram
6 hours ago
> conflicting styles don't make sense
Sometimes true, but one issue here is that because Tailwind utility classes vary from being a 1:1 mapping to a single underlying style rule, to mapping to several, it's not always obvious which classes will conflict.
dzonga
6 hours ago
author of the article is making a logical argument - in matters of human behavior. that never works.
author should've instead asked himself & the world - why people are drawn to tailwind. yeah technically it's inferior - but in terms of being optimized for how people do things - tailwind is superior.
zelphirkalt
8 hours ago
I wouldn't even write such a class name. It smells like bad styling and layouting already. A CSS class should convey some semantic meaning. I would name it after the thing that should be red or green, not "red"/"green". That doesn't tell me anything. Maybe its name could be something like "danger" or "active" or something. Also the 500 looks very sus. Responsive design is best when it avoids such hardcoded numbers and depends on its content and dynamic viewport width calculations, and perhaps a few minimum sizes, if necessary to decide when things float, wrap, shrink, grow, etc.
It's a red flag (ha).
francislavoie
8 hours ago
Absolutely not. Semantic classes are the wrong way to go. Your components (and their props API) are what encode the semantics. With Tailwind you never need to try to come up with a name for anything relating to style, you just use utility classes. It reduces cognitive load significantly.
drdaeman
6 hours ago
And so we’re essentially back to <font color=red size=10>?
guessmyname
9 hours ago
The fact that “text-red-500 text-green-500” resolves by stylesheet source order rather than anything visible in the markup means the “locality of behavior” promise quietly breaks exactly when you compose components dynamically, which is why tailwind-merge exists at all, and needing a runtime dependency to answer “which of my two classes wins?” is a real design smell, not a bikeshed.
9dev
8 hours ago
That is kind of my point, though—you don't need tailwind-merge, really. There are two cases it solves:
One, adding classes from the outside to an encapsulated component with its own, internal classes, to make sure the outside-applied classes take priority. Either use the !important modifier on them (`ms-auto!`), put the components into a container div with the classes for layout concerns, or even better: Figure out why you need to make styling changes to a component that cannot be expressed via props. I would generally recommend components to not have outside-element styling like margins in them anyway, which most often fights with positioning later.
Two, merging prop-derived styling with base styles - for example for a `size: 'sm'|'lg'` prop. It's tempting to just use tailwind-merge here:
const classList = tailwindMerge(
'p-4',
size == 'sm' && 'p-2',
size == 'lg' && 'p-6',
);
But that isn't necessary at all: The better alternative would be to use data attributes for visual concerns and built-in or aria attributes for interaction states. There are almost always element attributes that can represent what you want to have correctly, and data- where there are not. Then, you can just add a class accordingly: <span
class="p-4 data-[size=sm]:p-2 data-[size=lg]:p-6 data-active:font-bold"
data-size={size}
data-active={active}
/>
And you'll end up with easier to maintain components that derive their styles from the CSS cascade alone.francislavoie
8 hours ago
I would reach for class-variance-authority (aka cva) instead for stuff like a size prop, it declaratively solves that.
9dev
7 hours ago
Interesting, I didn't know that project yet! I'll give it a thorough read.
gf000
9 hours ago
It's not true - agents will visibly struggle more with worse code bases. If anything, certain kind of maintainability matters more in this era, than before, because it is so much cheaper to just generate a huge amount of low quality code.
SebastianKra
7 hours ago
I agree that all of this is bikeshedding, but the cascade complaint is actually real.
It's so common, that there is an expensive runtime package, `tailwind-merge`, that is used by default in the most popular component library.
simonsarris
2 hours ago
also note that `class="text-red-500 text-green-500"` gets flagged immediately by the Tailwind VS Code plugin
> 'text-green-500' applies the same CSS properties as 'text-red-500'. tailwindcss(cssConflict)
So even if you do it by accident, it will tell you.
nailer
2 hours ago
> You can onboard new developers to it, able to contribute productively immediately
Maybe it helps with people nee to Web development but as an existing CSS developer tailwind slows me down incredibly.
I know exactly what styling I wish to apply, I now have to translate it into another language to apply it.
And the browser inspector looks terrible because every element has a long bunch of utility classes added to it.
boxed
8 hours ago
Projects with a single gigantic CSS file also work and you can onboard people etc. That's not an argument.
9dev
7 hours ago
they do work… for any value of work. I have been in the industry for a while, and I've never seen a project which doesn't slowly devolve into fights with the cascade over time - arbitrary abstraction layers, ad-hoc organization patterns, developer-specific conventions, inconsistent naming, and so on. All of this usually creeps in the more a project grows. At some point, someone adds postcss or scss or another preprocessor and starts splitting files, and then a whole new set of new, made-as-you-go conventions comes in: Files per component or per view? Or rather per architecture concern? Usually a mix of them. As files are split, the class naming gets even more inconsistent.
And at that point, onboarding a new developer always means wasted time on understanding this whole organically grown set of bespoke conventions, class names, patterns, hierarchies, and so on.
Tailwind does not really have this pattern of deterioration growing along both time and complexity, it stays consistent on both axes. So it's definitely an argument IMHO.
atif089
6 hours ago
> I have been in the industry for a while
> Out of curiosity how long have you been doing frontend development?
I've been writing CSS since IE 6 and I can tell you not a lot of us oldies like tailwind. I've only seen a strong inclination towards Tailwind from the newer generation frontends (2015 and beyond)
Us oldies actually prefer BEM over anything else.
My talking point is based off internal comms from a large'ish company (200+ FE devs) so YMMV
greggh
5 hours ago
Many of us oldies who started working on the web years before IE 6 actually like Tailwind. We all have our preferences. But I know multiple 50+ year olds who have been doing this work since CSS was barely a proposal who now use Tailwind.
FireBeyond
31 minutes ago
Hah, yeah, there's always anecdotes. I stopped doing frontend a decade or more ago (and even then it had been a smaller and smaller part of my day). But I see the appeal of it. I started front end when we had to buy testing desktops that were beefier so they could run multiple VMware VMs, one with IE3, 4, 5, 5.5.
I remember the delight that our designers had when Javascript came out and my buddy showed that you could do some magic on images and form elements and have an actual checkbox with a check in it, not just a filled or empty box or circle. Or of the dev who thought he'd impress a girl on IRC by updating a prominent site's homepage to have a "Hi Sara" underneath the banner because he could just ftp to prod from his desktop.
crab_galaxy
6 hours ago
Tailwind trades cascade complexity for design drift when working across teams. You can’t ignore that it encourages a different kind of undisciplined sloppiness.
Would I take it over the 1000 line cascade problems? Probably, but I’d still prefer css modules over tailwind.
FireBeyond
38 minutes ago
The "rounding" was grasping at straws:
> How round is rounded-lg?
Author: "In my vanilla CSS, I can read it in the CSS file. In Tailwind, who knows?!?"
Reality: You read it from location 2, not from location 1. The horror.
I don't even have a dog in the race. I'm past my days of handcrafting HTML and CSS. I have a few Claude Coded apps that use Tailwind, but I've not looked at any of the files (yes yes, I know. They're personal projects that run in my homelab. I don't care.)