Robin Weser’s “The Shorthand-Longhand Problem in Atomic CSS” in an interesting journey through a tricky problem. The point is that when you take on the job of converting something HTML and CSS-like into actual HTML and CSS, there are edge cases that you’ll have to program yourself out of, if you even can at all. In this case, Fela (which we just mentioned), turns CSS into “atomic” classes, but when you mix together shorthand and longhand, the resulting classes, mixed with the cascade, can cause mistakes.
I think this whole idea of CSS-in-JS that produces Atomic CSS is pretty interesting, so let’s take a quick step back and look at that.
Atomic CSS means one class = one job
Like this:
.mb-8 {
margin-bottom: 2rem;
}
Now imagine, like, thousands of those that are available to use and can do just about anything CSS can do.
Why would you do that?
Here’s some reasons:
- If you go all-in on that idea, it means that you’ll ship less CSS because there are no property/value pairs that are repeated, and there are are no made-up-for-authoring-reasons class names. I would guess an all-atomic stylesheet (trimmed for usage, which we’ll get to) is a quarter the size of a hand-authored stylesheet, or smaller. Shipping less CSS is significant because CSS is a blocking resource.
- You get to avoid naming things.
- You get some degree of design consistency “for free” if you limit the available classes.
- Some people just prefer it and say it makes them faster.
How do you get Atomic CSS?
There is nothing stopping you from just doing it yourself. That’s what GitHub did with Primer and Facebook did in FB5 (not that you should do what mega corporations do!). They decided on a bunch of utility styles and shipped it (to themselves, largely) as a package.
Perhaps the originator of the whole idea was Tachyons, which is a just a big ol’ opinionated pile of classes you can grab as use as-is.
But for the most part…
Tailwind is the big player.
Tailwind has a bunch of nice defaults, but it does some very smart things beyond being a collection of atomic styles:
- It’s configurable. You tell it what you want all those classes to do.
- It encourages you to “purge” the unused classes. You really need to get this part right, as you aren’t really getting the benefit of Atomic CSS if you don’t.
- It’s got a UI library so you can get moving right away.
Wait weren’t we talking about automatically-generated Atomic CSS?
Oh right.
It’s worth mentioning that Yahoo was also an early player here. Their big idea is that you’d essentially use functions as class names (e.g. class="P(20px)"
) and that would be processed into a class (both in the HTML and CSS) during a build step. I’m not sure how popular that got really, but you can see how it’s not terribly dissimilar to Tailwind + PurgeCSS.
These days, you don’t have to write Atomic CSS to get Atomic CSS. From Robin’s article:
It allows us to write our styles in a familiar “monolithic” way, but get Atomic CSS out. This increases reusability and decreases the final CSS bundle size. Each property-value pair is only rendered once, namely on its first occurence. From there on, every time we use that specific pair again, we can reuse the same class name from a cache. Some libraries that do that are:
• Fela
• Styletron
• React Native Web
• Otion
• StyleSheetIn my honest opinion, I think that this is the only reasonable way to actually use Atomic CSS as it does not impact the developer experience when writing styles. I would not recommend to write Atomic CSS by hand.
Johan Holmerin wrote about style9 here on CSS-Tricks too which does the same.
I think that’s neat. I’ve tried writing Atomic CSS directly a number of times and I just don’t like it. Who knows why. I’ve learned lots of new things in my life, and this one just doesn’t click with me. But I definitely like the idea of computers doing whatever they have to do to boost web performance in production. If a build step turns my authored CSS into Atomic CSS… hey that’s cool. There are five libraries above that do it, so the concept certainly has legs.
It makes sense that the approaches are based on CSS-in-JS, as they absolutely need to process both the markup and the CSS — so that’s the context that makes the most sense.
What do y’all think?
In twind, a tailwind in js runtime, we had the same problem. To insert the css rule in a deterministically order we calculate a precendence number for each CSS rule which determines the position in the stylesheet (ascending; eg lower numbers first). The idea originates from otion and we adjusted it for the tailwind. We basically use the following rules (i have omited some tailwind specific rules):
dark mode – to override light mode styles
media queries – are sorted in a mobile first manner using the
min-width
valueother at-rules – like
prefers-reduced-motion
pseudo classes – ensure correct order; for example
active
overridesfocus
andhover
(see When do the :hover, :focus, and :active pseudo-classes apply?number of declarations (descending) – this allows single declaration styles to overwrite styles from multi declaration styles
greatest precedence of properties (ignoring vendor prefixed and custom properties) – shorthand properties are inserted first eg longhand properties override shorthands
I use Tailwind, Vuetify, and Bulma, currently. I have a lot of trouble understanding the added value. How style = “background: red” is different from class = “red”. I find HTML is getting really overloaded.
We’re also doing this and it feels pretty great.
I don’t agree with any of your reasons for atomic CSS to be worthwhile. Let’s go through them 1 by 1:
Sure, you have less CSS, but you’ll have hundreds / thousands more classes in your HTML, which is also blocking of course, so it’s just passing the load from one resource to another.
If you use one of the libraries, then yes. But that would be true even with traditional presentation libraries (Bootstrap / Foundation). As soon as you want to extend it, you probably have to name more things that before (though with simpler names sure).
In theory yes, because there’s one class for each type of CSS property. But there’s more chance of a developer putting a bunch of inconsistent classes on an HTML element? The inconsistency is once again simply moved to the HTML.
Well, we can’t argue with that.
Ultimately we should use what we’re comfortable to use (though we get comfortable through repeated use), what produces great work (though this depends on how well we use the tool), and what is practical (depending on budget / circumstances).
Repeating words, like class names, compress well, so it’s not a problem.
Yes, sometimes you’ll want to create a “component”, i.e. a reusable set of styles, but as you cleverly predicted, you’ll probably have less trouble naming it.
I think the word “consistency” is referring to stuff like spacing scale, colors etc. When writing CSS there’s nothing really stopping you from breaking those rules, other than code reviewers and to a certain extent linting tools. In utility-first CSS this is not possible, so I’d say stronger consistency is inherent.
Utility-first CSS definitely looks terrible at first, so it’s requires some emotional investment. Chris didn’t like it, I do because I was unable to reach a combination of CSS best practices that worked for me, so Tailwind’s approach resonated with me. Others continue to hate it, it all depends on the motivation.
I guess my biggest pain with Tailwind is when I start decorating a piece of html, add 15 classes, duplicate it and then have to change something somewhere in that long line (or list) of class attributes.
It’s just hard to parse.
This doesn’t make things easier to read when many elements come with that many classes
And sure, one can take those list of class names and create a unique class, and I think I like that approach the most, but then I’m too used to bootstrap and their sass utility classes and just go with their way.
At the end I can see why it is appealing and people seem to enjoy it.
In twind we introduced grouping to reduce the class list size. Your example could be written as:
Additional we support aliases for these component like classes:
The thing that never made sense to me is that with atomic css it virtually always takes more work to make small visual changes, as you just end up replicating the same conceptual set of styles over and over again. So instead of saying in one place how each (for example) card should look and then specialize from there, instead you end up searching for every instance of something that should look kind of like a card. And that’s not even talking about how much bigger bundle sizes are with atomic css (due to the increase in both html and css). Frankly the only advantage seems to be that it allows initial development to be easier, which is fine for mock-ups… But then, why not just use inline style tags…? Frankly I happily use a variety of CSS solutions, from scoped css, to styled components, to semantic css… And all except atomic css seem quite sensible.
I think atomic css is suitable for Component based framework, like vue, react or svelte. I always use tailwind on react or svelte project, so powerful, no repetition at all, all style is reused by component. But for native html, I think native css is better.
I’ve been working on Compiled, a build time atomic CSSin-JS library here: https://github.com/atlassian-labs/compiled
It aims to have feature parity with other libraries as well as be able to be fully extracted to a static atomic style sheet. As well as keep the DX as it is today. You wouldn’t even feel like it’s output as atomic CSS!
Interestingly there are benefits of atomic CSS above reuse and consistency.
It has allowed us to implement style composition that works at runtime, with build time styles.
It’s super powerful and really does enable a bigger ecosystem potential, because you can ship packages to NPM and can still resiliently override styles, not needing to worry about the cascade. It just works.
Pretty keen for what’s next!
The biggest thing missing from this article (and what’s bugging a lot of the commenters) is the concept of extracting components. TailwindCSS does a good job of explaining this here: https://tailwindcss.com/docs/extracting-components
This is one of the biggest advantages of Tailwind IMO. Being able to still use methodologies like BEM but still maintain some of the advantages of an atomic system. You can make up your own “best of both worlds” approach.
I’ve just started diving into Tailwind on a regular basis, using it in 11ty, Preact, and WP templates. So far, I’m liking its flexibility.
True story:
15 years ago, when I just got started as a developer, my CSS started to evolve in that direction. I had w25 for
width: 25%
and w50 forwidth: 50%
and ten or fifteen more for width, height, margins, paddings…After some months, I realized how stupid I was to write HTML for my CSS, instead of vice versa. I stopped, started writing real CSS and never looked back.
Now, 10-15 years later, I sit on my porch and see the young uns commit the same silly mistakes I made back then.
Only that most dont seem to realize the mistake and let it be like I did back then.
That’s sad.
That was an interesting read, didn’t know about the double selector solution.
I wrote a quite similar but more general post about Atomic CSS-in-JS
https://sebastienlorber.com/atomic-css-in-js
This technique has been used on the new Facebook website and the css is way smaller now as it scales linearly.
Prerendered static pages can have very minimal inlined css and load faster even if the html increase.
One problem remains to me as it makes the jsx code larger too, leading to more JS code to download for framework hydration. Not sure how it will be solved.
What’s the difference between the atomic CSS and inline styles? A layer of abstraction which would never be used as abstraction in 99% of cases?
Pushing the styling to something more cachable.
The FAQ on acss.io answers this question.
How is Atomic CSS different than using inline styles?
Hi Chris,
You say (about ACSS):
This article being about “Auto-Generated Atomic CSS” I’m surprised to see that you do not mention the huge advantage Dynamic libraries have over static ones: it is their ability to produce any style authors need or want. In other words, authors do not have to choose from a finite list of styles in a stylesheet, they can create styles on the fly!
Exactly the point which most people miss!
I’ve been having trouble getting Tailwind/PurgeCSS to remove unused styles based on my static site’s DIST folder. I want to be able to use atomic classes in authoring, build, then remove the atomic classes that aren’t used based on what my content authors are actually using. Has anybody found a way to do that yet?
What do I think? I think the whole “atomic CSS” movement is sorely misguided and idiotic. As a user, have you ever tried to customize (or fix) elements of a site that uses atomic CSS — you can’t.
Basically it’s just a lazy-arse abdication of the responsibility of designing the page elements and naming them well.