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

A bit of light in the dark ages of Tailwind and CSS-in-JS.

It's difficult to realize how significant CSS Zen Garden was 20 years ago or so.



CSS-in-JS (like how Styled Components and Emotion do it) is primarily a solution to code organization. It doesn't fundamentally change how CSS works.



It can be, depending on how it's used. But most of the modern tools I've seen are specifically designed to compile away the dynamic aspects of CSS-in-JS, meaning it essentially becomes a different way of writing normal CSS.


It made me fall in love CSS for sure. But it didn’t prevent me from understanding the appeal of Tailwind for large scale projects.


Please enlighten me of the benefits of using tailwind in large projects as opposed to using inline styles.


Not having to name a myriad of things! Sure, things like BEM can help you at least avoid naming conflicts, but you still have to decide a few thousand times what something is called semantically correct, in a way that you and your French colleagues and the intern of next year are going to understand it.

When I got productive with Tailscale, I finally realised how many brain cycles I wasted on this. I’m, like, a programmer; of course I’ll obsess over picking the perfect name. Of course I will not be happy with the names my junior picked, and of course I will constantly be unhappy with some of them. This game of self-inflicted bike shedding is just a trap for people that like logic and order, and it’s… wholly irrelevant.

With tailwind, you just declare how something is supposed to look and feel like, at the place where it is, in the context of your configurable design system. No names, no bike shedding. Okay, that’s not a hundred percent true; you can name group selectors, because as opposed to plain inline styles, Tailwind can target children, siblings, pseudo elements—there are no restrictions.

It’s a truly elegant solution for authoring web pages.


How do you cope with page URLs, with JavaScript classes, variables, methods, Git branches, CSS filenames, image filenames, directory structure, etc.?

I know the joke is that naming things is hard, but it really isn’t and every web developer successfully and effortlessly names things all day long without even thinking about it.

If I have a sidebar, coming up with the name “sidebar” for a CSS class is not a serious problem I need Tailwind to help me avoid. I don’t find it plausible that other people genuinely feel pain over this one particular instance of naming things without demanding solutions for all the other things they have to name. Can you point to many Tailwind advocates who complain about how difficult it is to pick filenames, or page URLs?

I think it’s really strange Tailwind advocates latch onto this one particular thing – naming things is a problem – when they seem to successfully name things all day long in every other context. It doesn’t feel like a real problem. What happens when you need to create a file? Do you get paralysed by the myriad choices of filenames available? Or do you just pick a name and move on?


I don't do front end these days and don't really have a dog in this fight, but most code is nouns and verbs, while design choices are adjectives and adverbs. When the business decides that we need to run, not walk, you can just change the name of the function from walk to run instead while changing the code, if that seems appropriate. With CSS selectors, when the business decides we need to change the "feel" of the page from "spiky" to "gentle," you might only want to change some subset of the design that was built up around the "spiky" concept, while leaving others as they are. There is not a comprehensiveness and absoluteness to the changes, and it's a lot harder to reason about "pure functions".

Add to this that CSS is like a codebase built on try/catch and throwing errors for its primary control flow mechanism, where the only thing that tames this is the naming conventions employed, and you've got a situation where the names really matter in a way that they don't when we're doing other programming, because the names are the only connective tissue to help you reason about the "control flow" once your css starts getting complex.


Naming things involves creating an ontology that makes sense. In the context of URLs, that’s an hierarchical structure of things that’s self-describing; in the context of code, it’s a direct model of the domain logic underlying the problem, at least for the most part: There’s lots of code involving "managers" and "containers" and "controllers" and "factories". That’s where it starts to get murky. Meanwhile, CSS classes require creating an ontology of things that’s both reusable, and at the same time referring to small or partial aspects of a thing, sometimes a thing that shows up in other parts of the code, sometimes as a variation, or just a one-off occurrence. If you do it properly, that means you’ll need to find a shared vocabulary for button attachments, centered max width wrappers, field hints, list items, and so on. It’s draining, because it’s not productive. The end result is just hopefully maintainable style sheets.


It's draining because you're over-complicating things. CSS is for styling HTML. So however you structure your HTML, CSS closely follows (hierarchy of components/partials). Then you consider variants. After that, parents influencing children styles. Then on top of that, you add a reset (because small browser incompatibilities) and maybe a grid utility.


Using webcomponents there is no problem with identifiers, you can repeat the same identifier as many times as you need.


I still don't understand how not naming things is a benefit over inline styles? With inline styles, instead of class="bg-blue" you'd write style="background-color:blue" and then you could remove an entire build step and an entire dependency because the browser already supports this.


You can override bg-blue with a more specific selector or a user stylesheet, which you cannot do with inline styles.


So you write a rule to say that blue buttons are actually yellow?

Then someone sees the code for a button which is clearly marked as blue and will be surprised it's yellow.


Try to imagine that was an example. There was even another response mentioning different shades of blue. Imagine one might override it locally, just for one widget. Imagine one had a style (something other than color, even) that wasn't designed to reference a CSS variable ahead of time. Imagine there's no heaven.


That; and also adjacent selectors, pseudo elements, media queries, variants, dark mode handling, state handling (hover, focus, etc.), and much more.


So you make the bg-blue style make things yellow?


No, but your designers have decided instead of cornflower blue, they want it to be aqua instead. Have fun searching for all variants of the color in your stylesheets—including CSS varsity, hex codes and rgba variants. In Tailwind, all you need to do is update the config file, and it'll apply to backgrounds, text, shadows, anything.


Well that's why we use CSS variables and any change would apply everywhere and also give us semantic information with naming like color-primary or color-surface.


Do you treat your JS and TS the same way, eschewing semantic clarity?


Why do you assume there's no semantic clarity?

Tailwind is made for the age of apps and components. The semantics are on component level.


So you can name a component, but you cannot name the styles attached to that component?


It’s naming one component, perhaps a sidebar, versus naming the fifteen children of the sidebar and their state transitions, in a way that’s consistent with the other 137 components that comprise your app.

It’s not about not being able to name all these things, it’s that it’s pointless to do so.


You don't need to name subcomponents in a way that is consistent with the rest of the app, or even at all. That's what nesting is for. You can use simple names, or anonymous tags, that don't overlap because they live in a top level component with its own class/id.


Isnt that what scoping is for? So you name you conponent and use selectors for styling sub components.

My issue with most styling is that they want to use composition when it does not make any sense. Like spliting styles and using the pieces on things that has no relation to each other. If two things are not likely to change together, you shouldn’t use the same class for them, unless you consider the class as primitive.

Tailwind is an extreme case of this, and like all extreme things, it’s a path to ruin.


> If two things are not likely to change together, you shouldn’t use the same class for them, unless you consider the class as primitive.

That is exactly the kind of dogma that I'm referring to—Tailwind makes thinking about that pointless. You apply style composition to achieve the result you want to achieve; not more, not less.

> Tailwind [is] a path to ruin.

Well, I can only speak from the experience of quite a few very successful projects relying on Tailwind: They look consistent, development is easy, style bundling an issue unheard of, and making changes to the design system requires a single line in the config file. New developers are productive from day one, there's no bike-shedding over the styles, and nobody adds ad-hoc colors they just pulled out of their hat.

For me, styling on the web is solved.


I get your point, but isn't this contingent on the colors and other properties being stable? I don't see why this can't be achieved with pure CSS instead of adding a (big) set of names as a layer of abstraction and then optionally pruning the build for performance.

Because consistency is achieved only at UI design time (see design system). Unless you're prototyping your design at development time, I don't really see the improvements compared with Pure CSS.

> style bundling an issue unheard of

But then you need to have a component system to have any benefit from that (if you're using the raw classes name from Tailwind). Which was easily done with just naming the component (class or ID) either automatically or not.


> Because consistency is achieved only at UI design time (see design system). Unless you're prototyping your design at development time, I don't really see the improvements compared with Pure CSS.

So, every class now repeats the same properties and values (because CSS has no mixins or composition), there are dozens of classes that try to replicate scoping and nesting through BEM or similar, styles are completely divorced from actual components where they are used...


This is what I highlighted in a previous comment. If you have a drop shadow or some padding on a component, that does not mean another component which have the same values automatically have a relation between them. So it’s mostly a syntactic shortcut at this point. And the end result has no semantics.


I've not used Tailwind yet, so please forgive me for not knowing how you achieve these things with it.

As I understand it, you add a drop-shadow to an element by writing a bunch of classnames that hook into Tailwind and apply the styles. If you want several components to use the same drop shadow, how do you _not_ repeat all those classes on all those components? If you are repeating the classnames, you're just pushing the "problem" of repeating your style rules out to the components. I'm sure there's a Tailwind way to solve this, but I don't know what it is yet.

My experience with several UI libraries and general react usage suggests you might make a new component (`<CompanyApprovedShadow/>) that applies the drop-shadow, and then each component which wants the shadow would use `<CompanyApprovedShadow/>`. But what if one's an `article`, another a `button` etc? What if one's blue and another's red? This gets complicated real fast and/or you end up with so may properties on your style components for customisation that you've lost the benefit you originally sought.

The point of repeating style directives under different classnames, in raw CSS, is that you often want to customise things. The shared values should be provided by the cascade, or variables/--custom-properties.

``` .company-button, article, .card { box-shadow: whatever; } ``` Is pretty reasonable CSS, imo. I confess I've not used this in a large project, but _every time_ I use something like styled-components, and most UI toolkits, as soon as I have to customise something I wonder why I've agreed to the abstraction.


> Isnt that what scoping is for?

The one that hasn't been available in CSS until 2023?

> So you name you conponent and use selectors for styling sub components.

So how can you do that with CSS?


> So how can you do that with CSS?

The old:

  .card > .card-header
> The one that hasn't been available in CSS until 2023?

That was nesting, IIRC. Sugar syntax, so you don’t have to fully specified the whole selector.


> .card > .card-header

And we know that it never worked well. That's why BEM and many other naming strategies exist: to try and replicate scoping purely through naming discipline.

> That was nesting, IIRC. Sugar syntax, so you don’t have to fully specified the whole selector.

Nesting partly solves the naming and the "keep all of a component's style close together".

The other partial solution is CSS Scoping: https://developer.mozilla.org/en-US/docs/Web/CSS/@scope

And this still doesn't solve the issue of repetition where every component has to spell out the margins, the paddings, the borders, the...


Because you honestly don't need to, and shouldn't need to.

CSS missed out on scoping, nesting, and composition even though most of these things have been available elsewhere (notably, SASS) for 20 or more years.

What you really want is to have a component and all styles scoped to it. In the absence of that you want as few names replicating scoping, nesting, and composition, as possible. And you want to compose repeatable patterns across all of your components (something that is possible with mixins, and is impossible with vanilla CSS). So you cut down on repetition across class names until you're left with utility classes and a separation of concerns that cuts a different way: https://x.com/simonswiss/status/1664736786671869952

Note: no one is stopping you from having your own arbitrary classes when you need to. See e.g. Cube CSS approach https://piccalil.li/blog/cube-css/


What I’m arguing about is that composition isn’t necessary in most cases. Because all components evolve independently. Also nesting is syntactic sugar, nice, but orthogonal to these discussions. And scoping is done with selectors.

CSS isn’t an imperative language. It’s a declarative one. And what’s most important is understanding what it is trying to solve (mostly not styling elements with tag’s style). Which is what Tailwind is replicating.

Tailwind may help you in some cases, but not because of a defect in CSS. It’s like using an ORM, then past a certain treshold, you find yourself doing (badly) SQL but in $language.


> Also nesting is syntactic sugar, nice, but orthogonal to these discussions.

What is it with you and nesting? We were talking about scoping styles to a component.

> CSS isn’t an imperative language. It’s a declarative one.

So what? Doesn't mean you can't provide nice declarative ways of doing things. Like SASS has provided for 20+ years.

> Which is what Tailwind is replicating.

I fail to see the logic or, indeed, the meaning of your assertion.


> What is it with you and nesting? We were talking about scoping styles to a component.

I get where you coming for. But the issue that the scoping[0] you're referring to on your other comment is something I've never encountered or heard someone complain about.

In my mental model, an element owns it subtree in terms of styling. Meaning that, if I a component can be placed either statically or dynamically, then it belongs to all the elements that may be its parent. And if it want independent styles, then I reset it at the root of the component. So I assumes, any style property that is not set at the root of any element is inherited from the parent.

No need for elaborate syntax and mechanisms that just drive up the complexity of the code.

[0]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scoping


> But the issue that the scoping[0] you're referring to on your other comment is something I've never encountered or heard someone complain about.

Why do you think BEM exists? Or CSS-inJS? Why do you think web components exist?

All these are attempts to actually do what you claim css is already doing: to make sure that the element owns its subtree in terms of styling.

CSS is global.

> So I assumes, any style property that is not set at the root of any element is inherited from the parent.

Of course not. The hint is in the name: Cascading stylesheets. Your "owned" subtree is owned by every single element above up to and including document root.

> No need for elaborate syntax and mechanisms that just drive up the complexity of the code.

There's nothing complex about Tailwind. It actually reduces all the complexity to a set of small primitives. And since it's still CSS, you can write your own CSS as you wish.

Here's a good overview of how and why you would use it: https://piccalil.li/blog/cube-css/


> It’s a truly elegant solution for authoring web pages.

It's an ugly hack that undoes a lot of the benefits of CSS.


Configuration. Inline styles are direct style Implementations in your html, tailwind is an abstraction that lets your configure and refactor easily. I can say from experience, being in charge of developing a large product that was bought and merged into a larger corporation, using tailwind from the start allowed us a refactor our app’s design within 1 day instead of it being several weeks worth of work.

And as a bonus it provides best practices in css scaffolding of a product design (typography, grid, colors)


So how does that work exactly?

For instance now I have class some-button to make the button blue. Next week I‘m told make them yellow.

It’s a change in one class

What would be the equivalent in Tailwind?


It’s less about replacing blue with yellow, which means overhauling design. My example was when your style system includes several color spectrums - primary, secondary, accent, grey, dark, light.

Your components are styled using these spectrums - button.color-primary-500.bg-grey-300

When you want to change the colors globally it’s extremely simple.

Same with paddings and margins.


You use components, e.g. react, and replace the `bg-blue-500` class with `bg-yellow-500` in your button component.


HTML already has a button component. How is this better than creating a root CSS style for buttons using CSS variables to abstract the colors?


For this contrived example it isn't better. I was very sceptical of tailwind before I tried it, but in certain scenarios it's nice. I think it shines if you are already using a component focused development process. As others have said you don't need to come up with class names for every thing you want to style. Rather than cross-reference between js/html files and css files everything (structure, style, and interactivity) is in one place. You also don't need to care about specificity.

Personally I think it gets a bit mad when you start wanting to do :hover, :active and media queries, but you can still use classes (using the @apply directive) for that if you wish.


The root style is global to one app, the component may be used in many different apps.


But you can use CSS rules to override variable values for more specific selections. So you can define a global --primary-button-color at the root, apply it to every button, then override --primary-button-color in, say, `tool-container`.


So that's exactly the same as using the CSS style directly. The way you've described it, Tailwind provides no benefit whatsoever.


So tailwind needs to be used with some kind of library or framework and not vanilla HTML and JS?


It's possible to use with vanilla, but probably not a good experience. You want either components or at least a templating system where you can break things down into reusable parts.

It's somewhat similar to raw HTML vs something like PHP, you can have thousand of HTML files, but if you wanna change something common (say the header) it's not fun with thousands of individual files.

Lastly you can combine Tailwind with regular css classes

    .btn-primary {
      @apply bg-blue-500 px-2 py-1;
    }
but at that point I'm not sure how much you are gaining by using Tailwind


mobile selectors, "group" selectors, hover styles... the list goes on and on. like all things, it depends on how your workflow. if you have a super defined hierarchy and naming scheme you can reference then sure, centralize all your CSS. if you have many components and multiple people working on them sometimes it's better to separate everything out and enforce with a guide.

i can't tell you how frustrating it is to open a component and have no idea what you're looking at because you worked on the thing months ago, or you change one thing in your centralized CSS and have no idea what the side effect are. with tailwind i can see exactly the styles i'm looking for.

FOR ME, works better to have a core style file and then move everything to something like tailwind per component for maintenance.

tired of seeing the same simplistic arguments against utility frameworks. if you can't see the merits of it then you obviously aren't working on projects that need that, but don't criticize it blindly. it's not for lack of CSS knowledge.


The thing is, when you are doing CSS properly, you will "anchor" your selectors appropriately. What I mean is the following: Ltes say you got some "component" or whatever you want to call it, and have a button in it, that is special in some way. Of course you are not gonna write "button a {someproperty: specialvalue}". You should be writing the selector as specific for that component in this case, for example "my-special-component button a {...}". If the design is implemented thoughtfully, then most of the problems people claim to have with CSS are simply non-existent.

Like "side-effect" you mention. That can only happen, if the design is implemented reusing parts of other design definitions, where they are not meant to be reused. Any other side effect would be intentional. For example button text color and paragraph text color. If one wants them both to change, when one of them is changed, then one reuses the style definition, but probably in most cases one does not, and therefore has separate definitions and therefore has no silly side effects, even after a few months not looking at the project.


yeah i'm just tired of making the same argument over and over. for what it's worth i've been doing CSS "properly" since the time when that site was a thing.

back in the backbone days, now vue/react and before tailwind this is EXACTLY what we did, scope the styles down to the component, then layer it on top of the global styles. so sure, .my-selector a is specific to that component.

the "side effect" happens when your app starts to span 100s of components and you need slight variations for everything. you're telling me you can assure me that none of your master global classes contain any design definitions that could not possibly be reused? i'm calling bullshit. it's like saying your backend 100% never has any duplicated functions.

so with that out of the way, let's say you do get your global styles like 90% of the way there. so in each component you scoped it down. now lets say for one button you had to adjust the margin a bit, or another the styling is somewhat diff. multiply this by 100s of different components.

tell me now how me opening a file, seeing "button variation-class-1 variation-class-2", then going down into the component <style> section and looking up exactly what the variation and what properties were used is in anyway "better" than just seeing some super powered inline style directly in the button. it's the same complexity and now you've gotten rid of the prime benefit of CSS hierarchy and split the "style and structure" in just one independent component.

at some point you can't possibly create the number variations you need and you can't extract out "reusable" parts. maybe you have 5 buttons and only 2 need the 20px margin. do you create a "button 20px" class and do that in each component? no. at some point you will just inline it because it's easy, or you will create your utility class except it will be worse than tailwind.


Or you can just just ".special-component .button" selector adapting the selector to be more specific as needed. Most code reuse in CSS is bad, because they break when the design changes. You don't DRY just because the code are the same.


yeah and when you do that for 100s of components i'd rather just see a utility class on the button itself vs hunting down its meaning in the related section of the component.

this is exactly the shit i was annoyed by when scaling, having to scope a .my-component-X section for literally every component and then grokking it later. you're basically taking the worst part of CSS (hunting down the meaning of a selector) and duplicating it for every component.

now maybe you say name it better or more semantically? congrats you recreated BEM lol. literally all the arguments used against it are purists for purity sake or thinking people haven't gone through this same thought exercise.

just use whatever is best for the task. i still use css the "traditional" way.

/rant


Why the hyperbolic about 100s of components? Because if that exists somewhere, then it's just plain bad design to me.

Let's say you writing templates for a website and you have a partial named "main-navbar", it has a search input field that looks like the other input, but needs some alteration. Are you going to name all these alteration like "margin-x-2px", "font-small". Or just add a new style block with ".main-navbar .search-input" as the selector.

> you're basically taking the worst part of CSS (hunting down the meaning of a selector) and duplicating it for every component.

Why are you hunting it down? If I need to find the styles that are applied to an element, I just open the web inspector. More often than not, from bottom to top, it's the reset style, the component style, the variant style, and the styles derived from its relation with its parent and siblings.

With tailwind

  <div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg outline outline-black/5 dark:bg-slate-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
    <img class="size-12 shrink-0" src="/img/logo.svg" alt="ChitChat Logo" />
    <div>
      <div class="text-xl font-medium text-black dark:text-white">ChitChat</div>
      <p class="text-gray-500 dark:text-gray-400">You have a new message!</p>
    </div>
  </div>
With "traditional css"

  <div class="alert">
    <img class="alert-icon" src="/img/logo.svg" alt="ChitChat Logo" />
    <div>
      <div class="alert-title">ChitChat</div>
      <p class="alert-message">You have a new message!</p>
    </div>
  </div>


You don't need the classes on the alert sub-elements:

  <div class="alert">
    <img src="/img/logo.svg" alt="ChitChat Logo" />
    <div>
      <h2>ChitChat</h2>
      <p>You have a new message!</p>
    </div>
  </div>
Then the CSS is just:

  .alert {...}
  .alert img {...}
  .alert h2  {...}
  .alert p   {...}
You can probably also get rid of the nested div by using flex, floats, or margins.


I would add, that usually also I would not hunt things down, because I put styling for a navigation into a stylesheet file called "navigation.css". Not much hunting to do there. Sounds crazy, but actually works.


every page in our app is a "component" which could have many sub components, some reused. i think it's actually very common in large scale apps so maybe we're not even starting on the same foot.

would you not consider opening the web inspector "hunting down" when i can just understand the full thing just by looking at the element itself? to your example, let's say now you want to put your search bar next to another item, but that item requires that there be 4rem right margin. Then you put your search bar next to another item in another component that requires 8rem top margin. this starts breaking down at scale. in the past we would just inline the margin value. eventually this moved to basic utility classes which evolved into tailwind.

and with your code, you've just proved my point. i find tailwind overly verbose (necessary evil), but honestly i don't even need to open the "inspector" to understand how this will look. you can give me that 12 months from now and i can understand exactly what it is in 10 seconds. with "alert" i have to referencing something else. and this is just a handful of lines, imagine if your component is larger than that. it's also a bad example because i will just eventually roll the styles for "alert" into its own class if it makes sense. get the best of both worlds.

again, i challenge you to just consider that a lot of other people as smart as you have thought through these issues and made the according trade-offs. the example you're giving is just way too simplistic and pretty much like every example someone gives in these threads.


> the "side effect" happens when your app starts to span 100s of components and you need slight variations for everything. you're telling me you can assure me that none of your master global classes contain any design definitions that could not possibly be reused? i'm calling bullshit. it's like saying your backend 100% never has any duplicated functions.

At that point the design department of your business has failed its job. Case closed. If everything is slightly varied, then there is no coherent design. Not even my backend-dev developed "designs" are that bad.

> tell me now how me opening a file, seeing "button variation-class-1 variation-class-2", then going down into the component <style> section and looking up exactly what the variation and what properties were used is in anyway "better" than just seeing some super powered inline style directly in the button. it's the same complexity and now you've gotten rid of the prime benefit of CSS hierarchy and split the "style and structure" in just one independent component.

I also care about what soup I deliver to any visiting browser. So I care about not having a thousand repeated inline styles, that make it harder to write user CSS for example. Visitors of my site can very easily adapt the styling, by simply changing the styling of 1 class or even 1 CSS variable that I defined in a theme.css.

But you are also making a general mistake. You are letting perfect be the enemy of good. Even if one only succeeds 90% at what I described, this is still great and simple to maintain in 90% of the cases. I doubt tailwind is a big win over that.


you know, maybe understand actual frontend dev issues more deeply before arguing against toolsets you are unfamiliar with. none of your suggestions/generalizations actually address my actual real world issues and rather they are non-issues in modern dev.

the type of apps i'm talking about aren't just a "theme.css", so whatever mythical large app you're talking about that adheres to this rigid css standard while being easy to maintain i'm ready to be proven wrong lol.


Tailwind makes things like dark mode and group hover/focus states dead simple with modifiers (dark:, group-hover:), keeping all your logic in your HTML. With inline CSS, you’d need messy JS, extra classes, or bloated style tags to handle the same.

It also enforces a design system (spacing, colors, fonts), while inline CSS often leads to small, inconsistent differences everywhere.

It’s a great solution to a problem a lot of people were having. That’s why it’s so popular.


media and container queries. Pseudo selectors


Even on one element you can combine tailwind classes. With styles you need to write out the final result.

Then you can also use @apply if you wish but leave that out of the argument as it is considered unidiomatic.

Of course if using React you can also use styles directly and then create your own utilities to determine how they compose. Which might give you much of the Tailwind edge without needing the compile step.


Even on one element you can combine CSS classes. Easily.


They published a book that I purchased when I was first learning CSS. Truly a different era.




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

Search: