Hacker News new | past | comments | ask | show | jobs | submit login
Tailwind and the death of web craftsmanship (pdx.su)
79 points by paradox460 on Aug 2, 2023 | hide | past | favorite | 112 comments



Adopting any system of coding design whether it be Bootstarp, Tailwind, Rails, Express, etc. is overall meant to reduce the cognitive complexity of the group working on your solution.

If most people have exposure/understanding to the Boostrap way of doing things, or the Rails way of doing things, then we as a system/company/team/organization benefit when new people join the team. There's already a well established way of getting stuff done.

If we forgo the use of a system, we will end up building one anyways. But this time, it will be known ONLY to your existing team members. It could/will buck convention and established best practices, it will grow to develop the bloat and technical debt for which the existing systems were rejected.

Software engineering is about trade-offs. This is just another trade-off.


But one of the major pitfalls of tailwind is that it gives the illusion of a system, but isn't actually a consistent system. `m-2` can mean entirely different things, depending on how your configuration is set.

With a really good design team, this isn't a problem, as the numbers are just pointing to something in a design document, but I've yet to see something that well done in the wild. And its not really that hard to make such a design system using css or preprocessor variables


> `m-2` can mean entirely different things, depending on how your configuration is set

Does it matter? Putting aside that anecdotally it seems very rare to override the default values for spacing anyway, m-2 is twice as much as m-1, which is whatever the baseline spacing for your project is.

Sure it can vary from project to project (though again, it doesn't seem awfully common for folks to do), but the config file is in your source control so m-2 is always consistent for the project you're working on.


It is super common things to do when you actually adopt it as system. People choose either -4 or -8 to be 1x measure of space and setup the size in config.

Of course there is a big group of devs who use tailwind for easy copy paste templates that never change the config. But those who use as system use config a lot.


I'd argue that if you use Tailwind as is, without creating a system of configuration around it, you're team issue isn't Tailwind. I fully understand what it's like to have a team that doesn't understand build tools, but you really shouldn't be allowing these people to prepare deployments by themselves. I think that's totally "ok" in our current web culture, there's no more looking at source code on a website and figuring things out that easily. It can become annoying to have someone come to you for something that should be so basic (in our minds), but I guarantee it's way less annoying that being called upon to fix production! I'm not even a front-end developer, I just kind of fell into the role because I know code and started in the late 1990's, and then picked it back up in the early 2000's. I get why CSS is difficult, but I think the patches over the language by libraries is still not making things easier.


The thing is you have to use build tools to use Tailwind. It doesn't work (well) without it being able to search your templates to generate css for the classes you use.

So anyone using Tailwind already has the setup to add tailwind.config.js into their project. They make it as easy as it can possibly be.

The reason why people don't change the config is that you need the default tailwind config for all the copy-paste-able snippets to work. People buy something like TailwindUI and copy what they find around. Including things like colors (which i find super stupid that the TailwindUI team didn't you know... use their own config OR css variables to be able to change brand colors).

And that kind of usage of Tailwind is lazy and you won't learn anything. But that's up to people. It doesn't mean Tailwind is bad abstraction or not possible to use for efficient design systems.


Just because a 2 can represent different values doesn’t mean there’s not a system, it just means the system is configurable. This is a good thing, you just have to know how the tool works.

Even if I don’t know what specific values m-2 may hold for a given project, I certainly know a lot more about it right off the bat than whatever .class-name you may have come up with from scratch, and can reference the config to know the rest.


You can configure it to use names like `m-xs`, `m-sm`, `m-md`, `m-lg`, etc if you want, that's the beauty of it. It lets you create your own design system.


That's true, but I don't the author is saying that Tailwind isn't a trade-off — just that it's a bad trade-off.


How do you guys feel about tailwind? I’ve been using it on some side projects. It feels ok, but I’m constantly looking up stuff in the tailwind docs. CSS I already am pretty confortable with. I think it’s an improvement, but not by as much as I thought.


I've been using it at work for just over a year. I don't like it. I still need to constantly reference the Tailwind docs for most non-trivial properties. It flat-out doesn't support some things I need. It produces giant blocks of difficult-to-scan HTML. Chunks of common styles end up repeated. The problems it purports to solve are better solved in other ways, such as CSS modules.

Ultimately, I don't begrudge people for using whatever tool they find best. But I try to avoid Tailwind when given the choice.


> It flat-out doesn't support some things I need

Can you share some of those? I don't do much raw CSS so have no idea where it has gaps.


One example is rotation along axes other than Z, which is required for 3D transforms. Here's a two year old issue on it: https://github.com/tailwindlabs/tailwindcss/discussions/3521

Another is attribute selector support. They have many common ones, but to do anything more complex you need to add support to your configuration. https://tailwindcss.com/docs/hover-focus-and-other-states#at...

I don't want to oversell this; I think Tailwind does a pretty good job covering the "happy path", and if you haven't run into any gaps I wouldn't worry about it too much. But you'll encounter its limits well before you encounter the limits of CSS.


Nah, it's not an improvement for me.

It's an abstraction, but a poor one. It's too low-level to be truly useful, and it's also confusing as some properties doesn't mean the same as in CSS.

You end up with huge unreadable blobs, reminding me of the "write-only" label that Perl once used to have.

And the answer to avoid that unreadable blob is to include CSS, defeating the point.

I prefer CSS/SASS with variables for customization (so you don't have to repeat the blobs) and well-chosen class names (so you can actually read and modify existing code).


I was quite hesitant to jump into Tailwind. I've tried it in the past few months and quite like using it. I do have to refer to the docs a lot but that is okay because I think they're docs are quite usable.

I miss some of the already established components from Bootstrap, but whatever. Bootstrap is always kind of a pain in the butt to integrate into a project.


Tailwind is the greatest thing to ever happen to CSS in my entire history of using it (for me, personally).

It's like being free from 1000's of levels of mundane resistance. It feels like going from writing assembly to some higher level language.

CSS finally GETS THE FUCK OUT OF THE WAY and lets me design as fast as I can think.

No project I have ever built, even the most "designey" have come even 10% as bloated as that input example he gave, so I have no idea what that is.

Nearly everything in this article I couldn't disagree more with. Maintaining tailwind (note GIVEN that you are using a SPA/JS framework, thats key) is one of the easiest things in the world.

I have complex layered apps with all sorts of directional changes, feature additions/removals, and its just as easy to modify this CSS without a hair tangle like traditional CSS or even CSS modules (albeit CSS modules are quite nice)

In cases where theres repetition within a single component, I will combine CSS modules + tailwind using @apply.

> There's been a rapid deterioration in pride-of-craftsmanship in development. It's naive to believe that "back in the old day" everyone wrote everything with a perfect eye towards beautiful craftsmanship, and now we just push code out as fast as we can. I remember having to use ugly CSS hacks to get IE to render things properly, or float clears to get containers not to shrink under an inline image. But there was definitely more interest in "doing it right" rather than dismissing it as a problem that wasn't worth solving.

Speak for yourself man - these are all unsubstantiated claims. I see absolutely nothing of the sort with my peers. For me personally, I care MORE about craftmanship than I ever have when I have brilliant tools that allow my input->output energy efficiency to reach nearly what feels like 100%. With Neovim + Vue/react + Tailwind + Room-temp superconductors I can design the most complex brilliant designs and systems, where I will launch entire features as a single developer, caring about every miniscule pixel perfect detail, all the backend coding, all the wiring together, the i18n, the 1px layout shift of a dropdown on android, the translations, EVERYTHING, and you will still be writing CSS and whining about "craftsmanship" having imaginary conversations in your own mind that doesnt in any way reflect reality


Hear me out because I thought I HATED tailwind, but have come to reconsider.

What got me to convert to using Tailwind on projects (after a lot of reluctance) was styling variants. How to handle edge cases in your CSS empire.

For modern CSS practices, he gives a good example in the article of creating a button component with scoped styles. At first, that seems to work really well. But now use that button in unique and unforeseen location where it really needs left margin. There are SO MANY little scenarios like that which will creep up in actual usage.

You could target the button via id or perhaps a class on the parent, but making adjustments that way is not really fun, maintainable, or repeatable.

So then the next step is creating a few utility classes so you can easily apply a bit of margin, padding, etc. But now you have to decide what stays in the .button class and what properties to make flexible via added utilities. Pounding your head on the table brings you to the same conclusion Adam (the Tailwind creator) reached: it sounds crazy, but may I could only use utility classes...

Tailwind is certainly verbose. But it's very readable, explicit, and easy to debug. Wait... why is this button blue? Ah, I see right here in context... because it has bg-blue-600 applied.

The cascade is powerful but also tricky... it can be hard to determine where and when across global and local style that a piece of html picked up a certain color, size, or property.


yeah. "web craftsmanship" here seems code for "code style the way i like is Good, everything else, including beginners and hackers just trying to ship fast, is Bad".

in other words, gatekeeping.


Tailwind doesn't seem like a good fit for me (although Tachyons looks interesting!).

I don't think it's inferior or superior, just a better approach for certain people.

Personally I've found Bulma to work well in conjunction with Django.


IMO Tailwind only really makes sense if you're using a component-based framework. Trying to adopt it in a vanilla Django app (with monolithic templates) will lead to lots of duplication.


Can you expand on why that would be?


Not who you replied to, but it's basically how tailwind works. The magic of tailwind comes from a few things:

EVERY property must be explicitly specified. There is no "cascading" (inheritance). At first glance, this might seem like a horrible idea, but it has a TREMENDOUS upside in that everything is explicitly defined in the clearest possible way. (the style is directly ON that exact element. if you have a div inside another div, that inner div will not inherit font, colors, etc. You must define it all again).

The benefit of this is that everything can be "componentized" or split apart, and reordered, and moved around, and nothing will "break" stylistically. Your styles literally are perfectly attached to the structure. This is because EVERY single element is decoupled from EVERY other element, stylistically. This is truly the 100% stark opposite of traditional CSS.

A good example of this... is the very first time I googled a tailwind component, for some dropdown or something, I pasted the HTML + CSS classes directly into my project in some arbitrary spot, refreshed, and it looked PERFECT. I was literally floored and stunned! If you have programmed wordpress or other sites back in the day, and you know the true nightmare of trying to put some arbitrary bit of css or some script into your own projects and the styles are all conflicting, thats what I'm talking about. You know that pain. Even my BEST projects back in the day where I SWEAR to myself "im going to make THIS project the perfect CSS structure", ... after about a month i would be once again caught in these complex awful webs of conflicting and overwriting classes, despite my very best efforts.

Now there are downsides to this as well, but they're mitigated by using a component framework:

First, Because everything must be explicitly defined in EVERY place, this obviously creates a lot of duplication. After all, this is the entire point of CSS classes. Lets say you have a shopping cart item in one area of the app, the main listing, and then you want to show it again in your shopping cart. Youd have to define those style classes twice using tailwind if youre using a standard app which has "layout files" and "views". Now a component based framework though means you can make a component called <ProductItem /> and define the classes ONCE in that component. In this sense, a component almost becomes a much more advanced replacement for a CSS class (in a sort of weird and twisted way).

Lets take it further, and say WITHIN the product item is some text that looks like this: "Reviews (5) [NEW]!" And its styled a certain way. Now lets say that same style text appears below a different type of "review" and not a product item. In this sense, you can't use <ProductItem />, but you could make a new component called <ItemMetaInfo /> and then write your class in there (once), and extract that out to a component. There is a sweet spot to extracting out components vs a slight amount of duplication (say...5%). This also aligns VERY nicely with the entire purpose of component based frameworks is that the complex logic lives within the component... so lets say your <ItemMetaInfo /> grows in complexity over time, maybe it shows literal star SVGs, or supports multiple A | B | C sections based on an input parameter. That complexity can live in that new component along with the tailwind classes, defined once.

The second downside is its a LOT of typing, and this is where tailwind really excels to me. The properties you type are a god send, and SO much faster, easier to remember, and everything. This is coming from a vim freak who has memorized and written thousands of hotkeys and macros over nearly 15 years for CSS, and 100's of projects over the years. With tailwind, you do NOT need editor completion or anything like that. Its SO fast that it feels "effortless" in terms of the "ugh" resistance from writing CSS.

The best example of tailwind is right on their homepage: https://tailwindcss.com/

Scroll down about 100px and watch the realtime demo of them adding css classes and watch the design change on the left. It really IS THAT FAST to write tailwind classes and design THAT quickly, including responsiveness, and everything. This is 1000% not the same as css even with the very best macro/autocomplete setup.


> everything is explicitly defined in the clearest possible way. (the style is directly ON that exact element. if you have a div inside another div, that inner div will not inherit font, colors, etc. You must define it all again).

Except this isn't the case if you ever touch any of the spacing[0] parts of tailwind. Then you'll suddenly have your elements growing margins out of nowhere, seemingly, and you once again have to look at the ancestry of an element to figure out why its being laid out the way it is.

[0]: https://tailwindcss.com/docs/space


Hey, yes, I know about space. There's also hover-group, etc.

Those seem to ignore the spirit of what I'm saying though, as firstly, no one says you have to use them if you truly want 100% decoupling. They're just convenience functions.

If you want to control margins based on component indexes and only use margin, then great.

Secondly, they're quite simple to understand.

They're also not out of nowhere, they're out of what you explicitly define as say, space-x-4. Having this be on the parent just makes great intuitive sense, that it wouldn't be defined on the individual items. If I see items evenly spaced I don't even have to think about it that it's obviously on the parent. The mental overhead of this is truly negligible.

I would argue that "spacing growing out of nowhere" is an order of magnitude more painful when using traditional CSS that has grown beyond the "perfect" starter phase of every complex app that uses normal CSS. And I've even tried using all the different CSS frameworks from back in the day where you write classes in certain ways with prefixes, etc etc. It is inevitable that eventually you get so much tangled and dead code, the mental overhead of that is now something orders of magnitude more.

While we are talking about that, you mentioned it being bloated. Your argument is that it takes "CPU" cycles... I forget the exact article (and exact details, paraphrasing), maybe it was github. Classes outperform inherited CSS tremendously, and I think it was github that tried to design their site using 10's of thousands of 1-level-deep classes instead of nested. This is just nitpicking, however. With tree shaking, I have an entire app that sees millions of visitors per month get a 3kb css file when gzipped. The bulk of the js (compiled html) takes up < 100kb for the entire app. I don't think even the most "lightweight" wordpress theme or what have you comes close to that. I just googled "most lightweight high performing wordpress themes" and some blog spam has most of them weighing in at 900kb-1mb (!!!).


Thanks for that amazing in-depth explanation! I can see why people would prefer it for component based frameworks.


I second everything here and wish more people would actually work on complex projects with multiple maintainers before they rush to judge Tailwind based on arbitrary norms from the Netscape-era web.


I think you hit the nail on the head with the "higher level language" comment.

There's still value to being able to write at lower levels of language. Always has been, always will be. But having the higher-level languages lets you move a lot more rapidly, so long as the abstractions it makes for speed align with your goals.

For a lot of people, Tailwind's abstractions very much align.


I am seeing a lot of my former self in these comments.

I'd say this: Do what works for you and your team in the moment.

In 2023, I am a super hard-core vanilla-only web developer. My websites typically work without javascript and are completely rendered before they leave the server. I parade around HN advertising MDN as the Bible. Google is evil and Firefox is our savior. This stuff feels like religion to me.

Back in 2010 when I was responsible for the first website I was ever being paid real money to work on, I reached for Angular 1 and then happily upgraded to Angular 2 when it was time. Used bootstrap CSS for everything (aka the original back in 2011). I didn't stop using bootstrap CSS until ~2020. That was the last 3rd party item I dropped from my tool belt. RiotJS was the last JS framework I ever used and it got kicked to the curb some time around when Blazor was released.

10+ years is about how long it took me to completely lose my webdev training wheels. I think in many contexts (i.e. working with juniors and large teams), you are still going to want some sort of structure even if you personally don't need it. In our shop, I decided to establish and maintain that structure in-house. Our team is small and willing enough. In a larger shop, you will likely fail at DIY frameworks but I'd be the last one to say you shouldn't try. If you do succeed, nothing can compare.


I agree that it's really important to periodically check the pulse of where your team is at for stuff like this. I think part of the reason I lead the team I do is because I was encouraged to lean very hard into vanilla JS at the outset of my career and use tools sparingly. We're not nearly as "pure" as you state here, but I've been able to apply that experience to working with my team when it comes to discussing what we allow in and coaching them. It makes a big difference.


My experience with Tailwind detractors has been of people starting with their opinion that Tailwind is bad and then working backwards to come up with almost entirely arbitrary and contrived reasons to justify that conclusion. I truly can't believe that anybody who has completely learned Tailwind could posit the argument that Tailwind isn't any faster than writing pure CSS void of emotional bias. Whenever I see someone make such a statement, I can tell it's more of a propaganda piece than a piece meant to spawn logical thought.

To not leave this comment with simply a hand-waving disregard of this post, let me say this: Nobody who knows what they are doing writes Tailwind styles as long as the ones presented in this post, nobody uses arbitrary styles or custom styles as many times as is presented in this post, and yes (in a shock to nobody)- it is quicker to write "m-2" than write ".[my unique, non conflicting class name (usually incredibly arbitrary to avoid conflicts)]{ margin: 10px };" and then implement that in code. Usually, when you try to write pure CSS efficiently, you just end up basically duplicating Tailwind by creating utility classes anyways, except minus all of the actual perks of JIT.

At this point in time, if you say that you see no benefit in using Tailwind, you are being obtuse.


> I truly can't believe that anybody who has completely learned Tailwind could posit the argument that Tailwind isn't any faster than writing pure CSS void of emotional bias. Whenever I see someone make such a statement, I can tell it's more of a propaganda piece than a piece meant to spawn logical thought.

I truly mean no disrespect by this, but have you considered that other people might be better than you at writing CSS? Because what you are describing is absolutely not a universal experience.


I have certainly considered that, but I don't understand how writing out ".[my unique, non conflicting class name (usually incredibly arbitrary to avoid conflicts)]{ margin: 8px };" and then referencing that class in the code is quicker than writing "m-2"?


Not sure if you meant it to be, but "my unique, non conflicting class name (usually incredibly arbitrary to avoid conflicts)" is kind of a straw man; there are multiple ways to prevent that problem, such as CSS modules or web components.

Anyway, here's one common scenario for me: I need the same set of styles on multiple elements. With Tailwind, it goes one of a few ways:

- I duplicate the styles on all of the elements

- I abstract the data into some sort of loop and add the styles to the single element in the loop body

- I create a component, add the styles to that component and use that in place of each element

All of the above gets harder if there are existing elements that share some of the same styles, because I now need to identify the subset of styles that must be extracted. It's even worse if it happens across different tag names, because I then need to introduce branching into my abstraction.

Meanwhile, with CSS, I just give each element the same class name and add those styles to that class name.

I think this is what the author is getting at by saying Tailwind is far to the "write-only" side of the spectrum. There's no argument from me that just writing `m-2` is faster than adding a class name to the element, going to my CSS file and writing `.classname { margin: 0.5rem; }`. But that's only the first part of the process.


Why is "writing it as fast as possible" the ultimate goal?

Maintaining and debugging code (yes even styling) takes much more time than writing it in the first place. Which is why readability is so important, and why Tailwind's approach of big unreadable blobs is so jarring (it doesn't just make the styling hard to read, it also makes the surrounding markup hard to read).


Tailwind is both faster to write and more readable. Instead of having to read up and down and cross reference both inherited and local classes to find the active styles on an element I can look at a short string of instantly recognizable and easy to read utility classes right there on the element directly, as opposed to somewhere else in the file (or potentially in another file altogether).


> Nobody who knows what they are doing writes Tailwind styles as long as the ones presented in this post

That sounds a lot like no-true-scotsman.

The big image of the overstyled checkbox is from Netlify's admin dashboard.


Meh, probably is an example of a no-true-scotsman. A better way to word it would be to say that not many of the Tailwind codebases I've seen have styles of that length, except for on choice elements where most of the classes are overwriting default styling (such as on inputs).

You literally defended CSS in a similar way in your post "The cascade, selector chaining (see above), and specificity are all very powerful tools. If you misunderstand them, yeah you can cut yourself. Just like if you don't use a saw properly you can cut yourself.". In the same sense, just because some people write super long Tailwind classes doesn't mean that you can extrapolate that to all Tailwind classes.


for someone who has never used tailwind before, comments like yours read as being emotionally defensive of a tool you like to use. it's fine that you like to use said tool, but sentences like this

> Whenever I see someone make such a statement, I can tell it's more of a propaganda piece than a piece meant to spawn logical thought.

come across as pretty absurd. on whose behalf is the author supposedly writing said propaganda for? Big CSS? the guerilla antitailwindist movement?

the irony is you accuse the author of being emotionally biased, yet claim objectivity yourself, while defending said supposed objectivity with language that is far from dispassionate. this may resonate with other tailwind enthusiasts like yourself, but to the outside observer who has no investment (emotional or otherwise) in being either for or against tailwind, it comes across as far more overemotional (to the point of mild absurdity) than the author did in TFA.

this ends up having the opposite of what I must presume to be your intended outcome: it leads to the outside observer, reading these comments with mild interest to determine whether or not tailwind is perhaps worth another look since they checked it out last time, to be dissuaded from investigating this tool you like to use any further.


Exactly. This entire article is written like they're just TRYING to come up with reasons it is bad. "m-2 doesnt mean that always"? What are you talking about, of course does....... unless you change it in your config, in which case you intentionally changed it. How is this even an argument?


I do a lot of UI work and have never understood the appeal of Tailwind. It’s like relearning a new language. Tailwind was released in 2017. Maybe the CSS landscape wasn’t as good back then? Modern CSS is pretty awesome.

I’ve enjoyed using Vanilla Extract https://vanilla-extract.style/. It’s like css-in-js with none of the downsides as everything gets compiled to css.


Check out Panda CSS as well (https://panda-css.com/), I've been using both VE and this one, they're both great.


Most of Tailwind's class names follow a regular pattern and match very closely to the actual property names. It's much closer to learning legal shorthand than a new language.


So what's the savings against using the actual properties?


Classes vs style properties is the (dis)advantage.

I like tailwind so I prefer the class short-hands and composability.

Once I hit several styles on an element or obvious component styling (a la "primary-button") I just make a class with @apply.


There's a lot you can't do with inline styles, e.g., media queries.


I am not necessarily a fan of tailwind, but the author is explicitly writing how to not use a design system.

Not knowing what m-3 is is the whole point. You use it across the website to have some consistency in your spacing, and not just slap around another 8px.

Yes, you can also do that with CSS variables, and Imho at times I do think that bootstrap for all it's faults is a better base design system, especially for internal admin interfaces.


Yeah, the "slap around another 8px" part of that post made me actually wince, because it sounds like the author just kinda doesn't have a design system in place at all. If every page is gonna have bespoke styling scattered all over the place, Tailwind isn't going to help you very much, no.

If I'm given a design that specifies a margin of a different size than those we have defined in our style guide, I bounce it back for revision. If it all conforms to the style guide, it's easier to write, it's easier to maintain, and it just looks nicer.


I hate Tailwind just as much as the author but it's not a technical problem, more like a business and education problem.

Things like Tailwind (and formerly Bootstrap) are popular because they are easy to use. It's straightforward, brings results quickly, you don't need much of an artistic eye nor do you need to learn advanced CSS.

The author sounds like a CSS master/guru. The problem is that the typical web developer sucks at CSS. They know like 10% of it and much of that limited knowledge is outdated. Very few of them are going to level up anytime soon, hence your working situation is that you have multiple "amateurs" that barely have any clue what they're doing fighting the cascade. That's how you get disastrous unmaintainable CSS.

Tailwind is McDonalds.


> What would be a few, maybe even half dozen, lines of CSS in a separate file, are now shoved into the generated code, and repeated everywhere.

...

> One of the bits of advice Tailwind gives is to use @apply and extract common blobs of CSS into common classes2. > Why bother to even use @apply? Just write the damn CSS

If you take a look at the official Tailwind docs (https://tailwindcss.com/docs/reusing-styles#extracting-compo...) they say:

"If you need to reuse some styles across multiple files, the best strategy is to create a component if you’re using a front-end framework like React, Svelte, or Vue, or a template partial if you’re using a templating language like Blade, ERB, Twig, or Nunjucks. ... While it’s highly recommended that you create proper template partials for more complex components, you can use Tailwind’s @apply directive to extract repeated utility patterns to custom CSS classes when a template partial feels heavy-handed."

So Tailwind recommends the use of components/template-partials as the way to reuse styles (e.g. your styled button HTML with classes goes in `button.html` and you reuse that for each button) because they think reusing styles in the traditional CSS way via classes doesn't scale (https://tailwindcss.com/docs/utility-first).

I know it's trendy to bash modern webdev here but people criticising Tailwind should at least try and understand the Tailwind approach first. Same with comments like "it's just inline styles" - they cover common knee-jerk reactions like this in the docs (https://tailwindcss.com/docs/utility-first#why-not-just-use-...).

You know how it's popular now to prefer composition over inheritance in OOP languages because it scales better? Tailwind is more like that, whereas CSS is more about preferring inheritance via cascading rules. Tailwind advocates their approach scales better to complex custom web designs. If you're just styling basic documents, you're not going to see much benefit.


The "just write a component" strategy is really bad if you're trying to reuse your CSS across different projects and frameworks. I just want my buttons and cards and components to LOOK the same, not function the same.

I actively go against their "just create a component" strategy and I find it overcomes all the Tailwind limitations.


> The "just write a component" strategy is really bad if you're trying to reuse your CSS across different projects and frameworks. I just want my buttons and cards and components to LOOK the same, not function the same.

Could you explain the problem more? There's lots of Tailwind based component systems that have reusable button and card components you could look at for ideas how to structure this.

In comparison, reusing traditional CSS is usually a nightmare because the CSS for a single component is usually spread across multiple files and difficult to disentangle. That's part of the problem Tailwind tries to solve.


Which is no different from creating a styles.css file, writing your classes there, and importing it into your html...


Tailwind is more like you create lot of (utility) classes in your styles.css file that tweak just one styling property like a font, a color or a margin, and then reuse those classes to style each of your UI components like buttons and headers.

With traditional CSS, you would assign a class to each UI component and then write custom CSS for each one. Usually people try to share CSS between classes but then you have to be really careful you don't break unrelated components as you make changes later.

Read the Tailwind links above to understand more about the pros/cons of each approach.


I do think that the real value of Tailwind comes from the utility classes, rather than css-in-html paradigm. You could achieve the same, for example, with Pollen.css [0] or Open Props [1].

[0] https://github.com/heybokeh/pollen

[1] https://github.com/argyleink/open-props


This covers the design token part but it's very verbose (especially when you need responsive behaviour) when you want to iterate on styling a component compared to Tailwind and doesn't cover the other problems with CSS mentioned in https://tailwindcss.com/docs/utility-first. I guess it depends how complex your designs are?


I haven't used Tailwind in a larger codebase, but it looks to me like the problem here isn't with Tailwind, it's with the lack of abstraction in the HTML.

The tone of the article rubbed me the wrong way as well. Pointing to a couple of examples of bad Tailwind usage and then making the sweeping generalization that using Tailwind is equivalent to "the death of web craftsmanship" is completely over the top.

Sure, using Tailwind means that you won't have lots of one-off "craftsmanship" in writing CSS. But that doesn't mean there's no craftsmanship. It just means that the focus of the craftsmanship moves from writing nice CSS to designing nice boundaries between your HTML components.

But what do I know? I've only been writing semantic HTML and CSS professionally for 18 years.


You're not crazy. I've used Tailwind to accomplish 75% of the work required to rebrand an enterprise website with over 6k pages of unique content by updating only the Tailwind config. It's not perfect, but it's a reliably productive enough system as far as I'm concerned.


While the author raises a lot of good points, Tailwind is not the actual problem, it is the consequence of a larger problem - development time (or lack thereof). Frontend development has become extremely competitive and now that anyone can literally pick up coding in under a month, the bids for the existing projects has skyrocketed. And one of the ways agencies and development shops are able to compete now, is with delivery time. Say, I need about 2 months to build you a decent web app using custom CSS, there is always another guy willing to do it in a month. And for that guy, Tailwind CSS is a boon. He just needs to write some code fast and move on to the next project.

I am not sure what is the solution to the time problem. I foresee it will only get worse now that there is also AI in the mix.


Well, AI really suggests the endpoint: styling is done at the whims of the user, not the site owner. The owner, at most, suggests a default prompt for the content. But any particular formal encoding - language, visual design, etc. - is shredded and reconstituted by AI into whatever form the user considers most digestible. "Productions" are on their way out.


The biggest issues I've had with Tailwind:

1) Adding it to an existing codebase with lots of global scope. The techniques espoused in the documentation simply don't work well with existing leaky CSS with varying levels of specificity. Adding important to all TW classes and name-spacing all of them invites new problems.

2) Making your own components with lots of variants: the syntax required feels like you're doing contortions to make it work nicely. There's probably some special techniques but it's a lot easier to take advantage of the cascade in my experience.

3) Dev tooling: the OP I think is dead-on in the criticism that you break a lot of browser tooling when using Tailwind. The right hand side of the Elements tab is a complete disaster. The JIT that Tailwind provides to only include styles you need also makes things annoying: If I want to add some classes on the fly in the Elements tab (which is annoying anyway by having to edit HTML), it won't work if you need any classes not currently not used in the built code. You can mitigate this with modifying your JIT setting for dev to include a ton of classes but it just doesn't flow. The TW documentation is very cagey about how dev flows should work and for good reason.

I actually like utility classes as a concept and if I were starting greenfield projects I might consider Tailwind with an existing component library despite the dev tooling issues. I just think it doesn't layer onto existing codebases well.


1) My heart goes out to you here. I feel like re-adapting any existing styles to work in or with a new system can be a real challenge, and I'd hate to have to make it work. I've tried this once in an environment where it would have to mesh into a larger system with prefixes/important. I gave up and wrote vanilla. Tailwind just doesn't seem like a good fit there.

2) They have a couple of pseudo-selectors but just largely don't use them if we need the cascade. We just write vanilla instead, but it's funny how often that isn't required. When things need to be dynamic we'll often use the classnames library.

3) This is really interesting because we haven't really experienced this. I wonder if it's framework or ecosystem specific. I often find opinions vary broadly across these lines because of what you noted about the variance in dev flow.


> I've seen people, lead and principal engineers, who refuse to learn modern JS, insisting that since it was bad in 2006 it's bad today. Worse still is some of these people have used their leadership positions to prevent the use of modern JS, of component frameworks like react/vue, from being used across an organization.

Hold on... this is an incomplete thought. What did they say to do instead?

I'm serious. Did they built a pretty good system with Ruby on Rails, Java, or Laravel instead? Did they just use stock JavaScript without a framework? There are perfectly legitimate reasons why a developer might say that, for a certain organization, React is completely unnecessary and a symptom of overengineering. Also, for the few people writing new web apps in 2023 that don't require JavaScript, good for them.


In that particular case, we were made to stick with a hodgepodge of homemade components, server-side template includes, jQuery, and a couple of third-party libraries like select2.


> BEM, OOCSS, SMACSS, and friends pitched themselves as the "one true" solution. They all basically have something in common: they tell you to get rid of various CSS features to "simplify" things

Uh.. I don't think this author knows what BEM is


How do you figure? It's just a CSS methodology that avoids a certain kind of inheritance, which seems to match the comment.


BEM doesn't 'tell you to get rid of various CSS "features"', it just tells you how to name your classes and IDs


BEM is just a naming scheme. It's not a framework or library.


BEM, OOCSS, SMACSS are all methodologies, not just naming schemes.


You are correct. I missread it and was thinking BEM, unoCSS, etc. Such that BEM was something different then the rest of the list.


I think you missed the point of Tailwind, which is not to be an end-all be-all and replace CSS, it's just a utility library to replace/reduce the size of repetitive one-off classes. Nothing prevents you from using a CSS file for the more complex things you listed (which you should do), and Tailwind doesn't claim to replace all those parts of CSS.


The reason people think tailwind is supposed to replace CSS is the way adam wathan and the tailwind fanboys position it that way. There's a lot talk about how terrible css is from that camp.

Honestly, a lot of the negative sentiment comes from how the tailwind 'community' acts and positions themselves.


Can you provide a link to where Adam Wathan said something like that?


https://tailwindcss.com/ https://twitter.com/adamwathan

The tailwind home page feels tamer now than before, but the tone is still there.

Adam’s replies on twitter have always been a problem. I’m not going to go searching old tweet replies, so you can think I’m talking out of my ass if you want. But the guy is an ass. A lot of the responses to even neutral articles were whining manbabyish stuff (yes that’s subjective opinion). I haven’t seen it lately, so I’m hoping someone had a talk with him.


I do want to reiterate that I think tailwind itself has some great ideas. And there’s nothing wrong with utility css, if you’re not using a library you’ll end up writ a lot of it yourself.


Just a 50 KB library? Half of the budget of a fast site, according to Lighthouse.


Tailwind isn't included in your final bundle, it outputs a CSS file based on what classes you use.


RE "neutered inspector": you can inspect the element and click ".cls" to toggle off classes: https://i.imgur.com/ig2SQw3.png

RE "you can't chain selectors": you can stack modifiers like `dark:hover`. The example of having the same styles for hover, active, and focus actually seems Not Great as those are different states that you 99% of the time want to look visually different (Tailwind examples include things like hover:bg-blue-600 active:bg-blue-700 which just seems like a better guardrail)


RE: RE: Neutered inspector. Thanks for showing me that, I legitimately missed when it was added and never got it into my workflow. I'll stick a note on that section of the article

RE: RE: You can't chain selectors. Its still a valid issue, a lot of styles you would want to apply text decoration to hover/focus/focus-visible, but not the visited, active, or plain state. You still have to write `hover:underline focus:underline focus-visible:underline`.


Am I really the only person in the world who combines SCSS with Tailwind, so I can nest classes, declare utility classes, and use @apply in those classes?

What I mean is something like this:

   .Card {
     @apply p-2 bg-gray-100;
     &.--red {
      @apply bg-red-200;
     }
   }
Now, the docs (and the founder) are vehemently against implementing Tailwindcss like this, but it makes the code really readable and reusable. Adding "@apply ___" in my SCSS is much more compact than writing out full CSS, and I think much more readable. Plus, I get to apply whatever CSS that are out of scope of Tailwind. Win win.


The tailwind developers have specifically said @apply is a codesmell and they regret adding it.

If you're just going to stick the tail wind classes in a class like this, why bother? just use css vars.


I think CSS vars are complementary (I also use them in projects). There's probably a way to do this more elegantly, but with tailwind classes in a class I can quickly re-apply certain styles across projects / microsites.


You don't need tailwind for that? that's how css works.


You might be interested in OpenProps then: https://open-props.style

Basically its tailwind without all the classes, and without apply, in pure CSS variables


SCSS can be a nice timesaver, but it's generally not needed and adds complexity. Avoiding complexity is often the point of using Tailwind in the first place.


I have a bunch of projects (micro sites) that all use the same style, so I just import all my utility classes, so .Card looks the same across projects. Using SCSS and nesting helps me keep things like colors and other "attributes" organized across projects.

The problem I'm solving for is NOT having to find/replace every tailwind-spam across several projects/components/frameworks if I want to add a new "color" property to a .Card, or change the padding, or whatever. I just change the source utility, then link to the file (with Git / package.json linking), so the one change can be applied across projects.

If I followed what the founder and docs said, I'd have to spend every waking hour find/replacing text across all my projects.


I'd say you're doing things correctly then. In my experience, site/application-wide refactoring or restyling is much less common than adding or fixing components. You can't really optimize for both.


Out of curiosity, why are the docs/founder vehemently against this implementation? Seems pretty concise to me.


The provided example under "The problem" shows the biggest footgun in using Tailwind. I think it's an extreme example, and is just as bad as needing classes like "header-lower__wrapper-inner" that just have "display: flex;" applied to it.

I've been using Tailwind in combination with "normal" CSS where it's warranted and there are plenty of sites out there practicing the same thing. Atomic utility classes just make sense for some things. It's a tool that you can use correctly or incorrectly.


One of my biggest complaints about Tailwind complaints is they invariably start talking about the output - lots and lots of classes applied to lots and lots of elements. I don't buy it for the same reason I never bought the "semantic markup" stuff - the code exists to be parsed by a computer, and a human reading it is secondary. This is 1000x more true for simple web sites/themes, as opposed to applications which at least will have more maintenance.

Yes, a developer will read 100 lines of code for every line they write, so the DX is important. But the number of times you read the code pales in comparison to the number of times the page gets loaded by a user. Being able to build that quickly and move on is so much more important that having a lot of classes on your p tags.


I'm confused as to this author's proposed alternative, they don't like tailwind - so go use Vue? Apples to oranges, what about people using HTML + CSS markup without the overhead of a javascript framework? What's the alternative then?

In all honestly, I just couldn't care less about "web craftsmanship", I have bigger problems to focus on than today's incredibly messy DOM. Bootstrap allows me to quickly represents my designer's design system through an set of hammers (classes) that allow me to quickly convert work, plus with modern flex styling I really don't run into complex DOM rendering issues anymore that would require me to really dive into the styling and debug.


Personally, I like to have composition, logic, and styling be separate files. For me this boils down to html/framework, scripts for heavy logic, and css. This way, I can change composition, logic, or styling without fear of muddying the other and without the overhead of ensuring there aren’t regressions from forgotten dependencies. Tailwind muddies composition with styling, giving a sense of consistency with their classes but ultimately putting the burden on the developer to ensure that the whole confusing list of classes remains when changing composition. With a single class per component or styled components, this risk is avoided completely


Reminds me of Enron “oh you just don’t get it” line. Now they were committing fraud not css but when I look at that “mess” of classes the author published that was my first thought. He just doesn’t get this revolution tailwind has started. That mess is actually MORE maintainable than clever classes that do more than one thing.

https://www.themoviedb.org/movie/13020-enron-the-smartest-gu...


HTML and CSS are indeed a hot mess in some respects.

However, Tailwind is definitely not the solution, as it is the same as adding a style attribute to each html tag and writing the style in the html. What is valuable about Tailwind imho is the concept of utility classes, which can be accomplished with vanilla css variables.


i'm very guilty of making entire sites with style="margin: 10px; padding: 10px; etc." every style I want right there inline. Inline styles FTW! I would always say: "I'm just getting it working and some css person can clean this up later."

Now I use tailwind and I've learned their wonderful little syntax for mr-x or py and it does feel a little like inline style days. But I do make this file https://github.com/andrewarrow/settle-down/blob/main/assets/... with some stuff so I can say like "btn-main" over and over.


Yep, I have ended up doing the same with Tailwind, because in the end you end up with unmanageable inline styles.

I would recommend you to try Open Props [0], which you can use with Tailwind CSS, and see how what we all really crave is a good utility class system.

[0] https://github.com/argyleink/open-props


Not this again. With Tailwind, there are no hidden abstractions. Does that make the html harder to look at? Yes. But the tradeoffs are that you're not hiding how your CSS interacts with your documents, the way "traditional" CSS is done (more complex). If you're going to use the word "obfuscate" in this conversation, you cannot use it about Tailwind because everything that is happening is 100% transparent and based on single-use style-to-class mappings.


Huh? Tailwind is absolutely an abstraction. You can tell because the "arbitrary variants" feature lets you write (roughly) "real CSS" — that's the abstraction leaking. And that's not the only place it does.


".masthead" is an example of high-level abstraction class you might find in complex CSS. This is the kind of thing where there's no guarantee your markup will match how the classes were "intended" to be used. How the classes fit the markup is the hidden part.

I didn't say Tailwind isn't an abstraction. It absolutely is, but it's a minimal one that is transparent. Every class does exactly what it says, nothing more, nothing less.


There are a lot of arguments I've heard in this article rehashed time and time again to the point that I feel if I were Adam Wathan (Tailwind Creator) I'd just read this, shake my head, and move on. No one is holding anyone over a barrel. You still have to have your head screwed on right when you write code, and a failure to do so is not a condemnation of the tool by itself.

Tailwind has tons of good escape hatches, and many of these arguments about terrible maintenance are just rubbish. I use the developer tools with Tailwind all the time when debugging. I just pick a test case, write an inline style to demo an override, then go back and update my styles. I still write a lot of custom CSS when it's called for, modern features, included with Tailwind. Now it's easier to spot those more complex features in a much shorter stylesheet.

I've explained carefully to my junior developers that Tailwind is a tool to make it easier to generate and manage a standardized, reusable design language in CSS that is expressed through JavaScript without inflicting a performance penalty on the end-user. It's not a panacea. As much as it has grown and prospered, we still look at it as an extension of PostCSS, not a replacement for CSS. I stress to my team that learning and writing real CSS is still necessary, and that our goal should always be to look out for ways to not use Tailwind or to eliminate it when it makes sense to do so.

I'd love to hear more about scoped styles, especially why they were deprecated by browser vendors. I'd be interested in seeing some data about performance penalties from HTML parsing too. I personally haven't noticed anything that could make a difference no matter how "ugly" it is. I guess I just want to stress my/our open-mindedness. I don't understand why Tailwind has become such a religious topic of discussion for CSS. I remember when writing CSS was a lot harder before Tailwind and lots of other really nice CSS features were around. I feel like we've come a long way.


the author neglects to mention any of the opposing opinion blogposts, which I think a responsible author should: https://www.swyx.io/why-tailwind#appendix-opposite-perspecti...


Added


This is always true. Systems exist. There is nothing to fear about them. If someone wants a McDonald's burger they will go to McDonald's. If someone wants a steak they will go to a steak house. This is where designers and marketers can help the industry. By advocating for and selling craft.


> a magic bullet for any and all valid complaints about CSS

A craftsman should know about all the available tools, about their trade-offs and when to apply them. Completely dismissing a concept and offering a "magic bullet" is the opposite of web craftsmanship.


Tailwind CSS is fast fashion. Gives you a fresh look in a cost/time-effective way, entirely not sustainable w.r.t. the environment, but sells like hot cakes in any case.

The reality for better or worse is it's good enough for most companies and engineers.


As someone who doesn’t do FE full time bootstrap and now tailwind make some hard things in CSS easier for me. Compared to figuring out how to vertically center some shit in IE 6 the modern world is simply a blast.


Yet another boring take down of Tailwind by someone who doesn't understand design systems making the same boring old arguments.

I like working with CSS and have been using it since it came out in the late 90s. Utility classes are by a large margin the best experience I've working with CSS, and Tailwind does it really well.

Also, people really have to stop with the: "But that's not what the original designers of CSS intended!" as if language designers have never made a mistake or a bad decision before.


I don't do web dev, so maybe I am missing something, but couldn't tailwind be fairly easily extracted into separate css file, and other way around?

That way if you dislike tailwind, just run some script and voila, you have your .css file you can edit separately, like a good old days


I think you're more or less described what the end-result for Tailwind is. Tailwind doesn't prevent you from writing vanilla CSS in any way, shape or form. It's a big part of why all the hate often feels so weird. People largely just seem to hate seeing it. I've never read one of these articles where people could resist subjectively calling out what amounts to, "Well, it's ugly!".


> Scoped CSS lets you [...]

Scoped CSS doesn't seem to be a feature of CSS. The "scoped" attribute shown in his example apparently existed in some browsers a decade ago and has since been removed.

I guess maybe it's a Vue template feature that happens to reuse the same syntax?


It's in Vue, and surface ui. There's also a pending @scope keyword coming to CSS in the near future.

I'd expect most component frameworks to have some equivalent version of this. In svelte, it's the default; all style tags written inside a component are scoped to said component.

You can largely achieve scoping manually if you have to, via nesting, which recently landed in CSS.


tailwind is like Go, at first it seems like a bad idea but it works really well in practice


He maybe right in some ways but prototyping got so much faster with tailwind.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: