Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Reimagine Atomic CSS (antfu.me)
52 points by city41 on Nov 17, 2021 | hide | past | favorite | 44 comments



This approach still seems nonsensical to me, and I've written tens of thousands of lines of CSS.

If you're going to be doing things like

    .m-0 {
      margin: 0;
    }
Then you may as well just do

    <div style="margin: 0"></div>


As I addressed in another branch of this thread, you're actually missing the point and his article does a poor job communicating it off the bat with its first two examples.

In your example, `.m-0` and `style="margin: 0"` aren't equivalent in this paradigm (even though they happen too be technically equivalent). `.m-0` really just means "no margin". If you look further down the article, you'll see:

  .m-1 { margin: 0.25 rem; }
  .m-2 { margin: 0.5 rem; }
  /* ... */
  .m-10 { margin: 2.5 rem; }
As you can see, the numbers don't match their values, the class names are meant to be relative to one other.

A better way is to use constraint-based design and come up with a system where you would only have `.m-none`, `.m-xs`, `.m-sm`, `m-md`, `.m-lg`, `.m-xl` (for example).

Libraries like Tailwind give you WAY more than you're going to need and you should only really be using a subset.


I'm not sure I am missing the point actually. As a class name, `.m-none` is pretty terrible, because it says what the thing looks like rather than what it does. I can't see how in practice this doesn't lead to a much greater mess, and even in the best case you would end up with

    .m-none.br-kinda-rounded.p-a-bit.bw-pretty-thicc.bc-old-mauve
The clever example in the article with using a loop to define values is not something I find particularly compelling either. The whole point with specifying relative values, e.g., percentages, ems, root ems, etc., is that they can change relative to an element they inherit from or from the root of the document.

Furthermore, using class names is not the only way to achieve the effect in the example you pulled from the article. Sass (and just about any CSS superset from the past decade) can achieve the same with mixins, which would spare the resulting document from all the selector noise.


Atomic CSS class names have no semantic information and they are not meant to. If you need a class to describe what an element does, you're free to add one.

The amount of class names is off-putting at first, but you get used to it pretty quickly. (I'd also recommend using numbers or sm, md, lg etc for your size increments, but to each their own.)

Tailwind and friends aren't trying to solve the CSS Zen Garden problem. Their focus is on re-usable CSS rather than re-usable HTML. Depending on your needs that may be better or worse.


Right. The point is to turn what we’ve taught on our head and use CSS to describe what things look like and use components to describe what they do (a component can be a web component, frontend framework component, partial, etc). In fact, using this style of CSS without components is probably not the best experience (but I’ve ne er tried).

As stated in another thread, it’s not necessarily better but it works very well, and I personally find it nicer having everything my component needs in one file.


+1 to this. It makes a lot of sense with a component-based library or framework like React, Svelte, etc. I've never been more productive, and its a bit of a bummer to work on projects that aren't using this kind of thing. I enjoy this general approach whether powered by Tailwind or some style-props based ui library (chakra ui for instance).


I suppose in theory it gives you the ability to tweak all styles of a given type together in unison.

    .color-primary {
      color: #f00;
    }

Could easily become

    .color-primary {
      color: #00f;
    }

But beyond this case, I don't understand it either.

It feels like the gravity has moved a long way from CSS Zen Garden and that the web isn't about semantics and documents anymore since the practical usage differs.

Any designers that can shed light on this tailwind trend?


You're not far off.

What people often miss is that this is to be used for creating design systems. `m-0` doesn't necessarily mean `margin is zero`, it means the smallest margin available. You should also call it `m-xs` for example. Or if it does mean margin is 0 then it might be better suited to be called `m-none`.

These utility classes play very nicely with component-based design. Just like JSX puts your markup in the same file as your code, this does the same for styling/layout.

I wouldn't say it's necessarily "better for all" but it's absolutely a very viable way to work no matter how much some may cry heresy. It saves you from having to figure out what you're going to call everything upfront and let's you break things into components as you go. I haven't personally use it on a big project but I've found it a very clean way to work and have heard good things from folks using it on big projects.


> "the web isn't about semantics and documents anymore"

Bingo. The "documents" era of the web has given way to the "applications" era.


That's the thing that speaks for tailwind with the swatch-like config, but applying this principle here would lead to utter chaos.

If i need a little more spacing on alk my medium margins, i would end up with something like .m-8 { margin: 10px; } if i start editing these compiled styles


I feel the same way, but also understand what folks are saying in the replies to this, but I'm more coming at it from an API standpoint preferring something like https://github.com/workframers/wonka (albeit in Clojurescript). But there I get to use inline styles with 'special' values that could mean something in my theme like `padding: my.theme/container-padding`, and it's also just data so I can merge them etc.

This seems preferential over a single string with various syntax. I could be wrong in that my linked approach is not the same.


The main differences are that inline styles don't give you the ability to set responsive or pseudo-class variants and that using classes limits you to values defined in your design system.


Added bonus, the classes autocomplete in most editors, allowing you to style a component extremely quickly. "Tailwind" is a very apt name.


1. Text editors can autocomplete just about anything, including CSS styles.

2. The code you write shouldn't be compromised by the tool you use to write it.


Your approach wouldn’t work with media queries. In a system like tailwind you can do class=“m-0 md:m-4”


If you're going to use something like Tailwind, please still add legible class names (e.g. "thumbnail") and don't just write things like class="m-0 md:m-4".


One could argue that `class="m-0 md:m-4"` is more legible because it describes exactly what is happening. No margin on small screen, margin of 4 units on medium breakpoints. Where the styling on thumbnail is unknown and the breakpoints are unknown, unless you look at the css, then if you need to override that css you have to worry about specificity.


You're talking about CSS. I'm talking about classes.

> `class="m-0 md:m-4"` is more legible because it describes exactly what is happening

If by "what is happening" you mean "how it's styled", but that's extremely presumptuous. You might as well argue that there's no difference between Flash and an equivalently styled web page since if you took a screenshot of each they'd be visually identical—as if the whole thing begins and ends with getting the "rectangle full of little lights" to change in a particular way <https://xkcd.com/722/>. It's very narrowminded.

I'm not saying you have to be full on schema.org-conformant, but include something that actually makes sense instead of being as useless as the spew that CSS compilers put out.


I think the recommended pattern is something like:

If you’re using a component based framework (e.g., Vue), you create a named component.

If you’re not using a component-based framework, you create named classes with applied atomic styles.

With either approach, your styles are contextualized with a name and can be reused.

https://tailwindcss.com/docs/extracting-components


But then again, if you're going to use meaningful class names, i.e., names that suggest what the thing is rather than what it looks like, then you're not really doing the Tailwind thing.


> rather than

You don't have to sacrifice semantics in order to opt in to Tailwind's cutesy approach at expressivity. class="thumbnail m-0 md:m-4" works wherever class="m-0 md:m-4" does and isn't user-hostile.


The former lets you use media queries to make it responsive, and can help impose a design system versus having a bunch of random sizes and colors.


As a full-stack dev on a gigantic codebase, I struggle a lot with understanding the allure of this type of tools, considering the extra baggage they bring with them.

I could see myself doing something like

  .card {
    @include margin(2);
    @include borderRadius(10)
    // etc
  }
and so on, then using class="card" to tie it together, but adding a tree-shaking step, or a pre-crawling (!!) step just strikes me as entirely too hefty a cost for what amounts to parametric variants of a given style token


Do this with CSS variables and only add new ones once you need them. This has been working great for me.


FYI. The stylesheet for this relatively simple article is 152 Kb of which 88.5% is unused according Chrome's code coverage tool.


Also worth noting that it's ~37kb over the wire, applies to the whole site (where it is much more thoroughly used), and contains a bunch of svg icons that don't need to be loaded in separate requests. If anything, that reinforces to me how well considered it is!


I appreciate how these two statements, while fully factual, paint two completely different pictures.

Such a great example of omission and inclusion when reporting the facts.


I don't want to necessarily argue with OP's CSS file but there is a teachable moment here. We make a master CSS file because we don't want to serve bunches of CSS files per page. The constraint we want is:

    n << 100 
There are other solutions besides n=1 that satisfy this inequality. Serving 2 CSS files per page does not introduce substantial latency over 1, even on HTTP 1.1 user agents. Once you get to 4 you start to cause some problems, but 2 is barely noticeable.

CI/CD can fight your CDN by reissuing every asset on your website on every single publish, causing all of your visitors to soak up bandwidth at the same time. If you name your JS and CSS bundles based on the hash of the contents instead of prefixed with a version number, then many publishes will reuse the same assets, except that doesn't scale so the bigger the website the more evictions you'll see. But a site bundle with a per-page or per-subject matter bundle does scale, because targeted fixes often won't affect your home page.


And this approach may or may not be better, depending on usage patterns. Code splitting or even inlining may benefit users who tend to view only one page then move on. A single file loaded and cached upfront will tend to benefit users who stay on your site to view multiple pages.

In either case, splitting atomic CSS is probably more complex to get right because of ordering, and possibly more inefficient for everyone as well.


This project looks pretty interesting, but I find the hoops the author decided to jump through to showcase the "bad" parts of existing tooling hilarious.

The Tailwind config file "downsides" were the most interesting. 5 minutes every time to find the config variable to change? Maybe 5 minutes the first time and then it's the exact same process to for every other attribute. Tailwind even renamed all of the options to mirror the JS counterparts.

Again, I think this is a cool idea, but not everything needs to be a pissing contest. Just state the features your thing provides and let users decide if it's worth switching.

Side note: some performance numbers on using attribute selectors vs css classes would be interesting. I'd assume it's negligible until tens-of-thousands of selectors, but I'm curious nonetheless!


I don’t necessarily read it as a passing contest. Sometimes the best way to convey why a thing you built is interesting—especially in a crowded space like styling solutions—is to show how it contrasts with better known solutions.


I noticed this too. I actually find extending tailwind very intuitive. Rather than finding myself out of context and wasting time I felt grateful that someone designed a tool well and glad to be adopting it in my project.


adding to this that these "downsides" are the actual upsides of tailwind. Getting rid of config in favour of arbitrary numbers kills a lot of the actual utility such frameworks provide


Not to mention tailwind has had some sort of arbitrary value support since the JIT was introduced.


I'm not really a fan of CSS frameworks. I never get the hang of them. Most of them are really good at getting a consistent style, but any form of customisation often ends up more complicated than just implementing it yourself

In my work flows I typically keep my components/layouts small and my CSS scoped. So I write the semantic HTML (or JSX), add a few classes in there and then style the classes (often in the browser rather than in my editor).

People often complain about the web no longer being semantic, and I'd argue that CSS should also be semantic. The classes should fit the components. A button component should have a button class. A payment form should have a payment form class.


Admittedly, my front end work is a side project for me and not my day job. But I have actively tried to get used to style frameworks, big JS ones like Ant and MUI, or CSS ones like Bootstrap or Tailwind and just never got it


Really cool stuff. The arbitrary css in class names is just so cool, especially with css vars.


It's an interesting approach, but does it really solve the problems atomic/functional css have asude from purging/unused styles?

I always felt like the most useful thing tailwind actually brings is the preset based style system, as a dev u don't need to remember if the styleguide calls for 6 or 8 pixel space, it's just "m" to me and i can apply styles without worrying too much about design details.

If this approach compiles the atomic classes depending on arbitrary numbers, have i gained anything aside using the "class" instead of the "style" property? (aside specificity of course)


Nice that this looks like it's going to be the engine for WindiCSS 4.0.. less fragmentation is for the best, and Windi does a fantastic job already.


I don't know what to think about this style of CSS.

I was taught to use classes semantically and style from there.

A navbar would be a .navbar class for example and that would be styled in the stylesheet.

Istead of a bunch of classes in the html to define the styles for the navbar.

I'm old school though and this may be the better more modern way?


This might be worth a read or listen to with read aloud - https://adamwathan.me/css-utility-classes-and-separation-of-...

He discusses why he changed from this thought process & mentions the old CSS Zen Garden many of us grew up with as the "ideal" way to do CSS.

Of course your mileage may vary depending on the type of work/projects you do.


Holy cow have been rabbit holing on all morning on this.

I've had to fight my zen garden conditioning the whole time but I have to admit this looks good.

Thanks for the link!


This is a long read that explores some downsides and limitations of existing atomic css frameworks. Then it introduces UnoCSS, which is an interesting and fresh new take on the idea.


Antfu is great, I have been following his oss work for the past year and everything he does is great

He is also one of the main vite contributors




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

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

Search: