Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm not sure I get Tailwind still. Doing everything with utility classes and OOCSS / BEM are things we stopped doing literally decades ago. CSS modules still seem to solve every problem Tailwind solves, and better. CSS modules combine the power of global utility classes with locally styled components/locally scoped classes, and compile to static stylesheets, a requirement for performance. I'm not sure how Tailwind works, but any CSS that's built at runtime and JS and inserted into the DOM dynamically should be avoided, and is an example of favoring developer experience over end user experience. It's always surprising to me when the build process isn't front and center of any CSS framework, since that's the most important performance aspect. I'm not concerned about Tailwinds verbose CSS use since that's gzipped away, but the static stylesheet compilation aspect worries me if it's not front and center of the framework.

CSS modules let you use the full power and control of vanilla CSS, without having to worry about styles bleeding across components. Sprinkle in your global utility classes for your design system and you're good to go. Or sometimes even better, abstract design into components like `<grid>` `<column>` etc and not even worry about the classname implementation.

I know I'm missing part of the picture, because of the hype and joy that people report from Tailwind. What part(s) am I missing that move folks from the power, beauty, and simplicity of CSS modules, to all-utility-classes-all-the-time Tailwind?



Seems like you are basing your opinions on some misconceptions. Let me try to clear that for you.

"CSS modules still seem to solve every problem Tailwind solves, and better."

- Not necessarily true. Unlike css modules, tailwind removes the whole "think about a name for your class" mindset, reducing friction from the development process. It also unifies some base level design decisions like spacing and colors, which developers would have to rely on "best practices" otherwise, which don't necessarily get strictly enforced.

"I'm not sure how Tailwind works, but any CSS that's built at runtime and JS and inserted into the DOM dynamically should be avoided, and is an example of favoring developer experience over end user experience."

- You are right, looks like you are not sure how Tailwind works. Tailwind does not build anything at runtime, it all happens at build time. Tailwind will compile only the things you need (using the new JIT mode) into a css stylesheet which is sent to the frontend. Not much different from how sass or scss works.

"CSS modules let you use the full power and control of vanilla CSS, without having to worry about styles bleeding across components."

- Tailwind does not stop you from using vanilla css, but in most cases you do not need to. As per their website, you can think of it as an API to use parts of CSS, instead of CSS replacement. I think you are confusing Tailwind as a replacement for something like CSS Modules, but those two are completely unrelated. You can still use CSS Modules while using Tailwind. Think of it as an api to your design sytem just like you could think of an ORM as an API to your database.


Aside from all of this. Try to use tailwind for 1 side project. You’ll see the difference of productivity when you remember most of the tailwind classes as opposed to having to open another css file, create a class, then reimport them.

I just can’t go back without tailwind.


I used Emotion for a few years (CSS-in-JS). I was so in love with it that I never thought I'd change to something else. But Tailwind converted me. I can't go back either, it's too powerful.


You can use both and gain the benefits of Tailwind-in-JS to dynamically apply utilities and arrays/objects of utilities composed together, all using a nifty wrapper library called "twin.macro".

It sits on top of Styled Components or Emotion (your choice), and uses your project's Tailwind config faithfully, minus a few of the plugin features.

It eliminates the need for JIT or PurgeCSS because it compiles Tailwind's utils with a drop-in Babel macro, eliminating the need for PostCSS.

As a lover of vanilla Tailwind in Vue and Svelte projects, Twin Macro has made using it in React 10x better and I haven't looked back! :)


+1, tried Tailwind and liked it, but when I found I was able to use CSS-in-JS with it with twin.macro i was amazed. Now is used on all my side projects.


Just checked it out, looks nice. What I'd really like is a more concise way to apply tw classes in jsx. Something like this:

  <Button
    h5
    w6
    bgBlue500
    textWhite
    hidden={props.hide}
  />


I'm curious how that would work with typescript since it's adding extra props like `css` and `tw` to components which don't exist in the types


It's Typescript-friendly, and if you use the Twin Intellisense extension for VSCode, it will autocomplete from all of the generated utilities in Tailwind, even the ones created from your own configuration of colors, spacing, etc.

Twin Macro has a few snippets of type definitions you can add to your project based on whether you're using Styled Components or Emotion, specifically to support these two props (tw and css).

They link to their Examples repo from the official readme with several projects using many different combos, with a handful of TypeScript ones.


JIT mode basically means it recompiles your styles before you can press reload on your browser.

Before they didn't have all colors enabled by default, because generating classes like `bg-blue-500`, `text-blue-500`, `border-blue-500`, etc. for all colors would increase the resulting CSS way too much. They did the same thing with variants.

With the JIT none of that is necessary anymore. Plus you can use arbitrary values because it's being compiled now.


And even that is less magic than it sounds. Unless it’s changed in v3, there’s just a regex matching stuff that could be a class name and building a rule for it.

    /[^<>"'`\s]*[^<>"'`\s:]/g


The other thing is, building css the traditional way, after refactoring and renaming things a bit, I usually end up with something that looks very similar to tailwind syntax anyway. So might as well have a flavour of that we can all refer to and agree on than each person having their own set of utilities and naming conventions. That way I don't have to explain or write elaborate comments - I can just point the person to the tailwind docs. So it solves that kind of problem for me.


> You can still use CSS Modules while using Tailwind

Recommended even. I use Tailwind with CSS Modules as a fallback when there's simply no way to achieve the result using Tailwind (rare) or it's prohibitively complicated/messy.

Also for CSS hacks if you need to support an older browser.


Based on what you've said, you're right, you don't understand it...the things you're comparing it to don't really make sense. Bem doesn't make sense to compare and neither do css modules. Tailwind is a stylesheet with css classes that do atomic things like changing border radius. It gives you a set of classes that allow you to build just about anything you need with just classes. When you compile an app that uses tailwind, it takes just the styles you use and puts them into a single stylesheet. So all of the things you claim to be wrong with tailwind aren't true. Tailwind is very performant because you only use the styles you need. It uses css variables for theming so there's no need to implement it in userland. It also uses css variables to do transforms which is very important because transforms are not additive in css yet. Even if you don't end up liking tailwind, the thing is executed really really well from the ground up.


I (not OP) get all of these, incl. the relief of not naming things.

But I still prefer naming things to the class soup and the excessive repetition.

ITCSS (Inverted triangle CSS) works with the cascade and results in clean, minimalistic CSS files.

I do not build SPAs though and do get that quick styling works really well for that scenario.

I do import Tailwind colors, spacing and sizes in SCSS maps for easy access and do like the standardized approach.


> ITCSS (Inverted triangle CSS) works with the cascade and results in clean, minimalistic CSS files.

The real Tailwind lightbulb for me was understanding that the cascade and actual CSS files don't matter anymore - you don't need them. No more inheritance, no more naming, no context switching.

I'm a big fan of ITCSS and was using Harry's methods before he coined the name but we don't need to fight the cascade any longer.

> I do not build SPAs though and do get that quick styling works really well for that scenario.

Admittedly, if you're not breaking down your UI into small components then Tailwind will get real messy, real fast.


Using Tailwind @apply to compose utilities into classes is a wonderful feature and gets you the best of both worlds! :)


There's three things:

1. It removes a layer of abstraction that's redundant if you use a component-based UI framework.

2. It provides constraints that act as guardrails against introducing inconsistencies into a design.

3. Its tooling is not magic and does not have runtime impact.

More detail at https://vincenttunru.com/why-tailwind/


I think Tailwind is multiple very different ideas in one library. One thing that Tailwind is is a set of primitives and design tokens that’s just slightly higher level than CSS, but still lower level than a component library. I think it’s pretty good at this.

Another thing that Tailwind is is an opinionated delivery mechanism for your styles, in this case, as utility classes that can go straight into your HTML. This is probably a big cause of Tailwind’s popularity, not because any one person necessarily loves using classes, but because it makes it extremely easy for everyone to start using Tailwind in nearly any imaginable web project starting at plain static HTML files and going up from there. This aspect of Tailwind is something I’m not a huge fan of. To me it feels like a step back from a lot of higher-level CSS tools (like many CSS-in-JS libraries) to just go back to concatenating magic string literals into my UI code. All the official Tailwind tooling (AFAIK) either just watches your codebase looking for these magic string and generating the appropriate raw CSS, or doing it in real-time with their new JIT compiler (which I admittedly haven’t investigated yet).


I agree with this. I think the "set of primitives" part is an interesting, perhaps great, idea. Makes it easy to enforce consistency across your codebase. But I don't like the delivery mechanism--chucking all the styles in with the markup feels like it makes everything hard to read: the HTML/component declarations are harder to read both in code and in the browser, the individual classnames are hard to read (both because the names chosen are often gibberish and because parsing a dozen of them in a row is difficult), and the styles are annoying to debug in the browser (now you have to scroll through a dozen different utility classes to figure out what's going on).

Personally I feel like a better approach is taking that same philosophy of design system primitives and executing it via something like SASS mixins, paired with single-file components à la Vue or Svelte. Then you can use better names (no need for brevity now), keep the styling separate from the markup (but still paired with it), and have a better experience debugging in the browser.


what do you think "high level" css-in-js libraries do if bot string concatenation, just watching your codebase with magic strings and generally ng raw CSS?


Plenty of CSS-in-JS libraries let you define design tokens somewhere and import them or access them on some theme object.


This doesn't answer my question.

CSS-in-JS libraries are not magic that summons CSS out of thin air. They basically do the same thing Tailwind does: they watch your code for changes, they re-build/re-create raw CSS by, yes, often using string concatenation etc.


> CSS modules still seem to solve every problem Tailwind solves, and better.

I agree with most of what you're writing, but in arguing this out with people who seem to be enthusiasts, what I think I've discovered is that while there are existing (hell, longstanding) unbundled technical solutions fully capable of solving the problems Tailwind does... they don't solve the practical problems of channeling a group into a good-enough design system, and in fact many people who've been doing CSS have never actually really used a design system (especially if their experience is solely recent, and definitely if their experience is only incidental in the sense that they're application devs first). And many organizations don't have roles where someone can focus on solving this problem.

Just-add-Tailwind may hit an interesting pit-of-success spot for a lot of people in this position, where TW provides the atoms of the design system to scatter in a just-in-time manner. Sure, not elegantly, but practically.

Personally, I'd prefer to work with people/orgs that don't see this is an optimum, but I might accept it as a situational local optimum.


I think this is not well understood.

Tailwind is great if you're a startup, or someone who is a webdev and also has to be the designer. But in a large organization, with a company-wide style book, and a design department, it's not a good fit.

Tailwind works for "I see this control in my head, and I'm going to code like this to make it happen." It's not really a good match for "I see this control from the design department, and I'm going to make it fit into the rest of the site codebase like this."


> Sure, not elegantly, but practically

Let's be honest, there isn't much elegance to the non-Tailwind solutions either. At the end of the day it's text input used by a rendering engine to style layout, it and your customers don't care how it got there.


Tailwind is global utility classes too. There’s no runtime aspect. It’s literally just css classes. The nicest thing is that all your variations exist, so you can do things like hover:font-bold. So you can see the rules immediately like with inline styles, but they’re more flexible.


At least finally someone using the dirty word "inline styles". It's like all the other comments here stepped right over that point from the grandparent. Only it's not inline styles, it's inline styles and you are Dennis fucking Ritchie in 1970 and your fingers hurt from the teletype so you are making up crude abbreviations for everything.


As an engineer in his 30's, I've been quite vocal about how much my fingers hurt after coding professionally for almost 15 years.

Tailwind is a f*king godsend XD


Side note, most of the time you don't want to make a font bold on hover as most fonts this will increase its length to account for increased stroke width. It doesn't transition well either.


I'm in the same boat. Started using Vanilla Extract (https://vanilla-extract.style) earlier this year and it's the best CSS setup I've ever worked with.


> CSS modules still seem to solve every problem Tailwind solves, and better.

I don't understand Tailwind either; but I find myself struggling with CSS modules when I need to override a CSS rule of a child from a parent. Like, say, my button should always be green, except in this context I want it to be purple, its font-size larger and its padding a bit different. With CSS modules, the parent component is unaware of the class name of the child component; so it cannot target that. Perhaps this should all be done with CSS variables; but then hell, how many CSS variables should my components expose? and besides, I am not even sure even they will completely solve this.


.button is the default .button-context is the context version

className={isContext ? ".button--context" : ".button"}

----

alternatively if you want many unrelated base rules that aren't color/padding as a baseline:

.button contains base rules .button--default default color/padding .button--context contextful color/padding

className={`.button ${isContext ? ".button--context" : ".button--default"}`}


Yeah, sounds sensible. What I've been doing so far was attempting to extend the base class like so:

    import classNames from 'classnames';

    ...

    className = classNames(styles.buttton, props.className);
But this leaves me at the mercy of the order in which webpack builds stylesheets (sometimes props.className will be defined after styles.button and my plan would work; other times it will get defined before styles.button, and styles.button will override the CSS rules of props.className).

Really wanted the extension of the base rules to work out; but I guess you are right: the className from the props should override entirely one of the classNames of the component.


Use Twin Macro for Tailwind-in-JS — you won't regret it!


I build tailwind exactly once and it gives me a static css file with utility classes. I get local scoping from scope classes ala Svelte.


With Svelte, I just maintain a small reset file and maintain a list of native CSS variables that I can use from my Svelte components.

I have not personally found the need to use Tailwind because of that.


I don't personally care for Tailwind, but I get the reasoning why people gravitate towards it: just as modern JS moved away from inheritance to composition, so is CSS.

There's a fundamental argument that inheritance is a mirage and things in large projects become much simpler with composition-based approaches. You don't really need to grok the inheritance chain with Tailwind in the same way you do typical CSS.

For my personal projects I'd continue writing my own CSS... but for teams, I'd go to Tailwind without a second thought.


> Doing everything with utility classes and OOCSS / BEM are things we stopped doing literally decades ago.

I don't understand what you mean by this. Literally decades ago would take us back to at least 2001. OOCSS, BEM, etc. were all created after that year. Wouldn't it be correct to say "Doing everything with utility classes and OOCSS / BEM are things we hadn't even started doing literally decades ago."?




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

Search: