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

I've used Tailwind extensively at previous companies and inevitably each one creates an abstraction that's akin to:

    const headerClasses = [(list of Tailwind classes here)];

    <header className={...headerClasses}>...</header>
because the complexity of reading and writing all of the classes is just too much. At that point, you've just reinvented CSS classes. Tailwind fans will tell you to not do this but if multiple companies are independently having the same problem and coming up with the same solution, the onus is not on the user anymore, it's on the creator to fix it. @apply can work but again it's really not recommended by Tailwind itself, for whatever reason.

These days I recommend learning CSS really well and then using Vanilla Extract (https://vanilla-extract.style), a CSS in TypeScript library that compiles down to raw CSS, basically using TS as your preprocessor instead of SCSS. For dynamic styles, they have an optional small runtime.

They have a Stitches-like API called Recipes that's phenomenal as well, especially for design systems, you can define your variants and what CSS needs to be applied for each one, so you can map your design components 1-to-1 with your code:

import { recipe } from '@vanilla-extract/recipes';

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'
  }
});

Impressive that OP still is using ReasonReact, I thought it was all but dead after ReScript.



I actually agree with this criticism, and it's the best criticism of Tailwind I've seen so far.

I still love using it, and will continue to do so. This isn't enough to stop me from using it. Normally I try to avoid this, and abstract the repeated bundle of tailwind classes in a component instead (it can be a container component or just some useful utility component to avoid having to repeat the same group of classes too much). This can have its own drawbacks, because having too many custom components that you need to remember to use in given situations (which is effectively how most design-systems would work anyway) makes the codebase less approachable.

And even still, we'll end up using the above trick (referencing a group of classes bundled into a property of an object somewhere else) from time to time, as well as the obvious option of just repeating classes as necessary. Referencing classes from an imported object is annoying too, because it breaks intellisense.

I'm honestly not sure what a better solution is though, because Tailwind has really sped things up for me compared to CSS/Sass/Chakra/Material.


Tamagui really aims to solve this problem and some of the usability problems of className by putting style props directly on the props. You can nest components and abstract them as you want and still get the Tailwind style shorthands except as TS typed tokens.

No worry about how you reference things or static-ness etc and the final output can be flattened down to just div + css even across module boundaries or when nesting multiple times.


Interesting.. so is Tamagui using react under the hood, but "hiding" it from you to some extent? Does it provide some helpers for responsiveness?


It’s just React through and through. It has an optional optimizing compiler that really improves performance even across hard to parse areas like nested conditional logic or spreads of items across module boundaries.


I believe the solution here is most definitely @apply. I know the Tailwind team seem to be against @apply (and even flirted with removing it), but having used it extensively to build complex (imo) sites and apps, @apply has been unavoidable and actually great to have.

The fact is @apply is there and if we're using it and there's enough push-back, I don't see why the Tailwind team wouldn't listen to that or create a workable upgrade path for those who wish to continue to use it.

So use the tools you have - don't avoid it just because the core team dislike it. They can't remove it in the version you have installed today (unless they have some magical way to reach into your computer/server)


The real answer is in CSS developing a proper supported way to do mixins.

I keep being surprised that noone bring this up in these discussions.


I do it with emotion (CSS in JS), specifically NOT using styled components and instead using the CSS prop which allows for composing styles more naturally, and it works great. Never felt a need to get my team on Tailwind since it’s already super productive with the right set of mixins.


I get you, the problem is that CSS in JS is simply not possible for the vast majority of web developers out there that work on a site render framework that is not JS ;)

I think this is the main reason why Tailwind took off so fast. The frontend tooling world forgot the majority of its users.


In general, framework owners _want_ you to become tightly coupled to the framework, whereas as a user you want to be able to move away from it more easily. I think this conflict of interests is behind recommendations like this, rather than sound technical reasons.


Why would framework owners want you to become tightly coupled to the framework? They're basically just offering a set of solutions that come with some tradeoffs, but they don't intentionally create problems for their users.


For framework owners that have a business built on top of their framework, that tight coupling is their "moat" that helps keep you locked in as a paying customer for longer.


Yeah I don't understand what the other solution would be. If you want to have reusable styles, eg "btn" class with all your defaults @apply is the obvious choice. Otherwise you'll end up with similar gargantuan CSS class spagetti and/or have to abstract away some generic components because handling the classes is just too much.


I was under the impression that you create a button component and apply the atomic styles in that component. That way you don’t don’t need a “btn” class, you just use the Button component anywhere you need a button.


What if you need to render your button component as an anchor element? Or as a div with role="button"? You will want to abstract your button styles somehow so that they can be shared between these three (or more) use cases. Sure you could experiment with using a polymorphic `as` prop to render your button component as any element, but if you're using TypeScript that can get complicated quickly, especially if you're then trying to use that component in conjunction with something like next/link or react-router/link. It's simpler to strip default styles from all your components, and then have a button class to add to any element you want to appear as a button.

BUT, then what if you want to add some kind of special behavior to your button that involves subcomponents, e.g. a loading state that conditionally renders a spinner inside the button? Or you want to provide convenience props like rendering an icon before or after the button text? Then a class is not enough.

If you're building a design system/component library, none of these options are simple and there are always tradeoffs.


This assumes you only have one button class, while you might want to reuse several that have minor differences.


You could do that but many of the minor stylings might not warrant a component, like a specific hover effect. For quick prototyping though it just is way more convenient to not to constantly break apart small html snippets into their own components.


Sure, I've already moved onto CSS in TS solutions like vanilla-extract above though, I like my typechecking in my CSS.


  > const headerClasses = [(list of Tailwind classes here)];
  > <header className={...headerClasses}>...</header>
Why wouldn't they extract the header as a higher-order component? That would make more sense even without Tailwind, and I haven't heard anyone in the Tailwind community advocate against that.


I think this is just an example but `header` is a standard HTML element. It's not always necassary to seperate every single element into it's own component. Every time you make a framework component you add overhead to the rendering of the app.


> It's not always necassary to seperate every single element into it's own component.

It's not necessary but in the given case it's obviously useful as there is more to abstract than just the html element - the styling.


> Every time you make a framework component you add overhead to the rendering of the app.

not true, separating components by concern can save you a lot of fiddling with memoization


Attributify mostly solves readability issue:

  <button
    bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
    text="sm white"
    font="mono light"
    p="y-2 x-4"
    border="2 rounded blue-200"
  >
https://windicss.org/features/attributify.html


It solves the repetition and collation into className, yes.

But I think the author's main gripe was that component abstraction was made difficult with Tailwind when you want to customize the component from the outside, by dynamically altering the styles through props (as often needed for contextualization in design systems), and not just no-prop components like Tailwind suggests: https://tailwindcss.com/#component-driven


> At that point, you've just reinvented CSS classes.

Sort of, except instead of context switching between CSS, HTML, JS and your programming language, you can now remove the CSS entirely. Less context switching is good.

I'm a Tailwind fan and I don't see a problem with that pattern for some cases. Obviously there are better ways to organize that should be preferred in general, but it's fine to do that here and there.


You still have to context switch between thinking about markup vs styling vs behaviour. The language you write the styling in barely makes a difference imo


Sure, but you don't have to switch between types of files, syntax, etc. "Barely makes a difference" seem subjective, so YMMV.


Tailwind classes are a syntax (especially since you can set arbitrary values e.g. `top-[-113px]`) and they still represent CSS. So really you're using a DSL with its own syntax, functions and directives on top of the CSS you should still understand. The main benefit is speed: you have a library of utility classes that are shorthand for common CSS patterns. If you are pretending that you're not writing CSS, you are forgetting how the browser is actually working -- eventually you may have to leave Tailwind and you'll find you've forgotten how to write good CSS.


all kinds of companies make bad choices, i've also seen a bunch of companies use tailwind correctly -> components + @apply are enough to make it very pleasant to work with :)


While that may be true, as I mentioned, if many companies repeatedly keep making the same errors (including a sibling commenter here), it is on the creator to fix it, not the user, and even if they do, it could be a bad solution. Personally I've moved off of Tailwind entirely, too many footguns to deal with, and I'm not sure why it's any better than writing CSS, at scale, not prototyping. In a way, it feels like the CSS version of Perl or APL these days.


> In a way, it feels like the CSS version of Perl or APL these days.

Perl, maybe, but not APL. CSS version of APL would let me style my personal site and blog in less characters than it took me to write this comment :).


windicss is a superset of tailwind that lets you define shortcuts and aliases for easier composition, and variant grouping for less typing


[flagged]


No, I haven't. As I said I've worked in several companies that explicitly have this sort of abstraction. If so, what's the point of using Tailwind? Tailwind fans will say it's atomic classes, but when using them with many devs, it indeed turns into reinventing CSS classes.

So, perhaps it's the other way around, that Tailwind is simply not a good tool at scale.


As I understand it, Tailwind is essentially CSS 2.0 - or 5.0 I guess - with the warts ironed out (sorry for the unpleasant mixed metaphor):

A) CSS / Tailwind: lots of little 'atomic' classes describing individual visual properties. Easy to understand, easy to customize, but slow to read, slow to get started, with plenty of stuff to learn

B) CSS frameworks / Tailwind-based components: fewer classes declaratively describing the component's role (eg 'btn-primary'). Quick to get started, elegant to read, but prone to abstraction leakage and trickier to bend to your own exact specifications

There's always been a need for both kind of tools for different projects and there will always be, in the same way that e.g. network programming may involve anything from bit-banging commands to high-level protocols.

(And you will often go back and forth in the same project: you quickly crap out your first draft using prebuilt components, then it turns into a serious project and you start actually investing time in design and twiddle with the individual properties, and eventually it grows to a large project where you now have to go back to components again for the sake of maintainability, but this time it's your own components with your own accumulated set of properties.)

Over the years CSS frameworks kept improving, but CSS was much slower to do so - although it acquired flexbox, grid, etc., the language limitations stayed, and they were bad enough to e.g. spawn SASS/LESS out of a genuine need for better maintainability.

Tailwind takes all the stuff that was added to CSS over two decades, like media queries, and makes it part of the core nu-CSS language design. Tailwind saw a ton of hype and adoption because all the developers who had always wanted to go the (A) road now had a well-designed set of simple classes they could use with a lot less hassle, plus a bunch of developers who had adopted (B) because it was the road that had all the momentum suddenly realized that they probably wanted to use (A) once it was made less painful.

Many of the flame wars between "pro-Tailwind" and "anti-Tailwind" people were actually disagreement over whether to take the A or the B road.

Using @apply, header classes, or component projects like DaisyUI somewhat resembles using a CSS framework like Bootstrap, but by building on top of Tailwind it means that, when your project or resources grow and you want to move from (B) to fully customizing your style in (A), you will be able to write your individual little graphic touches in Tailwind instead of plain CSS.


CSS is fine, arguably even good these days. For every new feature in CSS, such as the is, has, not selectors, Tailwind needs to reimplement them in its own way, basically becoming a DSL in the process.

I mean, look at the syntax for something like this:

    [--scroll-offset:56px] lg:[--scroll-offset:44px]


Sure, but still, it's the kind of small-but-incremental QoL improvements that apparently frontend designers really really want in their working week.

You linked to a very interesting TS library above, vanilla-extract, and you can probably see that what it does is very similar to what Tailwind does, only plugging into 'tsc' instead of PostCSS? Eg. https://vanilla-extract.style/documentation/styling/#media-q...


It's just using camelCased CSS similar to Emotion or styled-components, how is that similar to Tailwind at all, it's not atomic? Unless you simply mean that both Tailwind and VE have a build step, which is, well, vacuously similar. VE also runs PostCSS underneath after it compiles to CSS, so you can use whatever plug-ins you want too.

Regarding the QoL, it seems more to me that now Tailwind discovered that they have to support arbitrary CSS as well as every new feature, so they have to manipulate their DSL to fit all of that into their current syntax, ie class names, even if it doesn't fully fit. I'd rather just use CSS at that point, if I'm cramming a bunch of stuff into a class name.


I don’t understand. Do you think there is something in the CSS spec that dictates developers must use high-abstraction classes? What do you think CSS classes are?


That is likely the gist of it. I love tailwind for prototyping / designing in code, for large code bases, not so much.


Tailwind shines when paired with a component based library such as react. If you’re repeating the classes all the time, perhaps create a component for that.


My example was within components. Even inside components it's just visual clutter, hence the abstraction I mentioned.




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

Search: