Hacker News new | past | comments | ask | show | jobs | submit login
Working with Tailwind CSS every day for 2 years (themosaad.com)
154 points by themosaad on Nov 29, 2022 | hide | past | favorite | 205 comments



I really like Tailwind, I see a lot of hate in this thread so I'll try to offer a counter-perspective.

First of all, it's important to understand what Tailwind is good for, and what it's not good for.

Tailwind is NOT a design framework. It doesn't dictate the "theme" of your app. I like to think of tailwind as a tool for writing "clean" css that encourages good UI/UX practices. We need to stop comparing it to Bootstrap.

Tailwind is a tool that makes using "good practice" design simple and easy. If you haven't read Adam Wathan's "Refactoring UI", I'd recommend it. It's a book that helps developers make better UI/UX design decisions, and Tailwind is the framework that Adam developed that encourages a lot of those practices.

On that note, my recommendation is, "Tailwind CSS is AMAZING if you're a small team of developers and you don't have a dedicated designer". Tailwind probably is NOT a great choice if you work with a team of designers and need everything to be super-custom and pixel-perfect.

It's also important to note that tailwind is based on utility-classes. It can easily be used alongside "regular" css. I love that it gives a sane convention for writing that stupid "padding: 4px" class you're going to need. My old apps had crap like this that all did the same thing:

.p-4 .pad .padding-4 .padding-button-4

After several developers got their hands on the CSS, and didn't realize there were already classes defined for what they needed.


Tailwind can be used very effectively with designers, so long as both teams agree on the scales used. All of that stuff can be modified to your designer's heart's content.


> We need to stop comparing it to Bootstrap.

Bootstrap has actually a lot of utility classes (since v4 IIRC).


I have a lot of custom stuff like breakpoints and typefaces and new sizes. If your designers can work with the tailwinds configuration file it shouldn’t really be a problem


A lot of people I respect really like Tailwind, but I just don’t get it. Seems like you end up with walls of classes because you don’t name your compositions, and thus your design system doesn’t actually live anywhere. If you have a standard box in your design system with a certain padding, spacing, font size, and so on, are you really going to write out all its constituent properties every time you use it? How do you manage that effectively?

As an alternative, use a scss mixin (or even just a class) called standard-box and then use it everywhere. Do that for all the major parts of your design and you’ve created a language with which to build individual chunks of UI. Modifying the design system itself isn’t free but it’s a lot easier than mapping the change to every use of every tailwind class.


> A lot of people I respect really like Tailwind, but I just don’t get it.

I think I get it. For years there have been lots of popular CSS authoring conventions for JavaScript apps that are clever but complicated, fragile, and have a lot of churn. Most of these either have large JavaScript runtimes that generate and inject styles on the fly as components render, or deep integration into the JavaScript build system like Webpack/Babel/etc. plugins, or both.

I believe that there was intense fatigue from supporting these systems, keeping up with the newest versions and trends in the high-churn JavaScript ecosystem, etc. Tailwind essentially offers a ridiculously simple way out of this fatigue. You just say screw it, let's just author all our JS component styles using string literals that from JavaScript's perspective are just totally arbitrary HTML classes. No more importing things, or using plugins that transform the AST of our JS source code, or extracting chunks during SSR, etc. We just run this totally separate Tailwind compiler that looks for string literals that seem like Tailwind utility classes and generates a stylesheet that we just import in our HTML.

Tailwind is pretty great on its own as a utility CSS framework. There's a lot of thought put into the "API" of their utility classes, and because it's so popular there is a ton of support for almost any use case you'll come across. But at the end of the day, I think the reason for its ubiquity (particularly among respected veterans and "influencers" in the frontend community) is that is offers a way out of the "JavaScript tooling rat race" that was causing so much fatigue.


You manage this complexity the same way you manage the complexity of repeated code nearly anywhere else: you encapsulate. If you have a standard box (div + styles) that you use everywhere, abstract it in whatever way you're writing your application allows. For example, a component in React.


Or...a CSS class? I think the point is that CSS already has the means for abstraction built right in. Using an application framework in order to abstract a list of styles adds unnecessary complexity that compounds upon itself.

Somebody writes a React component to abstract a button and its standard styles, and all seems simple enough. Then we need marketing styled buttons, and buttons that are anchor elements, and ones that are small and large, and inverted and with and without shadows because the design people insist, with icons, block width, pills, etc, etc. And you end up with a monstrosity component with all sorts of bespoke interface that everyone has to learn, when some cascading classes and plain HTML would have entirely sufficed.


That seems to be a straw man its never just plain html and CSS. Even if it were, what you describe just devolves into dozens of similar or slightly different (many times by mistake) classes that all need to be maintained.

I would much rather dig into a component that has been built to be flexible for all of those needs to keep it up to date, restyle of add new behaviors and designs requirements vs going through hundreds of html files looking for all of the classes that were created to do those variations you list above (including effectively duplicate (or worse yet cascaded with different behaviors to accommodate the different html construct they were placed on when created).

I really did dislike tailwind from first learning about it, but using it has turned me on it 100%.


I think the usual solution here is just to make your standard box a component, as opposed to using a div with a class. Then if your design system changes, you just change the wall of tailwind classes in that single component file.


And now you are inventing your own Theme UI or Emotion


I'm very sceptical of class-based CSS solutions because I had "do it semantically" drilled into me when learning it.

However I think I'm starting to come around because it finally clicked that modern web programming already abstracts styles on another level, as components.

So instead of repeating yourself, you create something like a StandardBox component and it brings all of its styles.

It still doesn't look or feel very elegant to me, but this seems to be the compromise most web developers are willing to make. Changing the theme seems to be mostly done through variables.


I use view components (ViewComponent gem) aggressively. Instead of having a .standard-box class, I have a StandardBox view component that owns all of its own utility classes.

On the rare occasions where I need to duplicate groups of Tailwind classes, I’ll extract those into ‘semantic’ CSS classes using @apply.


It is possible to "group" tailwind classes.

See https://gist.github.com/axeldouglas/7f45b2a862401c7b515c138e...


Having used Tailwind, once the class lists get large, it inevitably just becomes CSS classes again. My previous role had

    const headerStyles = ["bg-slate-100", "rounded-xl", "p-8" ...]
    ...
    <div className={...headerStyles} ... >...</div>
and so on. So at that point, why use Tailwind anymore?

Currently I use Vanilla-Extract, it's like SCSS but uses TypeScript instead of the SCSS language. It also compiles down into native CSS, so there's no runtime overhead like styled-components, emotion, etc.

https://vanilla-extract.style


> Currently I use Vanilla-Extract, it's like SCSS but uses TypeScript instead of the SCSS language. It also compiles down into native CSS, so there's no runtime overhead like styled-components, emotion, etc.

I like Vanilla-Extract! Didn't wanna add to the article by talking about it and other alternatives I tried.

It solves the main two problems I encountered with Tailwind CSS by offering type-safety and low learning curve since you're mostly writing vanilla CSS.

However, some of its issues are:

- Isn't as widely supported as Tailwind CSS since it's exclusive to TypeScript (cannot use it in Laravel)

- Requires a special integration with each framework (couldn't use it with Remix.run)

- There's a runtime overhead when you use it inline via its Sprinkles package. Without it, you're back to thinking about class names and having your CSS in a separate file which I personally found not to be neither the most productive nor the most maintainable approach.

That being said, Vanilla-Extract author has recently Joined Remix.run and I hope they address some of these points soon.


Thanks for the vanilla-extract recommendation, I'll be using this!

In my case, tailwind was useful for providing a handy set of vocabularies for simple and common stylings. But once customizations start to pile on, we're back into SCSS. Using 2 systems at once meant additionally gluing them with the postcss toolchain, so effectively we have 3 preprocessors running for every style refresh.

Looking in at TypeScript from the clojurescript ecosystem though, I'm still yet to see an equal to https://github.com/noprompt/garden or https://github.com/Jarzka/stylefy: single language, excellent composability, compile-time anonymous class names, inline styles... almost like they solved CSS (except for typing)

that said, tailwind's and SCSS's VS code integration is pretty amazing.


Fwiw to the avoid 80%-in-TW / 20%-in-SCSS-or-something-else dichotomy/complexity, we built Truss which brings TW (Tachyons) style abbreviations to the existing "something else" tool chain (Emotion in our case).

So then everything goes through a single system.

https://github.com/homebound-team/truss

> tailwind's and SCSS's VS code integration is pretty amazing

We get that too, by being just vanilla TypeScript, no editor-specific integration necessary. :-D

(I've linked to Truss in another response, so will stop now. :-))


> This plugin would allow me to use my existing CSS knowledge by writing:

   <div
     class="display-flex align-items-center gap-8px border-radius-6px padding-4px"  
    >
    <!-- ... -->
    </div>
> Instead of:

    <div class="flex items-center gap-2 rounded-full p-1">
      <!-- ... -->
    </div>
But... why ?

I just don't get tailwind.


My gripe is that it’s stuff I don’t always have to care about. If I’m not working with CSS it’s just noise.

Whatever way that’s refactored, I only want to see that when I’m working with the styles and I don’t wanna do anything special for it either.


This is far from being the best example of WHY you should use Tailwind CSS. I wrote about the HOW.

I'll try to write another article about the WHY soon.


I think something clicked yesterday after reading a comment. It works for SPA/css-in-js/components-based projects, and this long class string is manageable as long as it is only present in one single place in the whole project (thinking about versioning and the mental model of where's what).


I recently came to the conclusion that the best way to write CSS is through a Design System and write all the CSS yourself. And when I say Design System - I mean it can be your own or it can be external.

The problem with writing a lot of CSS is that you will eventually run in circles and repeat yourself on the same elements with only small differences. A design system lets you adjust it all at once through Variables, and as of recently - some of the more powerful Selectors and Pseudo-classes.

It also makes it easier to switch a design so you don't inherently lose all your work.

I don't have a strong opinion on Tailwind CSS because I don't use it but I am familiar with it. I know it hasn't been adopted inside CMS's all that much, but it is very popular when it comes to things like individual elements (cards, headers, etc.) because those individual elements will work universally across any Tailwind project.


I hope to work on a product where productivity isn't as essential as the quality of a design system to use such approach.

Saw the folks behind a CSS-in-JS library (Stitches) move to it recently[1]

However, they still saw the need for utility classes for cases where devs want to hack together something custom[2]

[1]: https://twitter.com/colmtuite/status/1572918908637650944 [2]: https://twitter.com/colmtuite/status/1572918911301218305


I don't like Tailwind, except for pure layouting where it truly shines. My belief is this: we should think of layouting and styling as two distinct concepts and it doesn't help that CSS is used for both. After years of experimenting with various CSS approaches, I believe using Tailwind for layout and styled-components for styles is the ultimate setup for React applications.


Same feeling here.

In Tailwind, I'll use a `p-3` or a `.flex.items-center` all day, but making a nice button is such a slog, since I'm coming from a Vuetify background.

Vuetify also has the same layout classes as Tailwind, like `pa-3` and `.d-flex.align-center`

I use Vuetify + Pug (instead of raw HTML templates), so I get to make buttons like this:

    v-btn(outlined color="accent" @click="handleClick") Do Stuff
or a Data Table with, say, a custom-formatted UPC column, like this:

    v-data-table(:loading="loading", :items="items", :headers="headers", :items-per-page="50" dense).my-3
         template(v-slot:item.upc="{ item }")
              span {{formatUpc(item.upc)}}
(I like to separate attributes with commas, which is valid pug but invalid HTML)

Sure, I could get something like TailwindUI, but their components are walls of divs and classes that each take half an hour to load into my head to visualize what's creating all the combos of states (regular, hover, focus).

So my Vuetify + Pug approach works great for greenfield projects that I create.

But if I'm downloading a nice template for a side project, say from Cruip.com, it's not like they're going to use the same build chain as me, and they do have to settle on a standard CSS library, so they default to Tailwind (just like 5~10 years ago they would have defaulted to Bootstrap).


The secret of TailwindUI is to take the HTML version of their code and just built your own components (e.g. if using VueJS). I would not use their suggested VueJS examples, as generally I prefer the options API.

It does however add some extra time to build your app - but consider that they have already built 90% for you - in terms of the design etc.


I mean what is stopping you from making a Button component once w/ Tailwind similar to Vuetify?


I've used probably 50~80% of Vuetify if taking together the last dozen projects I've done.

I'd basically have to re-implement all of Vuetify in Tailwind, from buttons to chips to toggles to alerts, etc?

The v-data-table is like 10 sub-components stuck together, each of which I'd have to re-implement with walls of Tailwind classes.


How do you distinguish what is "layout" versus "style"?

I know there are some obvious things, but like you said CSS is used for both. So there is a lot "grey area". Especially when "style" can affect the "layout".


Having gray area doesn't mean you can't separate purposes whenever possible (if that's what you want to do.)


I hate Tailwind with the passion of a million suns. It's loved by novices and those who don't know what they're doing, almost exclusively. They claim they know CSS, but will fail after any casual test.

The best arguments against it:

1. Spamming utility classes causes horrible git commits, git history, git difference checks;

2. Conflicting utility classes aren't clear, and sometimes the order cannot be trusted;

3. Replacing `p-4` with `p-4` will require you to replace its occurrence all over your app, sometimes affecting thousands of matches. And since there is no context to it, it'll be hard to JUST replace them where you would want them to.

Vanilla CSS using `:root` declaration, a fixed base size (using `rem` and `em`), CSS variables and `calc()` are SO powerful.

In almost every single use case, using vanilla CSS or SCSS is far superior. For React projects (and Vue.js, and Svelte, and Angular), I'd recommend anyone to just use (S)CSS modules. It's so elegant and doesn't come with any of the disadvantages.

Except maybe a slightly larger package size. Minimally so. Your framework of choice should be (or allow) code splitting to take place. And a few bytes more or less aren't going to make or break most websites.


It's loved by novices and those who don't know what they're doing, almost exclusively.

I've been a frontend dev for 25 years and I quite like Tailwind. I'm not sure which category I fit into. I think it might be both.

Replacing `p-4` with `p-4` will require you to replace its occurrence all over your app, sometimes affecting thousands of matches.

This is a really good example of where Tailwind is actually quite nice. Imagine if you didn't use a utility class, and instead you'd written your styles in plain old CSS or SCSS with "padding: 8px;" everywhere on your years-old-built-up-to-thousands-of-styles design. Replacing those is easy enough, but what about your SCSS mixins? Your CSS calc()s? The places where someone thought an element needed a bit more padding and used "padding: 9px" instead? Manually managing styles is hard. Tailwind makes it a bit easier, mainly because it encourages consistency and removes variability. Finding and replacing a "p-4" everywhere is trivial, and if you've used a library well rather than rolling out an ad hoc mix of things you can be quite confident your change will work everywhere. It's far from perfect but it's quite a well-thought out approach.


They "No true Scotsman" started their argument, so I think you're fighting uphill.

I have around 10 years experience, tailwind is a godsend for maintainability and speed.


Variables would be the obvious answer here.

If you're applying `p-4` to all your component classes you're only marginally improving on applying `padding: 8px` on all of your component styles. Both are terrible solutions even if one is slightly better than the other.

I generally agree with OP. Tailwind is horrible for larger projects and I have no idea why it's so well liked. The utility class approach is bad, and naming of their utility classes is even worse.

For smaller projects or for prototyping it's okay, although even then it's only slightly better than inlining styles.


But p-4 is a variable: you can set the actual padding applied in the configuration file! It doesn’t have to mean 8px, but rather „standard amount of padding“. If you want to use „small amount of padding later on, you’ll use p-2 - whatever that means in your app.

That solves the exact problem you and OP complain about, in a neat, configurable, safely replaceable way. Tailwind allows you to separate design intent from implementation values.


Vanilla CSS also allows this, without any real effort either. Tailwind's power is in its defaults, as soon as you want to start tinkering, you were much better suited sticking to vanilla CSS.


I didn’t dispute that. I just refuted parents point here.


Variables would be the obvious answer here.

Variables are very cool and super powerful, but they fall down if you need to support old browsers and it's really easy to make things that are hard to reason about (defined in several places, used in calc()s, overridden in the cascade, etc). On a team that doesn't communicate or test well vars can be a source of pain.


Pretty much every CSS framework I've ever seen (and certainly every CSS-in-JS solution I've ever seen) has as its underlying premise "we know you hate CSS, and want to avoid thinking about it as much as possible". For people like me (and perhaps you) who actually like CSS, our reaction is "why would I ever want to use something that abstracts away the power and flexibility of a thing I enjoy using?"

The problem is that I think we are outnumbered. Most engineers want to avoid CSS, pretend it doesn't exist, and if all else fails, build a complicated mech suit they can climb into to manipulate it without coming into contact with it directly. That's why things like Tailwind and Bootstrap and CSS-in-JS solutions took off.

My solution on the teams I worked with was to say "hello, I will do all of the CSS in the entire application if it means we don't have to add another dependency just to indulge the team's distaste for CSS."

Near the end of my career I was pretty much just doing CSS all day. I liked it, but probably what I did was create a terrible situation for whoever came after me. They still hated CSS, were bad at using it, but now had to maintain a lot of it. We probably should just have consented to the convenience of the majority even if (as I still maintain) they are wrong.


I actually think a lot of CSS-in-js frameworks became popular for the same reason that React became popular: css, html, and to some extent JavaScript can't really be decoupled.

Things like CSS Zen garden made it seem like they were, but that was only a separation of control. If the HTML structure changes, then the CSS likely needs to change. That's a tight coupling.

CSS-in-js embraces the coupling and makes it explicit instead of implicit, which makes refactoring safer and speeds up development.

edit for typo


I wrote that because the benefit most people who liked styled-components told me they appreciated was that they didn't have to think about inheritance. Everything is encapsulated in the component. This, to me, is equivalent to saying "I think the cascading part of CSS is either a mistake, or too complicated to maintain in practice, so I am going to eliminate it."


> This, to me, is equivalent to saying "I think the cascading part of CSS is either a mistake, or too complicated to maintain in practice, so I am going to eliminate it."

I agree both with your interpretation, and with the sentiment. I think, in many contexts, using the Cascading part of CSS is a mistake. It's great for low-level design system kinds of work (e.g. setting matching text and BG color, then override font size or decoration later). But, it's pretty problematic when you start styling specific components in a web app. (This is less of an issue with static documents).

If we're using SASS instead of a css-in-js, we end up doing things like:

``` .my-custom-hero { h1 { // override necessary styles } } ```

That's essentially the same encapsulation. It's just hidden in a one-off class in the CSS rather than embedded with the HTML - which makes it even less legible. It also has a dependency on the root-level `h1` styles. If someone change those, they inadvertently change the hero's styles.

We could get around that by having a `.my-custom-hero .my-custom-hero-title`. But, that's essentially a worse version of css-in-js encapsulation: I have to come up with names for everything, and my component's internals are split across multiple files.


Whether you like it or not, the cascading part of cascading style sheets is an integral part of the web platform, and one is better off in the long run swimming with the current of the platform rather than against it.


Agreed. I think he's completely misidentified why CSS-in-JS became a thing.

I've done a lot of thinking about this - like "How would we adopt what we have done in frameworks into web standards"? And the closest I have come up with is Svelte.

In Svelte your file is a component. It can have a <script> tag which defines state that can be passed to it or any other arbitrary javascript. You can have a <style> tag which scopes specifically to that component. And the rest is just regular HTML markup with the ability to do light interpolation for dynamic values.

That's how it should be, in my opinion. That's what we should be moving towards as an industry. I think CSS-in-JS was a side effect of JSX being in JavaScript. Hopefully we are moving away from that trend now.


I cannot agree with this comment any more. Every time I read any one of these Tailwind threads, I am yelling in my head "SVELTE!!!!" the whole time. Going against the current of the platform is a recipe for disaster every time, and Svelte is one of the few frameworks that is actually trying to go _with_ the current.


> Pretty much every CSS framework I've ever seen (and certainly every CSS-in-JS solution I've ever seen) has as its underlying premise "we know you hate CSS, and want to avoid thinking about it as much as possible". For people like me (and perhaps you) who actually like CSS, our reaction is "why would I ever want to use something that abstracts away the power and flexibility of a thing I enjoy using?"

I don't find this to be the case with styled-components. To me styled components feels just like writing CSS (which I like), but with a slightly obscure variable syntax (if I need a prop or theme value).


> It's loved by novices and those who don't know what they're doing, almost exclusively. They claim they know CSS, but will fail after any casual test.

This is an incredibly silly take. As a senior frontend dev with almost 20 years of experience and a solid resume, I will take Tailwind over anything else in most cases. Obviously there is always exceptions based on the project needs, but to have such a hard take like this just cracks me up at how narrow minded it is.


I really don't get the "tailwind is for beginners" opinion I see parroted so much.

It doesn't enable you to do anything that CSS didn't already do. It's just a different way of writing it. Knowing CSS is a prerequisite to being able to use Tailwind.

"Tailwind is for beginners" just sounds to me like "I don't like this thing because I'm too smart for it"


Because Tailwind is often suggested as a way for novices to implement design without having to really learn CSS. Simultaneously, allows people who don't care at all for CSS or design to create something functional.

Tailwind does not require knowing CSS. It'll get you around tailwind faster if you do know it, but you do not have to care about what Tailwind is really up to when adding "outline outline-2 p-4 outline-offset-2". This is a major reason why people pick it up in the first place, to ignore CSS as much as possible.


> Because Tailwind is often suggested as a way for novices to implement design without having to really learn CSS

Where are do you so frequently see that being suggested? I would never suggest that and I've never seen anyone say something like that before.

Tailwind is not an alternative to CSS, it's a way of writing CSS.


Tailwind absolutely requires knowing CSS. You cannot effectively use `outline-2` without understanding what an outline is.


Millions of people know what an 'outline' is without knowing what CSS is. You might need a better example than 'outline'.


They can't explain the difference between an outline and a border though, and especially can't explain when to use which. If you try to use Tailwind without understanding that, you're gonna have a Bad Time™.


But almost every Tailwind shortcut translates to one line of CSS. So you need to know CSS, for example you understand words 'padding' and 'margin'. I did not know these words mean before learning CSS.


Being bad at CSS is certainly why I love it. But that doesn't mean it's not a great tool for experienced frontend developers as well.


> "Tailwind is for beginners" just sounds to me like "I don't like this thing because I'm too smart for it"

More like "I don't like this thing because I don't understand it, so I blame it on juniors because I feel superior to them, so I must be right"


I've been doing front-end development in some capacity almost as long as you, but it's not my forte. I've looked at Tailwind and I can see its value as a way to quickly prototype, but it also looks to violate the deeply instilled belief I have about semantically named classes. I know front-end development has changed its set of best practices several times now. We went from unobtrusive JS and semantic class names to modular CSS and CSS in JS, along with heavily marked up HTML to help out JS frameworks. Maybe this is just the pendulum swinging again. But, I'd appreciate any insights you can share on how this helps lead to maintainable software. The string of utility class names on various elements doesn't click for me the same way a class named "sidebar" or whatever would. But, a lot of smart people like it, so I assume I must be missing or overlooking something.


I've been writing FE for 15 years and I'm in love with Tailwind. I never have to worry about naming, which in and of itself saves me a mountain of time. I had no idea just how much effort I spent on it until I stopped. Also, what I love about Tailwind is that it isn't an abstraction - the parent comment makes no sense because you can't use Tailwind without understanding CSS. But for those who do understand, it makes you so much more efficient, especially with a plugin like Tailwind Intellisense.

Your HTML will be uglier and it will be more difficult to look at the elements panel to find the element you're looking for. Both of those things are super minor for me, and have almost no impact on my day-to-day.


This is so frustrating to read over and over. Tailwind uses PostCSS. You can literally wrap a bunch of utilities inside a semantic class name.

Or, if you're using containers/writing components you can literally do something like this: https://pastebin.com/qkdzGWNT

But that a { could just as easily be "tag"

Personally, I've always found BEM to be tedious and the sort of "roll your own" class names just doesn't scale. This approach scales. This is what we should be moving towards as an industry.


> This is so frustrating to read over and over. Tailwind uses PostCSS. You can literally wrap a bunch of utilities inside a semantic class name.

If it's being asked repeatedly, it should probably be addressed somewhere. I see no mention of it in the tutorial or any of the amazing starter videos I've seen. I also have never used PostCSS directly before, so that's not an obvious solution. I appreciate the example you shared. I've asked (the OP in particular) in good faith, so I'm sorry that the question frustrated you.

> Personally, I've always found BEM to be tedious and the sort of "roll your own" class names just doesn't scale. This approach scales. This is what we should be moving towards as an industry.

Can you please elaborate on this? We've had well-functioning applications that predate Tailwind or CSS in JSS, so I'm not sure what doesn't scale. Descriptive names might get messy if you jam everything into in a single stylesheet, but using the cascading part works pretty well. If we're going to say "no, actually we were completely wrong when we said class names based on styles like 'red', 'big', and '2spaces' were bad", I'd like to understand what fundamentally changed. If it's just a case of convenience, that's fine. But, what about this scales better? How does this approach improve maintenance?

Your PostCSS example where you compose rules looks like a good approach, but I don't see that widely in use. Instead, I see the same rules repeated across elements in a document. That's the style the Tailwind docs use, so I'm operating under the notion that this is the prevailing way to write frontend code with Tailwind. While I can see that as an improvement over inline styles by ensuring consistent spacing definitions and such, that was never the recommended approach anyway. The style used in the Tailwind docs strikes me as being more problematic for ensuring consistency across pages or an app, which I see as hampering scalability.

Anyway, I'm trying to better understand why and how this shift is better. The only clear answer I've had thus far is that it's faster to get started because you get nice styles out of the box without having to adopt something like Bootstrap. That makes sense to me for small projects. At least I can see why someone would make that trade-off. I'm less clear on the long-term maintenance aspect and how utility classes help that.


Also, I did look at the docs and there is an entire page dedicated to this subject that you can read and it does mention @apply.

https://tailwindcss.com/docs/reusing-styles#extracting-class...


Thanks. I did overlook that. Although, the docs are a bit foreboding.

"Whatever you do, don’t use @apply just to make things look “cleaner”. Yes, HTML templates littered with Tailwind classes are kind of ugly. Making changes in a project that has tons of custom CSS is worse."

Then it lists a bunch of reasons that seem to just be the author's preferences on things like naming and workflow productivity. I guess I don't think naming things is that hard. I generally need a name of some sort anyway so I can interact with the thing from JS. I haven't seen anyone argue not to use React or Web Components because naming a component is hard. I wonder if it's just a granularity thing. I tend to give a class name to a logical entity and then use ancestry selectors for nested tags, with SCSS mixins to avoid duplication.


I didn't think naming things was that hard, until I got a chance to try building a project without having to name things. I'll never go back.


I agree that some of it is preference, but the main point he is making is that the number of utility classes doesn't matter if you're using a framework and styling a component. Yeah, it's ugly markup but so what?

With that said, I think it's reasonable to roll some things up into general classes. They remain very easy to change.

>I haven't seen anyone argue not to use React or Web Components because naming a component is hard.

I think the whole point is that that UI element, or "interface", or "component" is the thing that should have a name. That entity doesn't need to have a unique CSS classname.


Got it. So this would be more of a hybrid approach and clears up that question on naming granularity. Give the "component" a logical name and use the utility classes for the component's entities. That makes a lot more sense to me. I didn't realize those child entities were the things people were giving names to. CSS has fairly rich ancestry rules, so I just assumed people use them.

Thanks for taking the time to clarify that for me. It does indeed change my perception on utility classes.


I still think you're misunderstanding. It sounds like you think that the component has a class and everything flows from that. Like .component-name a {}

The point is that HTML elements don't need to have semantic class names at all. So even the top most element in the component would still use utility classes.


Fair enough. I've realized I am currently using Tailwind in a way that is not the default and maybe a lot of people don't know about it. I think I will write a blog post about it.

Also, I think I am wrong. It's possible to use Tailwind without implementing a build step like PostCSS, which would necessitate using it much like the documentation describes.

>We've had well-functioning applications that predate Tailwind or CSS in JSS, so I'm not sure what doesn't scale.

CSS has fallen victim to the same "cult of semantics" that HTML has, and it's disingenuous bullshit just like it is in HTML (mostly).

Yes, if you have someone who really cares and writes great CSS they will have everything broken out into custom variables and well designed and named classes. You still have the problem of how you scale that, right? After all, if you have a .card that is formatted one way on one page but laid out differently on another, you have to think up a different class name or refactor.

Enter BEM. Block, element, modifier. A consistent naming system for your large application. That is both subjective, requires a lot of extra mental bandwidth in a time when you're lucky if half the "front-end developers" you're working with know more than the basics of CSS to begin with.

I have seen some truly tortuous HTML that uses BEM classnames. It was awful.

So when I say it doesn't scale, it's that most large codebases end up with !important everywhere and z-index:9999999 etc...

So throw it out completely. Utility classes make sense when you accept that you are probably styling a component and that markup will be inserted everywhere the component is. You never have to think about what your class name should be, and if it makes sense, or if it conflicts with other scopes.

Yes, an HTML element with 15 classes is not ideal.

The example I posted would hopefully be the logical end (and is if you're using Svelte with PostCSS configured) - where we're no longer writing html documents full of elements and then a stylesheet styling them. Each discrete UI element has one document, markup, state and styling are tightly coupled, and it can be reused many times.

It gets rid of nesting being very important, it gets rid of having to write verbose media queries for responsive. It makes changing things really fast.

The part about Tailwind providing sensible defaults in terms of sizing, spacing etc... is just icing on the cake. The problem that Bootstrap had is that it tried to do too much. It came about in an era when doing layout in CSS was really hard. So it rolls its own stuff like modals, dropdown menus, grid etc... it also takes it upon itself to style actual HTML elements, which to me is a very poor choice. Including bootstrap will get you default styles for headings etc... but I believe in Tailwind it's all done through utility classes.

Tailwind doesn't do that. The focus is on removing semantics from styling. Look at the API surface: https://tailwindcss.com/docs/

There's a "Components" link, but that's to a paid resource showing common UI patterns built with Tailwind.


Don't follow the hype and trust your own experiences and tooling. Tailwind can be good, but I get the sense that many commenters (not in this thread, however) may be paid actors in order to boost its mindshare.


Semantically named classes is a lot of effort to come up with, and don't really help much. One of the big selling points is that you can quickly style things without having to name every HTML element.


Thanks. I've commented on this elsewhere, but I think there's probably a mismatch in my thinking on the granularity of naming. I don't literally name every single element I'm styling. I agree that would lead to some very contrived names. I've understood semantic naming to apply to what, for lack of a better term, I'd call a component and then use ancestry rules to style within that "component". If you need to customize it, you can use the cascading part of CSS or use a pre-processor like SCSS to mix in and override things. I've found that leads to easily identifiable rules.

Of course, the trade-off is if you completely change the structure of that "component" you'll need to update any ancestry rules. But on the naming front, I'd need to come up with an identifier or logical class name to find the element in JS anyway, absent something like React that sort of controls the world.


I have 20 years of front end dev work and consider myself a CSS novice as I think the overwhelming majority of people are.

If you haven’t read and understood the spec then like most people you don’t know CSS, which is fine. It’s not a question of knowing what say conic-gradient does, but how browsers use CSS in depth.


> I hate Tailwind with the passion of a million suns. It's loved by novices and those who don't know what they're doing, almost exclusively. They claim they know CSS, but will fail after any casual test.

As a long term front-end dev who likes tailwind, I find this fairly offensive. Your second and third points sound like problems with CSS in general.

> Vanilla CSS using `:root` declaration, a fixed base size (using `rem` and `em`), CSS variables and `calc()` are SO powerful.

If I'm using tailwind on a project, I don't want more power. I want less. I want simple constraints that give me solid basic styling without constant adjustment. The adjustments I _do_ want to make should be easy. The options available should be good options, not all options. I don't feel like fussing with box-shadow for the umpteenth time to get something that looks nice, or poking at my margins or padding or gap until it works with the rest of the page. I don't want to come up with a grid system again, or a color set, or a bunch of variables to extract these from the custom classes so they see proper reuse and are easily available; I want them already done.


I don’t think you realize that there are a lot of old school developers such as myself (going +25 years) who’s simply looking for quick and dirty front end tools that simply work and get out of the way. Tailwind does exactly that.


I'm in the same boat, and Tailwind looks like something that would get in my way (or at least becoming annoying very quickly.) I don't want the equivalent of inline styles all over the place.


This is probably the main thing that puts people in one camp or the other.

Before Tailwind came along I very often found myself just wanting to put some inline styling in various places. It's so much easier to just add some CSS to an element than having to think of a class name, think of where the best place would be to put the class, etc.


How does it not get in your way? If you ignore it?


It gives you a consistent design system with sensible defaults and easy-to-use (and rather memorable or easy to deduce) utility classes.

Unlike the yet-another umpteenth bespoke `.card-header__buttons .card-header__button--secondary` that no one can remember and create an umpteenth+1

Tailwind works great when your site/app is a collection of components


`.card-header__buttons` that example makes no sense unless you use global CSS/SASS, which very few projects do anymore. Component CSS/SCSS or CSS-in-JS are very common which resolve this scoping problem.

So why bother with a DSL like Tailwind over Component CSS/SCSS for example?


How do you enforce consistency in CSS-in-JS? Paddings, margins, sizes, colors?

Everywhere I've seen CSS-in-JS used, everywhere it's people busy writing bespoke styles in every file.


By having a style guide with variants, StitchesJS pioneered this but there are others like Vanilla-Extract. With TypeScript, you can then enforce that only a certain variant can be used with a certain component at compile time, basically making sure that your design style guide gets mapped 1:1 to your code.

https://stitches.dev/


Hm. I see the same CSS-in-JS with a sprinkle of string interpolation for some variables :)

I may run into the same issues as the other CSS-in-JS solutions (I only encountered stitch once, and not for a long period of time)


Unlike other CSS-in-JS though, the way it's made, via object properties, enforces compliance to the design system. In things like styled-components, it doesn't.

Doubly so when using TypeScript, as with Vanilla-Extract: https://vanilla-extract.style


> the way it's made, via object properties, enforces compliance to the design system.

The link literally has this:

``` export const className = style({ display: 'flex', flexDirection: 'column', selectors: { '&:nth-child(2n)': { background: 'aliceblue' } }, '@media': { 'screen and (min-width: 768px)': { flexDirection: 'row' } } }); ```

Which is indistinguishable from any other CSS-in-JS which has hundreds of these one-off things scattered everywhere. With a theme somewhere exposing another hundred or so variables of varing quality. And all these solutions basically converge on Tailwind (or other utility-first CSS approaches):

``` export const hero = style({ backgroundColor: vars.color.brandd, color: vars.color.white, padding: vars.space.large }); ```

is nothing more than `bg-brand color-white p-4`

But TypeScript support is definitely nice.


Sorry, could you reformat your code? I can't read this well.


Sorry, I keep forgetting that HN is weird with line breaks

Re-typing/re-quoting:

--------

The link literally has this:

    export const className = style({
      display: 'flex',
      flexDirection: 'column',
      selectors: {
        '&:nth-child(2n)': {
          background: 'aliceblue'
        }
      },
      '@media': {
        'screen and (min-width: 768px)': {
          flexDirection: 'row'
        }
      }
    });
Which is indistinguishable from any other CSS-in-JS which has hundreds of these one-off things scattered everywhere. With a theme somewhere exposing another hundred or so variables of varing quality. And all these solutions basically converge on Tailwind (or other utility-first CSS approaches):

    export const hero = style({
      backgroundColor: vars.color.brandd,
      color: vars.color.white,
      padding: vars.space.large
    });
is nothing more than `bg-brand color-white p-4`

But TypeScript support is definitely nice.

----


This video from Stitches shows their API well, which VE copied with their Recipes plugin, code example below (timestamped to 31:07 in case the link doesn't work) [0].

    /// Define variants for a specific component, such as a button
    export const button = recipe({
      base: {
        borderRadius: 6
      },
    
      variants: {
        color: {
          neutral: { background: 'whitesmoke' },
          brand: { background: 'blueviolet' },
          accent: { background: 'slateblue' }
        },
        size: {
          small: { padding: 12 },
          medium: { padding: 16 },
          large: { padding: 24 }
        },
        rounded: {
          true: { borderRadius: 999 }
        }
      },
    
      // Applied when multiple variants are set at once
      compoundVariants: [
        {
          variants: {
            color: 'neutral',
            size: 'large'
          },
          style: {
            background: 'ghostwhite'
          }
        }
      ],
    
      defaultVariants: {
        color: 'accent',
        size: 'medium'
      }
    });

    /// Use in code
    <button className={
      button({
        color: 'accent',
        size: 'large',
        rounded: true
      })
    }>
      Hello world
    </button>
Basically, unlike other CSS-in-JS libraries, you can encode your design system into something called variants, then you use them by calling the function `button()` to generate a class name.

Notice how `button()` takes in certain properties and only those properties with their corresponding values (enforced by TypeScript). If you try to put in `button({ color2: 'testColor', ...})`, TS won't compile.

In this way, you can take the design team's ButtonSmall, ButtonLarge, NeutralButtonLarge etc and codify them into TypeScript such that you literally can't make a mistake. You simply can't do that in Tailwind, unless you use some outside plugin I assume.

(Note that VE also has a Tailwind-like atomic CSS API called Sprinkles, but since I don't like Tailwind I don't use this either).

[0] https://youtu.be/Gw28VgyKGkw?t=1867


> How do you enforce consistency in CSS-in-JS? Paddings, margins, sizes, colors? JavaScript variables, css variables? Both can be used. It's pretty trivial tbh.


That's the same problem I see with tailwind.

I prefer a global set of styles and more specific selectors to override styles where needed. It may not work well on projects with many teams but for the projects I typically work on it's fine.


> That's the same problem I see with tailwind.

The thing with Tailwinf is that it already has a decent design syste encoded in its utility classes. It's very easy to slap a p-2 for a standardized padding than trying to remember what it was when you write the next component with `{ padding: 2 2 2 2 }`

> I prefer a global set of styles and more specific selectors to override styles where needed.

That's kinda what I mean. In all projects I've seen you have a global stylesheet with a bunch of classes that no one remembers, and hundreds of one-ofs in every component either reimplementing stuff from scratch or serving as an `!important` of sorts


> Spamming utility classes causes horrible git commits, git history, git difference checks;

As opposed to what? This is still an issue with any kind of utility class, where the alternative is inline styles or CSS-in-JS, both of which are substantially worse.

> Conflicting utility classes aren't clear, and sometimes the order cannot be trusted;

This is fixed in the IDE with tools like ESLint. I have VSCode setup to automatically reorder Tailwind classes, and it works great. (It even moves broken classes to the front of the list, so you can immediately identify if they're working or not)

> Replacing `p-4` with `p-4` will require you to replace its occurrence all over your app, sometimes affecting thousands of matches. And since there is no context to it, it'll be hard to JUST replace them where you would want them to.

This is an issue with CSS in general, especially CSS modules. Plus, if you use tailwind correctly (with @apply) you only have to update it once.

> Vanilla CSS using `:root` declaration, a fixed base size (using `rem` and `em`), CSS variables and `calc()` are SO powerful.

With tailwind, you still have access to `calc()` and variables with their bracket `[]` syntax. It's just much easier to do everything else.


> if you use tailwind correctly (with @apply) you only have to update it once.

"Whatever you do, don’t use @apply just to make things look “cleaner”. Yes, HTML templates littered with Tailwind classes are kind of ugly."

That is straight from the Tailwind docs: https://tailwindcss.com/docs/reusing-styles

Using @apply a lot just reinvents CSS; that's not the point of Tailwind.

I can write similar comments about the rest of your statements, but I think you're too narrow minded to accept any input so I won't bother.


> If you’re going to use @apply, use it for very small, highly reusable things like buttons and form controls — and even then only if you’re not using a framework like React where a component would be a better choice.

I would say the documentation adequately responds to your complaints about re-usability then? Use a component, use the IDE, or use @apply. The notion about needing to change classes 1000 times for a small edit just isn't true (I've never had to do this w/ 2+ years of tailwind).

> I think you're too narrow minded to accept any input so I won't bother.

Making some sort of ad hominem attack about how I'm narrow minded is useless. I'd love to hear your input.


Sounds like you don't know how to use Tailwind. Any front-end app is almost certainly already using a templating language, so declare your aggregate shared styles as variables there. Now you don't have to repeat your long style declarations in every context, and your 100 different markup files don't change if you want to make a minimal stylistic tweak (handling your point 1+3).

Yes, you can do this sort of thing in CSS and SCSS, but you're already using a templating language that can do this job, so why are you pulling in yet another tool (SCSS), or why are you requiring the mental context switch to yet another language and another file (CSS) when you can stay directly in template you're actually working on?

In other words, by removing CSS from the full stack of template language+programming language+HTML+CSS+JS, you reduce the cognitive overhead and context switching needed to actually get stuff done. If you add htmx you can also remove most of the JS too, thus reducing cognitive load even further.


Happen to have a blog post link handy? Would like to see this strategy in more detail. With something like Django if possible.


Never used Django, but I see lots of videos/talks on Youtube discussing the combination of Django+HTMX+Tailwind (and sometimes also Alpine.js). Looks like some of them use Tailwind classes inline, but I assume Django has a way to bind from a model, in which case you can declare global static models for standard styling, like text inputs, charts, tabs, etc. and have those accessible to views.


I've been working with CSS since floating grids were a thing. I've worked on projects with SASS/SCSS doing the whole BEM thing and then using CSS/SCSS modules. I have a good working knowledge of CSS. I like Tailwind. It's system for colours, and spacing is a good sane baseline, and you can customise them to conform to your design system if you want. CSS grid makes layout much easier in general, but their utility classes make it trivial to express your grid layouts.

I can't say I've really had many issues with git diffs or conflicting classes. I'm not entirely sure why you'd replace blanket `p-4` all over your app. Are you making some changes to the spacing in your design system? Why not change it globally and let it propagate?

I'd tend to agree with you that (S)CSS modules are a great improvement over the completely detached styling of the past, but I still think Tailwind has a lot to offer with its prebaked system and dead code elimination.


I'm not super up to date with frontend development trends, but when I used it recently it really went against everything I've ever been taught about separation of concerns.

Like, I thought we were supposed to keep our content and styles separate, not mismatch them all together with a thousand utility class imports.


This has changed a number of years ago.

Basically it's a component-mindset; create some file with everything in it so that in the rest of the app you can just use `<MyButton>...</MyButton>`.

The `<MyButton />` file takes responsibility for everything it does (logic, handlers, markup, style).

However; this does not mean these files should grow large. Instead when things become too big (say over 250 LoC) you split up responsibilities by sub-components.

It's basically dividing problems in ever-smaller problems, but NOT by separating by technology (html/css/js).

Separation of concerns is horizontal slicing of the app architecture, components however slice the architecture vertically.


Consider whether those lessons were actually informed by evidence. Tailwind is not like inline CSS, the latter has a number of downsides because it doesn't permit you to access all of CSS, where Tailwind does.


The code to content ratio goes for a toss as well resulting in poor seo. Many say that it should be used to make components and not spam with utility classes. I'd rather use CSS modules and write CSS directly as it provides far more options.


> 3. Replacing `p-4` with `p-4` will require you to replace its occurrence all over your app, sometimes affecting thousands of matches.

I'm not necessarily a big fan of Tailwind, but this is a total strawman. Nobody would advocate having one big file with thousands of repeated classes or something. Just like it would be a strawman against the use of css variables to say that you'll end up with a bunch of global variables used in thousands of places that become impossible to change because you can't be sure you won't break something unintentionally. And in the extreme cases where it does happen, the latter scenario is often much worse of a problem IMO than having too much repetition, you can always get clever with find and replace to get through repetition.

Tailwind works when you use it with a component framework, you're not getting rid of all abstractions completely you're just moving them all to a single layer within the component class.

But both approaches still make it possible to shoot yourself in the foot and end up with a mess of unmaintainable code if you don't abstract things well.


So, I am a very experienced front-end developer with quite a bit of CSS expertise and I have been using Tailwind on a project, and I actually quite like it.

It lets me design things in browser in a way that nothing else I've ever used has. Precisely because of my CSS knowledge, I know how to do everything, it's just a matter of finding the class name for it (referenced post talks about having to look up docs a lot, I relate.)

There's also a lot of established UI patterns, so I don't have to reinvent the wheel. And I often do roll something up into a semantic class name when I know I'm going to reuse it a lot. For example, I have a .container that is a list of @apply'd tailwind classes. PostCSS gives a lot of flexibility.

But I mixed things. So I have media queries that affect that :root font size and that decreases the amount of element specific responsive work I have to do.

I should mention I'm also working in Svelte, which has the ability to scope local CSS at the component level. It's a delight. So I mix and match between utility classes and scoped (dynamic classnames get generated) styling.


You're welcome to hate tailwind all you want, but don't let that cloud your judgement.

Plenty of very experienced developers like it just fine.


I can't actually disagree with you on this – as someone who had a robust SCSS module workflow and a set of utility functions in place already, Tailwind makes a bunch of stuff worse and makes little obviously better. It's surprisingly clever technology, but I don't like what it does to my code, and I do not in practice see many of the purported advantages of using it.

But I will also say – you need to get over that. It's probably going to be ubiquitous, whether you like it or not. There is some justification for it, it offers some advantages for particular users and use-cases, and it's likely that those advantages are enough to outweigh the downsides in general use. It's not worse enough in the ways that matter. So I've embraced it in the past couple of projects and it's fine – at worst vaguely annoying, and I'm much happier not having to bother hating it any more. Life's too short.


I'm glad to see all the designers on here really hate tailwind. I hope they persuade my competitors not to use it.

I've been writing CSS since IE 5.5 and I was tired of the whole horrible mess. Tailwind goes against everything I learned about the reasoning and benefits of CSS' styled web page design. Tailwind is right. Tailwind is how CSS should have been.

I (and more importantly my customers) do no care about minor differences in padding, or that the markup looks ugly, or that it takes a few extra minutes to parse the git commit (made up for by massive time savings elsewhere). Tailwind gives you a quick way to get a design that looks good enough while maintaining control over the layout of the page.

Need something custom? No one is stopping you from writing your own classes to compliment the utility classes or from moving common combinations of utility classes into their own class.


See also Vanilla-Extract if you use TypeScript, it uses TS instead of SCSS to compile it down to pure CSS (I mentioned this in another comment as well, for reference).

https://vanilla-extract.style/


> I hate Tailwind with the passion of a million suns.

How much passion does your standard sun contain?


Enough passion for the feelings engendered by Macromedia Flash?


According to Wikipedia, it’s 15 megakelvins.


Those who make claims of the form "X language/framework is used by novices and the incompetent, almost exclusively" fall into two camps: 1) Inexperienced, insecure newbs, or 2) Assholes.


Tailwind is obviously not just loved by novices. I've done CSS since IE6 and I really like Tailwind. Your "best arguments" are not actually problems. If anything git diffs become clearer because you see immediately what was changed without having to jump between files and lines.

I wouldn't touch SCSS with a ten foot pole in 2022, especially not for React where literally any CSS-in-JS library is better, and I used to love SCSS.


Do you favor putting multiple tailwind classes on an element or creating your own CSS classes with an "@apply" and what tailwind specifics you want for that class?


Same. I love Tailwind. Been writing CSS since not long after it was invented. Tailwind solves almost every problem I've ever had with CSS. It's :chefskiss:


Your comment would have been radically better if you'd omitted the first three sentences.


I think you miss that using components or tailwind is not exclusive. If you have a reusable button than you are supposed to make it a reusable component and have it styled with tailwind at one place only. So your concern doesn’t apply at all.


My best argument against it is whatever the learning curve, you're better served just spending that time learning to write CSS instead.


My understanding of CSS has gotten a lot better from using Tailwind.


Same here. The CSS that each Tailwind class corresponds to is very visible in the documentation itself.


Have you reached a stage where you no longer feel its a good idea or are you sticking with it?

I considered using it for a few projects but once I start I feel it too much work with no payoff.


This has been my experience as well. Tailwind is a good pair of support wheels, but it gets annoying after a while, and is difficult to maintain long-term.


What make it difficult to main long term for you?


It's not a replacement for CSS, it's an extension of it.

You shouldn't use Tailwind if you don't already know CSS.


I'd be very surprised if there are many devs with a deep understanding of CSS using Tailwind.


I'm sorry but this is just a crazy thing to say. You cannot use Tailwind without understanding CSS.

Take the example `block`. Ok so a junior FE dev is supposed to use this class without understanding CSS? How?! It just maps to `display: block;`. It hides literally none of the complexity.


I don't agree with the parent poster, but I would say that there is a large portion of frontend devs that "don't understand css" at a very deep level. For every 1 that does there are a good 9 or 10 that just get enough to get by.

That said -- you need to know the average amount of CSS to be successful using tailwind.


>It hides literally none of the complexity.

If it didn't hide any of complexity you'd be writing CSS.


Nope, it saves me (a) keystrokes and (b) design effort. The restricted subset of color options, for example, lets me use a predictable color system out of the box without having to think about it. It's not about removing "complexity" in the sense of understanding how CSS works.


With a stylesheet you have the styles in one place, with Tailwind you repeat it on every element. Yes components and copy/paste are a thing but I'd be very surprised if you're having to manage less style code with Tailwind. As for design effort, setup a few primary/secondary accent colors as global variables.


Not exactly - with Tailwind you aren't repeating the design decision, only the scalar value. So `border-2` might mean a border width of 4px or 6px or whatever, but that's the design decision that is encapsulated within the theme configuration.


Well... you can bumble through using tailwind without understanding it much in the same way you can bumble through using CSS without understanding it much.

There are plenty of people bumbling through with both.

But understanding the CSS is needed to use it reasonably well.


I'll give you an example of one:

Adam Wathan, the creator of Tailwind


Hard disagree on that one. You can learn Tailwind in an evening.


When you pick up tailwind, you will automatically learn vanilla CSS along the way.


I like Tailwind, but I am not sure I'd recommend it to someone without prior experience and deep understanding of CSS. In my opinion, abstractions rarely teach us the underlying layers of a technology.


> I like Tailwind, but I am not sure I'd recommend it to someone without prior experience and deep understanding of CSS. In my opinion, abstractions rarely teach us the underlying layers of a technology.

I started with Tailwind and as I learned CSS along the way. Obviously people will look up stuff on MDN when they are stumbling on terms they didn't know about.

The tailwind docs + tw-intellisense plugin [1] are pretty good, too. Especially since the extension shows the raw CSS behind the abstraction when you hover over a class name.

[1] https://marketplace.visualstudio.com/items?itemName=bradlc.v...


What would recommend for some sane CSS/SCSS defaults?


Oh, I guess I cannot edit it anymore.

*EDIT* for the above.

What are some typical defaults that I should start with for things like margin, padding, font size? What sort of items should I be initializing in ::root then overriding later?


I copied this from a specific project, so there might be a couple things missing, but here is generally what I use

https://gist.github.com/bdougherty/404b4ca33dfdbff48614b454f...


This might be helpful.

https://necolas.github.io/normalize.css/

Design decisions, though, are ultimately up to your taste and judgement.


Separating markup and styling doesn't work. One is too related to the other. Instead, we have discovered reusable components (what used to be called widgets back when we were doing native GUIs). You have your little bit of markup along with the styling information together for making a button or whatever. And then you use it (with suitable parameterisation). You combine those into bigger components etc etc.

I use the word 'components' because that's familiar to people, but there is a related concept in most modern frontend frameworks. In this world, tailwind makes so much sense. If you do it right, there is very much less in the way of shared stuff that needs find-and-replacing and you're keeping all the information you need to see how something is going to render all in one place.


Oh two tailwind hate threads today instead of the usual one at a time?

Everyone as usual saying "long class lists" even though the literal home page tells you not to do this and to make components or partial views or whatever your tool of choice calls it.

At least be inventive and follow the instructions before you start blaming something.


If everyone using a tool is using it wrong, it’s a poorly designed tool


Everybody isn’t though, just the people who are complaining.


This is exactly it, and the person you replied to should have known this.

You simply can't look at people not reading the docs (or even the home page) who then moan about how the thing they are misusing isn't working - and then conclude it must be the fault of the thing instead of the people misusing the thing.


Some people would rather learn every CSS rule than spend 10 minutes reading the Tailwind docs.


The homepage has multiple animated examples showing the use of utility class spamming.


It's a lot of work to create components just to reduce CSS duplication (i.e. to create more components than you would make if you used traditional CSS classes.) Not always a good solution.

I do like Tailwind, for the record. It would be nice if its allies and enemies would both admit it makes tradeoffs.


It's only slightly more work than creating re-usable CSS.


I never read the docs (ok, I do, eventually) but I actually never saw that "tidbit" but it just seemed intuitive that having a million classes wasn't good... but that's why I almost always make what I need to make and then start breaking it down into components. Or, if I have a good plan, start with components. I've never had to use an absurd amount of classes.

What is the alternative?


The holiday season wouldn't be complete without the HackerNews Humbug


Slightly off topic, but you can chain your language selectors in VS Code settings so that:

    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
Becomes:

    "[javascript][typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    }


Wow thanks for this as it's not at all mentioned in the help text for these (Configure editor settings to be overridden for a language.). Also what a strange choice for multiple languages since there can clearly be conflicts this way:

    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[javascript][typescript]": {
        "editor.defaultFormatter": "something.else"
    }
If they're going to allow conflicts anyways, just invert the structure so you at least get to use normal arrays:

    "editor.defaultFormatter": {
        "esbenp.prettier-vscode": ["typescript", "javascript"]
    }
Though, I guess the logic is that the other format allows you to override ANY setting based on language.


I'll be honest here, after working with tailwind for 7 months I really hate this piece of tech and really regret picking this company.

I thought it wouldn't be a big deal but after seeing unnecessary long walls of class names I'm really tired. Anything other than basic styling is an absolute pain with tailwind and I don't understand why /my company specifically/ can't just use typestyle/styled-components/css modules to separate walls of text from components. With time, I degraded to the point I where I use classnames library just to split longer class string into 3-4 smaller ones so I can split the component into multiple rows to make it look better


interesting take! I started using tailwind with the classnames library early on and found it to be a really nice fit for my purposes. Also very interested in more tailwind-specific tools like tw-classed[1]

[1]: https://tw-classed.vercel.app/


If you dislike walls of class names, you could consider using Tachyon's style abbreviations like we do in Truss:

https://github.com/homebound-team/truss


From that link:

`<div css={Css.mx2.black.ifSm.mx1.$}>...</div>;`

That must be one of the most horrid things I've ever seen done to CSS.


Yep, that looks like what a few seconds of my vim typing looks like but tossed into source code. Ill take the wall of tailwind any day over that.


You're not wrong re vim. :-)

Definitely use whatever you like, but just like vim bindings, which look "omg wtf" to newbies but then become "cannot live without" once you learn them, once you learn the Tachyons-style abbreviations, I/we find that they fade into the background/become 2nd nature, and the succinctness ROI is worth it.

But happy to have you disagree/stick with TW.


I enjoy using tailwind, but I've only used it for side projects. Not sure if I'd use it for productionbfor the wall of class names issue you've mentioned. It can also be a big pull to swallow for many people, my professional recommendation is that almost always fimilar technology is better for devs.

The goal of tailwind is the DX, and it seems to only acheive that with people that enjoy it. I know the statement seems obvious, but there are instances where you don't like a language or a framework, but it doesn't fight against you hence why I consider that a good DX.


I have the same thought. Bootstrap is still my favorite choice.


Yeah, I used Bootstrap for a long time, and got used to how it works.

I wanted to like Tailwind, but it seems a bit disingenuous. It's like using inline style tags, just with shorter names.

From time to time I've made an effort to learn how CSS works, but after a while I forget the details. It's more productive if I can browse a catalogue of visual examples, with concise markup that is easy to copy and paste.

Bulma seems like the more "modern" take on Bootstrap: https://bulma.io/


If you like styled components, you can use this in a similar way, applying tailwind classes instead of CSS: https://www.npmjs.com/package/tailwind-styled-components

I like that it helps reduce the walls of class names since the classes are on their own lines in the tailwind styled component.


I do dislike long walls of classes as well. However, I'm yet to find an alternative that offers the same benefits Tailwind CSS offers without such compromises.

Wrote about Vanilla-Extract shortcomings here https://news.ycombinator.com/item?id=33793944


I am tired of 'analysis' that looks like this:

> Lightning-fast build times since March 15, 2021

Just put some numbers in there. Average code base size, average build time improvement since version x, etc. I can be the judge of whether that is "lighting fast" to me


> Just put some numbers in there. Average code base size, average build time improvement since version x, etc. I can be the judge of whether that is "lighting fast" to me

Fair enough. The video I linked to has some numbers Though. It used to take 19.44s to produce a 12MB CSS file in development vs a 1.9s to produce 11KB CSS file since this feature was released.

I believe it's even faster now (v3.1.8) as it only takes 260ms to produce a 29KB CSS file in one of my projects with dark mode and lots of variants.


I think Tailwind is great but I agree with many of the issues raised.

I desperately needed a _language_ instead of a framework back when Tailwind was still a framework. As I could not get any response from them, I ended up rolling my own called Turbo CSS - this was like 2 years before their compiled version came out. Take a look if you want to consider alternatives.

https://developer.boomla.com/turbo-css

I'd compare the two as Tailwind being primarily a framework, a language second, while Turbo is a language first. For example, in Turbo you write libraries and reference class names in libraries via `my.btn` instead of polluting the global namespace. It also has first class support for classes, like you can do `hover:my.btn`, which Tailwind doesn't support (to my best of knowledge, it definitely didn't back then). Also, Turbo has zero global side-effects, which makes it great if you don't control the entire codebase (think CSS reset).


Great work scratching your own itch! Not as easy addition to the workflow as it doesn't have a CLI but I think that's intentional or not a priority for you.

I backed out from maintaining a big Tailwind CSS plugin let alone create my own solution.

Similar to React, I don't think Tailwind CSS is easy to replace regardless of the slightly better alternatives that exist or might popup up due to its established community.


I've been using it regularly for the last 6 months and like it a lot. The blog points out the biggest switching over issues. Converting from rem to px can be annoying if you cant do that math in your head and at first you are often looking up the "simple" tailwinds way to write a css property that you already know.

I pretty much always have the tailwinds docs open and thankfully they are very good and searchable.

It does get a bit wordy when you try to do too much with it though. The nice part is you can always just plop a class in instead of doing everything with tailwinds.


Regarding rem <-> px conversions, I follow this intuition: _multiply by 4 gives you px value, dividing by 4 gives you rem value._

`h-8` utility is height of 8 * 4 = 32px or 8 / 4 = 2 rem.

Since design systems are supposed to be consistent, 4px is typically considered 1 unit of distance.

However, there are times when I might encounter values in Figma (or get inputs from designer) that won't allow me to use this easily.

Say, designer wants a max-width of 252px on an element. I usually use Alfred app on my work MBP to divide it quickly by 16 (since 1 rem is 16px under normal font-size settings), but you can use any calculator, even the one in Google search or DDG search.

It turns out to be a fraction, and in this case, it's 15.75 rem.

I use utility like `max-w-[16rem]`, closest consistent dimension that's a multiple of 1rem, and ship a pull request preview to the designer, asking for design feedback.

Chances are, designer agrees to stick to 16rem, and we ship it as is. If this width of 16rem, or closer values within the 250px vicinity, are used in other places in the design system components or our app; I'd typically add that to the Tailwind config as well.

Most of the time question of rem <-> px conversion comes into picture because we look at design dimensions in Figma / Sketch / Indesign etc. tools, and try to implement the same in our UI code.

But this would only slow down a developer, switching back-and-forth between design and implementation.

What I find more productive while prototyping a UI (or a smaller component), is to just "eye it", instead of getting actual pixel-values right at the first go.

From just eye-ing it, I can make a guess if it should be h-3 or h-4 (you can also guess the right value using a binary search style heuristic), and if my implementation looks bigger (or smaller) than the design, I'd adjust accordingly.

Only after I've implemented a basic prototype of the UI component, I'd cross-check with the design tool, and edit some utilities if necessary to get as close to the design as possible.


Forgot to mention that I no longer do these conversions for the most part, instead, I scroll down the suggested values from the Tailwind CSS IntelliSense VSCode extension since it displays the equivalent values in pixels.


While we're here, what are the top CSS or front end frameworks that are just static CSS files (maybe a single JS file too) that I can either download or hotlink from a CDN? I usually fallback to bootstrap for personal projects or prototypes because it is as easy as including a CSS file and a JS file. No npm install or compile steps required.


I love using just the grid/breakpoint part of Bootstrap and use it as grid.css. No need for complication, no preprocessor needed, etc.


I haven’t seen it mentioned in this thread but for those who like Tailwind in theory but hate all it’s drawbacks you might be interested in Open Props (https://open-props.style/) which is put together by a member of Chrome’s devrel team.

The first 10 minutes or so of this video is also a decent introduction it seems which will help put this project into context for you so you can see specifically what kinds of problems it solves in a way that Tailwind doesn’t and vice versa. https://youtu.be/O53MwmolKP4


That website has visual bugs and lag in Firefox. When I combine that with my general stereotypes about Chrome devrel I think this isn't for me.


I've been looking for someone to mention open-props and I really enjoy using it.


The problem for me is that Mr Wathan misrepresented « Good CSS » in his essay. His examples are BAD CSS, not good CSS. He then sets out to correct a problem with CSS that HE created by writing dirt poor CSS.

`.author-bio > div > h2 { color: red } is NOT good CSS. The div part has no reason being there (why would the div change the color of the title ?). After presenting this auto-created problem, he sets out to solve it with BEM, where you need untold numbers of classes for everything. So now, CSS has a naming problem, created to solve a non-existing auto-created CSS problem. And Tailwind now solves this pseudo-problem.

Apart from this, the thing that really bugs me is that every advantage claimed (apart from one) is also non-existing because if you do your CSS right, the same claimed advantages arise in pure CSS. Design tokens ? No need for json, custom properties are all you need. Specificity ? Cascade layers, pseudo-selectors are here for you.

The only "advantage" I see is the possibility of working in one file only rather than a couple of files. And that, for me at least, is not a real advantage but only a justification for being lazy. There's no way the price of all these processes and dependencies complexity are worth the laziness-added value.


Haven't seen it mentioned, but if you like Tailwind, maybe take a look at UnoCSS: https://github.com/unocss/unocss

It is like the next evolution of Tailwind. See this blog post for full explanation: https://antfu.me/posts/reimagine-atomic-css


Also, well looks like a lot of people here don't like this style of CSS coding.

As someone who's been writing HTML since Netscape days, I find Tailwind/UnoCSS plus a modern web framework (React/Vue/Solid JS) the most productive way to develop websites ever.

The main thing is in a single React or Solid JS component, you can have all the code, html and styling for a component in one file or most of the time in one function even.

No more having to deal with jumping around between template and css files, having to search for where a class or a styling is defined, or trying to figure out how to structure or even name the css classes. No more having to make decisions like "Should I put this styling in this class or that class or in a classless selector."

For me, it just saves too much time to not use.

I was skeptical at first, but once I started using it, I could never go back.


Tailwind certainly has some advantages and nice features, but the length of the class string when you need something common but complex like a custom styled checkbox including focus states, various hovers, media-queries, etc gets absurd and far more difficult to read, debug, and update.

I think one of the primary reasons Tailwind is so popular is how poorly React deals with classes and styles. In other frameworks/libraries like Vue/Svelte CSS is treated as a first-class citizen with a lot of nice quality-of-life improvements that React teams are forced to implement using awful CSS-in-JS solutions or with Tailwind. You get single-file-components, scoped styles, etc, all out of the box and without having to learn a new abstraction.

If the dev world wasn't a React monoculture (and more devs appreciated CSS deeply) I think you would see significantly less interest in Tailwind.


Having used both Tailwind and a more traditional scss system in the past. I think my personal favorite is styled components[0]. It strikes a good balance between not having to move between many files, like you might find your self doing with traditional css/scss system. While being declarative and semantically easy to read.

It is not perfect by any means, and in larger components/pages it can lead to some pretty long, difficult to reason about files. But its the best workflow I've had the opportunity to work with so far.

[0] https://emotion.sh/docs/styled


You could combine styled-components/emotion with something like open-props[0]. You get all the power that comes with CSS and a set of variables to help you create a consistent design. That's how I manage my styling.

[0] https://open-props.style


Ooo, that is very interesting. I will have to give that a shot, thanks for sharing!


Ongoing related thread:

Tailwind is a leaky abstraction - https://news.ycombinator.com/item?id=33787218 - Nov 2022 (272 comments)


I have been working with HTML since I was 11 — 20ish years. I thought I knew most things. text-decoration-thickness? Nope. I have been adding border-bottom to spans for far too long it looks like.


I started html when I was about 11 so I’m at 25 years.

I just discovered last night there’s an aspect ratio property…

https://caniuse.com/mdn-css_properties_aspect-ratio


It’s pretty new though. Less than 2 years I think.


I was just shocked. Last night I’m like “hmm need to maintain aspect ratio on this box. Maybe I can use calc and vh or something.

And this shocked up in Google. Rocked my world. I wanna go back across a bunch of products I’ve worked on over the years to fix stuff using this!

One of my favourite features now!


It is definitely magical! So many hours saved trying to find the right responsive break points for those particular card UIs.


aspect-ratio is a new standard, so don't feel too bad about that


Those are pretty new. text-underline-offset is another one that made my "fake" underlines obsolete


Also remember text-decoration-style, text-decoration-color and text-decoration-skip - setting this last one to 'ink' will make the underline, ahem, skip over the glyphs with descenders, adding to legibility


That's a good side effect from using Tailwind CSS docs. They curate new CSS properties and add the most important (in their opinion) once they're supported by all modern browsers.


no problem. I work every day with CSS ... you can't know every new feature unless you have at least one use case to get know the new feature. The "old" technics are still working but if you want the benefits of new technics like text-decoration-skip-ink you have to use the new ones. So as long as you do not run into such problems like "skip-ink" you do not have to learn ne new ones.


This style comes and goes. Utility classes. Then a framework like bootstrap comes up. Then it’s back to utility classes.

I feel like I’m taking crazy pills


> I feel like I’m taking crazy pills

You're not. If you look hard enough, long enough, you'll see this same pattern in every corner of programming. Round and round, never forward.


Funny you said that cus there’s another “look ma no framework!” Thread today.


With love we'll find a way, just give it time


> If I could do it over again I'd just use pixel values, like h-12 is .75rem (which is 12px by default).

(External quote from the maintainer)

Bit of a tangent, but what's the play here? The library is worse than it could be because of this reason. And of course you have adoption and don't want to break people's shit.

Seems like we're missing out on "what could be."


People should not have the power to set paddings of less than 4px, and only do so in increments of 4.

If you do it this way it becomes harder for anyone to go wild with every variant of padding under 8/12.


I try my best to use tailwind JUST as a utility library, and really utilize the @apply postcss function. I don't want 1000 classes in my HTML, I want to make re-usable classes filled with the tailwind styles from it's utility classes.


Nice to see this on the front page at the same time that a negative about Tailwind article pops up. Especially since I'm currently looking at whether to start using Tailwind. It's good to read multiple, conflicting, opinions about something.


Oh, someone uses Prettier or ESLint ? I removed those from my code, because those caused massive amounts of syntax fix commits. I recommend not adding both, they will fight with each other.


The root of the issue is that CSS itself has no good design pattern that scales well.


Does that blog have an RSS feed?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: