avatarHajime Yamasaki Vukelic

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

9504

Abstract

ell as most scoped CSS implementations) is remarkable for the super-tight coupling between the presentation and document structure, because atomic CSS <i>deliberately </i>sacrificed the separation for some alleged benefits.</p><h1 id="ee39">Why atomic CSS</h1><p id="03e0">Atomic CSS is probably the most radical idea (before CSS-in-JS) that the industry has seen since CSS itself. The curious thing it did is it flipped the table on CSS and did something that looks almost exactly like the pre-CSS HTML — without squinting too hard, too. The inventor deliberately sacrificed the separation introduced by CSS in order to achieve some other benefits (and no, it’s not “I don’t want to edit CSS files” because that wasn’t why atomic CSS was invented).</p><p id="2937">The main alleged benefit is that it makes the UI elements more portable by making selectors context-free. That is, if you move some HTML around, you don’t need to fix the CSS to address that because it is not dependent on either what the element is or where in the document it is located. While it sounds like a thing, I will give you reasons later for why it isn’t really all that useful.</p><p id="3750">The inventor also quotes some other benefits, like stable class names, not grouping selectors, but those are, at best, aimed at developers who are doing a poor job of coding CSS to begin with, like using presentational classes, or, at worst, completely arbitrary (like “grouping selectors results in more CSS”, which does not make any sense in my own experience). Some of the “benefits” of the atomic CSS have been superseded by advancement in the browser technology (e.g., RTL vs LTR).</p><p id="3610">The gist of the atomic CSS — and Tailwind, its successor — is therefore that you should code CSS within HTML using an alternative syntax so that, if you move the HTML around, you don’t have to change the CSS, and for that, you sacrifice the ability to give CSS exclusive control over presentation.</p><h1 id="a52b">Arbitrary what?</h1><p id="1735">Let me first get the “arbitrary” part out of the way. I’ll quote the inventor’s words from the <a href="https://www.smashingmagazine.com/2013/10/challenging-css-best-practices-atomic-approach/">2013 article</a> verbatim below and then explain why I think each point is arbitrary.</p><blockquote id="4023"><p>Simple changes to the style of our module have resulted in new rules in the style sheet.</p></blockquote><p id="55aa">This claim is nonsensical. CSS describes the presentation. Changing the appearance of some UI element will necessitate a change in the CSS. Does that always result in a new rule? Of course not. That’s only the case if you add new <i>variants</i> of the element to the existing one or you’re doing something generally wrong with CSS to begin with. I think the word “module” gives us a hint as to where the issue might be, but I wasn’t there so I can’t say for sure.</p><blockquote id="632f"><p>Grouping selectors, rather than using a class associated with these styles, will lead to more CSS.</p></blockquote><p id="2333">I have no idea how this works for them, but this is again, not a rule. It’s just the inventor’s observation with whatever class-based CSS they were working with at the time. Adding multiple selectors to a declaration block does not result in more CSS unless you count selectors as “more CSS”, in which case… what can I say. More power to you.</p><blockquote id="2f53"><p>To change direction [from rtl to ltr or vice versa], we’d need to overwrite some of our styles (i.e. write <i>more</i> rules).</p></blockquote><p id="c6f5">This is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction">true to an extent</a> if you are mixing different directions within the same page and doing so dynamically. Otherwise, this is <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir">not necessary at all</a>. At least not anymore.</p><blockquote id="816d"><p>Regarding unsemantic class names […] [in] W3C’s “Tips for Webmasters,” where it says “Good names don’t change,” you’ll see that the argument is about <i>maintenance</i>, not semantics per se. [Therefore, atomic CSS’s use of unsemantic class names is ok.]</p></blockquote><p id="6689">I’ve already written about this in <a href="https://readmedium.com/what-the-class-f2c88556d623">What the class?</a> Semantic naming should not arbitrarily change (unless the role of the element changes), and the markup does not need to change when you change the appearance either. From the maintenance point of view, the only difference is <i>where </i>you make the change, not whether you have make one or not. If you are using the full range of selectors to their full potential, and you are changing the appearance of some UI, all the changes are (normally and ideally) restricted to CSS. If you use atomic CSS, all of the change are restricted to HTML. Although you don’t change the class names themselves, you do have to edit the contents of the <code>class</code> attribute as that <i>is<b> </b></i>your CSS! This argument about stable class names is pretty much irrelevant. Having class names that don’t change does absolutely nothing useful for you in case of atomic CSS. It’s like stating that CSS property names like <code>display</code> or <code>position</code> never change—a completely useless claim.</p><p id="5914">And here’s a modern one from Tailwind:</p><blockquote id="5772"><p>build websites […] without ever leaving your HTML.</p></blockquote><p id="b825">This claim is frequently echoed as the first benefit that comes to mind by the Tailwind proponents. Another way they put it is “switching between HTML and CSS all the time is a pain in the padding-bottom”. The reason this switch happens in the first place is because HTML and CSS aren’t cleanly separated for these folks. If you separate them correctly, what happens is you either work on HTML <i>or</i> CSS but rarely on both at the same time. Occasionally — meaning rarely — you may go into the HTML to add some class here or an additional wrapper element there, and that’s about it. On the other hand, if you’re tweaking your HTML constantly while you’re working on the <i>appearance</i> of the page, you are either inexperienced (and that’s ok, we all have to start somewhere, as I said) or you are doing <b>HTML </b>wrong to begin with, treating it as an extension of CSS rather than the markup for the document structure (this is <i>not </i>ok, if you couldn’t tell).</p><h1 id="5b64">Not-so-arbitrary claims?</h1><p id="e75b">There’s one claim that <i>sort of </i>makes sense. And that’s this:</p><blockquote id="4a91"><p>Because the styles are “encapsulated,” you can move modules around without losing their styles.</p></blockquote><p id="dd7f">This is supposedly done for the following reason:</p><blockquote id="73e5"><p>Rules that are context-specific are hard to maintain. Styles related to such rules are not very reusable.</p></blockquote><p id="93c4">The reason to encapsulate is, of course, completely arbitrary. It’s hardly a rule that always applies no matter what you do. I’ve used context-specific selectors numerous times and it never hindered maintenance nor got in the way of reuse. But the property that encapsulation allows is legit. This is the same idea echoed in the scoped CSS (CSS modules, CSS-in-JS).</p><p id="9bda">Something being legit and something being actually useful are two different things, however.</p><p id="fc47">Context-specific rules are rules like this:</p><div id="e224"><pre><span class="hljs-selector-class">.hero</span> > <span class="hljs-selector-class">.button</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">160%</span>; }</pre></div><p id="cedb">This selects the <code>.button</code> element only within the <code>.hero</code> element. If you move that button outside the <code>.hero</code> element, then the style no longer applies. The argument is that, if you place the atomic class directly on the element, like:</p><div id="c24d"><pre><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fs-160"</span>></span>Click me now<span class="hljs-tag"></<span class="hljs-name">button</span>></span></pre></div><p id="91c2">then it does not matter where you move this button, and it will always look the same.</p><p id="c72b">On the surface, this sounds logical, and <i>sounds </i>like a benefit. But there are two issues with it.</p><p id="bd34">Firstly, if you really want this behavior, you can simply slap an id or a class on the element and select that outside of any context. It will behave exactly the same as if you had all the styling directly on the element as you move it around. In other words, atomic CSS is not doing anything you can’t already do.</p><p id="198e">Secondly, it’s highly unlikely that you will <i>always </i>benefit from this property. For example, it could be counter-argued that a button only needs to have larger font size when in specific areas of the page. I do not doubt that the inventor has encountered issues where this property of atomic CSS is beneficial <b>compared to what they were doing before</b>, but I wasn’t there, so I cannot say for sure how I would address those specific issues and whether my solution would be an improvement. The best I can hope for is that I may yet encounter it one day and then come up with a better solution. 😎</p><p id="525b">If

Options

the only <i>actual </i>benefit of using atomic CSS (including Tailwind) — style encapsulation — is of questionable utility (pun intended), why are we making all these sacrifices for it and learning a new language? To me it sounds like a city of broken promises.</p><h1 id="13d8">So what’s the alternative?</h1><p id="6e18">The <i>real</i> question is whether we <i>need </i>an alternative at all. If people would even bother to try using CSS as intended for once, maybe they would, like me, conclude that “we’ve been doing it wrong all these years/months/days” and there was nothing broken with CSS to begin with. Well, not as much as these framework/technique/library authors would have you believe anyway. I think maybe I would have had a harder time making this claim back in 2013 — I don’t remember really — but today I have absolutely no problem standing behind this claim.</p><p id="874f">Let me show you what I mean.</p><p id="4d31">We can convert the class-based button example to look like this:</p><div id="eea9"><pre><span class="hljs-selector-tag">button</span> { <span class="hljs-attribute">background</span>: blue; <span class="hljs-attribute">color</span>: white; <span class="hljs-attribute">font-weight</span>: bold; <span class="hljs-attribute">cursor</span>: pointer; }

<span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:disabled</span> { <span class="hljs-attribute">background</span>: grey; <span class="hljs-attribute">color</span>: silver; }

<span class="hljs-selector-id">#hero</span> > <span class="hljs-selector-tag">button</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">120%</span>; }

<span class="hljs-selector-id">#hero</span> > <span class="hljs-selector-tag">button</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-pseudo">:disabled</span>) { <span class="hljs-attribute">background</span>: red; <span class="hljs-attribute">color</span>: yellow; }</pre></div><p id="a056">Try it for yourself. Jumble up the order of these declaration blocks, and you will see that, for the most part, it doesn’t matter, for instance. That’s better for maintenance. The <code>#hero</code> selector is not very likely to change over time, and there aren’t any other selectors that are likely to change either. So we have stable selectors.</p><p id="cb7c">Some of the things, like the <code>:not()</code> pseudo-class did not exist back in 2013, but now that it’s supported by all major browsers, it has never been easier to write <a href="https://readmedium.com/mutually-exclusive-selectors-9e8bd29baafb">CSS that does not clash with your own code</a>.</p><p id="7cbd">This is the CSS’s intended usage.</p><p id="bf29">Does it have more lines of CSS than the atomic CSS (or Tailwind)? Sure it does. But it also has <i>less</i> code in the HTML.</p><p id="d162">Consider this markup using atomic CSS.</p><div id="e40a"><pre><span class="hljs-tag"><<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hero"</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-grey c-silver fs-120 cr-pointer"</span> <span class="hljs-attr">disabled</span>></span>Click here<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">section</span>></span></pre></div><p id="1851">I’ll let you imagine the CSS portion. Compare this to the HTML for the normal version:</p><div id="6a8a"><pre><span class="hljs-tag"><<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hero"</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">disabled</span>></span>Click here<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">section</span>></span></pre></div><p id="e5d2">And the HTML bloat with atomic CSS is an even more serious issue with the kind of CSS people write today. All those transitions, animations, hover effects, focus effects, everything crammed into the class attribute with super-terse syntax that is kinda like CSS but not really…</p><p id="b2f1">And let me reiterate on the “context-free” claim.</p><p id="4810">If I move any <code>button</code> element anywhere using our normal CSS, it obviously will not be affected. If I move a <code>button:disabled</code> element, similarly it won’t be affected. Those selectors are already context-free because they don’t <i>have </i>to be in context. The only button that will change the appearance based on context is the one being moved into or out of the <code>#hero</code> element. Do I <i>want</i> buttons to behave that way? Yes, in fact I do! As an ex web designer — sorry, I mean ex UX engineer — this is an awesome and welcome feature! The special appearance of the button <i>depends </i>on its context. It would be tedious to edit the CSS/classes every time I need to move a button.</p><p id="2283">And it gets even weirder when I want to reuse the code.</p><p id="6f8b">Let’s say I have three buttons on the page.</p><div id="4699"><pre><<span class="hljs-selector-tag">button</span>>First</<span class="hljs-selector-tag">button</span>> <<span class="hljs-selector-tag">button</span>>Second</<span class="hljs-selector-tag">button</span>> <<span class="hljs-selector-tag">button</span>>Third</<span class="hljs-selector-tag">button</span>></pre></div><p id="958f">The non-atomic version of CSS <i>already works</i> for this. How’s that for reuse!</p><p id="d0ad">What about the atomic version?</p><div id="daa0"><pre><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue c-yellow cr-pointer fw-bold"</span>></span>First<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue c-yellow cr-pointer fw-bold"</span>></span>Second<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue c-yellow cr-pointer fw-bold"</span>></span>Third<span class="hljs-tag"></<span class="hljs-name">button</span>></span></pre></div><p id="fd01">Well, that’s some reuse, isn’t it? So, what do atomic CSS fans say in this situation? “That’s because you are not using components or partial templates to DRY your code. I bet your code is bloated as fsck in other areas as well.” Well, I don’t know. You tell me. I didn’t even <i>need</i> components to reuse my code, but maybe I’m missing something. And I don’t know about you, but I like to save my multi-cursor prowess for something more meaningful than WET-ing my code like this even if I could pretend it’s somehow DRY with a straight face.</p><p id="7873">It’s quite ironic that the inventor of the atomic CSS says he invented it because he wanted to get rid of bloat…</p><h1 id="929d">The elephant in the room</h1><p id="f8b2">Now that I’ve got the <i>obvious</i> parts — as in clearly visible if you know where to look — out of the way, let me address the not-so-obvious parts.</p><p id="4b71">Atomic CSS can be done manually by writing “utility classes” yourself, or you could use a <i>tool</i> to do that for you based on the classes that you have. I suspect the main reason atomic CSS was not very popular originally was the lack of tools. When I tried it back then, it was so tedious!</p><p id="5438">Today you have tools like <a href="https://acss.io/">Atomizer</a> or <a href="https://tailwindcss.com/">Tailwind</a>. As with any tool, though, it <i>costs</i> to use those. It may be free <i>of charge</i>, but it’s not free as in zero-cost. Any time something else does some work for you, it doesn’t mean the work itself evaporated into <code>/dev/null</code>. It just means it got delegated to something else, and more often than not, it creates additional work that you still need to do. If I wanted to sound like a physicist, I’d say “Work can neither be crated nor destroyed, it can only be transformed and abstracted” or something fancy along those lines. 🤣</p><p id="c706">You should know better than, even for a single second, delude yourself that <code>npm install</code>-ing something is free. It costs you the time to set up the project, keep an eye on security issues, keep things updated, and usually introduce additional steps you need to take before you can see the changes in the browser. Sometimes it also means that you need to (not so) subtly modify your code to suit the tool, or maintain configuration files. It may make deployment more complicated because now you need to build something instead of just shipping your files as is to a properly configured server. I wouldn’t exactly call any of that “free”.</p><h1 id="f45a">Bonus rant</h1><p id="6915">Oh and have I mentioned how depressed I am reading the claims that atomic CSS invented in 2013 — which embodies the ideas of of the 1990s — is <a href="https://readmedium.com/all-this-modern-technology-670f64fd5fd0">“modern”</a>?</p></article></body>

The broken promise of atomic CSS

The (not so) hidden cost of atomic CSS

Photo by Aimee Vogelsang on Unsplash

Not knowing something isn’t a mortal sin. We all have to start somewhere after all. Sadly, today’s web contains many traps for the unsuspecting. In fact, it has for over a decade now. Some do it out of ignorance, some to satisfy their narcissistic tendencies — regardless of their reasons, they sell you things that are not only unnecessary, but outright bad for your code.

I was recently doing some web scraping project. Initially it was amusing seeing someone else’s code for a change, and revisiting the entire history of bad code on the web. But it quickly god old. God, the web is so full of bad code! While making the scraper’s job harder isn’t necessarily a bad thing, there’s otherwise literally no benefit to doing things the way they are commonly done. None.

The HTML is a bloated mess loaded with (mostly unnecessary) classes, and it’s difficult to select the elements in the non-semantic div soup typical of such websites. I’m not at all surprised that people can’t be convinced to use vanilla JavaScript for anything, since the HTML and CSS they use simply would not allow it. They’ve got much bigger issues. And that’s without even taking accessibility into account.

The worst offender is by far the atomic CSS and its latest implementations, like Tailwind, Atomizer and StilifyCSS.

If you are enamored by atomic CSS and Tailwind, there’s a good chance you’re not taking full advantage of both CSS and HTML. In other words, you think you know CSS and HTML, but you don’t. You are merely acquainted with their syntax (and possibly a few alternative ones), but knowing the syntax and making the language(s) work for you are not the same thing. Not really.

I’m not judging you, though, don’t get me wrong. If I thought you’re plain stupid, why would I bother writing an entire article for you? It’s just that the “experts” that sold you this idea are well… really good at selling ideas apparently, and they sometimes even work at fancy companies. And you probably never worked with someone who’d tell you all about how stupid some of these ideas are. That’s hardly your fault.

Some background

Let’s start with some background. And I don’t mean background. I mean history.

This idea that you do all the styling in HTML is, itself not new. In fact, it predates CSS. When CSS was introduced in 1996, it was intended as a fix for the mix of semantic and presentation tags (yes, tags) that plagued the web back in those days. The idea was simple: HTML focuses on the semantic document structure — what something is — while CSS focuses on the presentation — what something looks like (on a particular device). You go from something like this (and it still works in today’s browsers, mind you):

<center><p>This text is centered.</p></center>

to this:

<p id="intro">This text is centered.</p>

And you link a second file where you have this:

#intro {
  text-align: center;
}

To achieve the stated separation, CSS was equipped with a rich syntax for describing elements and collections of elements in HTML without having to modify the HTML and without bloating it with non-semantic information — selectors. This also means that by swapping the CSS file, one could give the exact same document a completely different appearance. CSS Zen Garden showcases this idea with a huge variety of designs applied to the same content. This separation had some additional benefits, such as the ability to load the CSS files separately while HTML is being loaded/parsed, or reusing the same cached styling across multiple pages.

Ideas and intentions are one thing, and their real-life application is usually something else.

Most web developers, even back in those days, focused mainly on the visual aspect of the page. This became especially prevalent once the web designer (called UX designer today) became a separate role—a non-programmer role specializing in visual appearance. Web developers that focused solely on the visual aspects of the page generally ignored the semantics of the HTML and used HTML to “design” pages. For them, the idea was that HTML represents a collection of styled elements — their role in the document is mainly to facilitate application of CSS declarations, basically a glue between content and CSS. If that sounds like “styled components” to you, that’s because styled components are but an extension of this idea.

This lead to the appearance of “class-based CSS” — a term I coined for the purpose of writing this article with less keystrokes. The class-based CSS uses classes as labels for declaration blocks. The style reuse is facilitated using inheritance —not dissimilar to inheritance in OOP. For instance, a .button base class is applied to all buttons, and then more specialized versions of button, like .button-cta, are applied to specific buttons.

.button {
  background: blue;
  color: white;
  font-weight: bold;
  cursor: pointer;
}

.button-cta {
  font-size: 120%;
  background: red;
  color: yellow;
}

I imagine this approach felt more familiar to people coming from a “real” programming language (as if CSS is imaginary). However, this approach starts to become complicated when we introduce variants that need to apply together, which is something you never run into in OOP. For instance:

.button-disabled {
  background: grey;
  color: silver;
}

If both .button-disabled and .button-cta need to apply at the same time, the order in which those rules appear in the CSS matters. If .button-disabled appears before .button-cta, it does not achieve the desired effect (supposing that -disabled must take precedence over -cta). In this simple case, reordering the rules fixes the issue, but things are rarely this simple in any non-trivial application.

It gets even more complicated when these rules are not in the same file, and fine-tuning their order becomes more complicated if not impossible. This sometimes results in attempts to inflate the selector specificity (e.g., .button.button-disabled) or the dreaded !important. And then what if we need to override styles in the -disabled version? This is especially true in cases where we are dealing with popular CSS UI frameworks such as Bootstrap where we do not control large portions of (often complicated) code.

Various methods have since been invented to deal with these issues. Most notably the BEM naming scheme, atomic CSS (Tailwind’s predecessor from 2013), and, with the advent of component-based code organization, scoped CSS including CSS modules and CSS-in-JS.

The issues these numerous tool-assisted and non-tool-assisted approaches attempt to solve are ultimately not issues with the CSS itself, but with the dominant way in which CSS is used, and they are all — without exception — still classified as class-based CSS. They also universally suffer from one or more issues associated with all class-based approaches. Most notably atomic CSS (as well as most scoped CSS implementations) is remarkable for the super-tight coupling between the presentation and document structure, because atomic CSS deliberately sacrificed the separation for some alleged benefits.

Why atomic CSS

Atomic CSS is probably the most radical idea (before CSS-in-JS) that the industry has seen since CSS itself. The curious thing it did is it flipped the table on CSS and did something that looks almost exactly like the pre-CSS HTML — without squinting too hard, too. The inventor deliberately sacrificed the separation introduced by CSS in order to achieve some other benefits (and no, it’s not “I don’t want to edit CSS files” because that wasn’t why atomic CSS was invented).

The main alleged benefit is that it makes the UI elements more portable by making selectors context-free. That is, if you move some HTML around, you don’t need to fix the CSS to address that because it is not dependent on either what the element is or where in the document it is located. While it sounds like a thing, I will give you reasons later for why it isn’t really all that useful.

The inventor also quotes some other benefits, like stable class names, not grouping selectors, but those are, at best, aimed at developers who are doing a poor job of coding CSS to begin with, like using presentational classes, or, at worst, completely arbitrary (like “grouping selectors results in more CSS”, which does not make any sense in my own experience). Some of the “benefits” of the atomic CSS have been superseded by advancement in the browser technology (e.g., RTL vs LTR).

The gist of the atomic CSS — and Tailwind, its successor — is therefore that you should code CSS within HTML using an alternative syntax so that, if you move the HTML around, you don’t have to change the CSS, and for that, you sacrifice the ability to give CSS exclusive control over presentation.

Arbitrary what?

Let me first get the “arbitrary” part out of the way. I’ll quote the inventor’s words from the 2013 article verbatim below and then explain why I think each point is arbitrary.

Simple changes to the style of our module have resulted in new rules in the style sheet.

This claim is nonsensical. CSS describes the presentation. Changing the appearance of some UI element will necessitate a change in the CSS. Does that always result in a new rule? Of course not. That’s only the case if you add new variants of the element to the existing one or you’re doing something generally wrong with CSS to begin with. I think the word “module” gives us a hint as to where the issue might be, but I wasn’t there so I can’t say for sure.

Grouping selectors, rather than using a class associated with these styles, will lead to more CSS.

I have no idea how this works for them, but this is again, not a rule. It’s just the inventor’s observation with whatever class-based CSS they were working with at the time. Adding multiple selectors to a declaration block does not result in more CSS unless you count selectors as “more CSS”, in which case… what can I say. More power to you.

To change direction [from rtl to ltr or vice versa], we’d need to overwrite some of our styles (i.e. write more rules).

This is true to an extent if you are mixing different directions within the same page and doing so dynamically. Otherwise, this is not necessary at all. At least not anymore.

Regarding unsemantic class names […] [in] W3C’s “Tips for Webmasters,” where it says “Good names don’t change,” you’ll see that the argument is about maintenance, not semantics per se. [Therefore, atomic CSS’s use of unsemantic class names is ok.]

I’ve already written about this in What the class? Semantic naming should not arbitrarily change (unless the role of the element changes), and the markup does not need to change when you change the appearance either. From the maintenance point of view, the only difference is where you make the change, not whether you have make one or not. If you are using the full range of selectors to their full potential, and you are changing the appearance of some UI, all the changes are (normally and ideally) restricted to CSS. If you use atomic CSS, all of the change are restricted to HTML. Although you don’t change the class names themselves, you do have to edit the contents of the class attribute as that is your CSS! This argument about stable class names is pretty much irrelevant. Having class names that don’t change does absolutely nothing useful for you in case of atomic CSS. It’s like stating that CSS property names like display or position never change—a completely useless claim.

And here’s a modern one from Tailwind:

build websites […] without ever leaving your HTML.

This claim is frequently echoed as the first benefit that comes to mind by the Tailwind proponents. Another way they put it is “switching between HTML and CSS all the time is a pain in the padding-bottom”. The reason this switch happens in the first place is because HTML and CSS aren’t cleanly separated for these folks. If you separate them correctly, what happens is you either work on HTML or CSS but rarely on both at the same time. Occasionally — meaning rarely — you may go into the HTML to add some class here or an additional wrapper element there, and that’s about it. On the other hand, if you’re tweaking your HTML constantly while you’re working on the appearance of the page, you are either inexperienced (and that’s ok, we all have to start somewhere, as I said) or you are doing HTML wrong to begin with, treating it as an extension of CSS rather than the markup for the document structure (this is not ok, if you couldn’t tell).

Not-so-arbitrary claims?

There’s one claim that sort of makes sense. And that’s this:

Because the styles are “encapsulated,” you can move modules around without losing their styles.

This is supposedly done for the following reason:

Rules that are context-specific are hard to maintain. Styles related to such rules are not very reusable.

The reason to encapsulate is, of course, completely arbitrary. It’s hardly a rule that always applies no matter what you do. I’ve used context-specific selectors numerous times and it never hindered maintenance nor got in the way of reuse. But the property that encapsulation allows is legit. This is the same idea echoed in the scoped CSS (CSS modules, CSS-in-JS).

Something being legit and something being actually useful are two different things, however.

Context-specific rules are rules like this:

.hero > .button {
  font-size: 160%;
}

This selects the .button element only within the .hero element. If you move that button outside the .hero element, then the style no longer applies. The argument is that, if you place the atomic class directly on the element, like:

<button class="fs-160">Click me now</button>

then it does not matter where you move this button, and it will always look the same.

On the surface, this sounds logical, and sounds like a benefit. But there are two issues with it.

Firstly, if you really want this behavior, you can simply slap an id or a class on the element and select that outside of any context. It will behave exactly the same as if you had all the styling directly on the element as you move it around. In other words, atomic CSS is not doing anything you can’t already do.

Secondly, it’s highly unlikely that you will always benefit from this property. For example, it could be counter-argued that a button only needs to have larger font size when in specific areas of the page. I do not doubt that the inventor has encountered issues where this property of atomic CSS is beneficial compared to what they were doing before, but I wasn’t there, so I cannot say for sure how I would address those specific issues and whether my solution would be an improvement. The best I can hope for is that I may yet encounter it one day and then come up with a better solution. 😎

If the only actual benefit of using atomic CSS (including Tailwind) — style encapsulation — is of questionable utility (pun intended), why are we making all these sacrifices for it and learning a new language? To me it sounds like a city of broken promises.

So what’s the alternative?

The real question is whether we need an alternative at all. If people would even bother to try using CSS as intended for once, maybe they would, like me, conclude that “we’ve been doing it wrong all these years/months/days” and there was nothing broken with CSS to begin with. Well, not as much as these framework/technique/library authors would have you believe anyway. I think maybe I would have had a harder time making this claim back in 2013 — I don’t remember really — but today I have absolutely no problem standing behind this claim.

Let me show you what I mean.

We can convert the class-based button example to look like this:

button {
  background: blue;
  color: white;
  font-weight: bold;
  cursor: pointer;
}

button:disabled {
  background: grey;
  color: silver;
}

#hero > button {
  font-size: 120%;
}

#hero > button:not(:disabled) {
  background: red;
  color: yellow;
}

Try it for yourself. Jumble up the order of these declaration blocks, and you will see that, for the most part, it doesn’t matter, for instance. That’s better for maintenance. The #hero selector is not very likely to change over time, and there aren’t any other selectors that are likely to change either. So we have stable selectors.

Some of the things, like the :not() pseudo-class did not exist back in 2013, but now that it’s supported by all major browsers, it has never been easier to write CSS that does not clash with your own code.

This is the CSS’s intended usage.

Does it have more lines of CSS than the atomic CSS (or Tailwind)? Sure it does. But it also has less code in the HTML.

Consider this markup using atomic CSS.

<section id="hero">
  <button class="bg-grey c-silver fs-120 cr-pointer" disabled>Click here</button>
</section>

I’ll let you imagine the CSS portion. Compare this to the HTML for the normal version:

<section id="hero">
  <button disabled>Click here</button>
</section>

And the HTML bloat with atomic CSS is an even more serious issue with the kind of CSS people write today. All those transitions, animations, hover effects, focus effects, everything crammed into the class attribute with super-terse syntax that is kinda like CSS but not really…

And let me reiterate on the “context-free” claim.

If I move any button element anywhere using our normal CSS, it obviously will not be affected. If I move a button:disabled element, similarly it won’t be affected. Those selectors are already context-free because they don’t have to be in context. The only button that will change the appearance based on context is the one being moved into or out of the #hero element. Do I want buttons to behave that way? Yes, in fact I do! As an ex web designer — sorry, I mean ex UX engineer — this is an awesome and welcome feature! The special appearance of the button depends on its context. It would be tedious to edit the CSS/classes every time I need to move a button.

And it gets even weirder when I want to reuse the code.

Let’s say I have three buttons on the page.

<button>First</button>
<button>Second</button>
<button>Third</button>

The non-atomic version of CSS already works for this. How’s that for reuse!

What about the atomic version?

<button class="bg-blue c-yellow cr-pointer fw-bold">First</button>
<button class="bg-blue c-yellow cr-pointer fw-bold">Second</button>
<button class="bg-blue c-yellow cr-pointer fw-bold">Third</button>

Well, that’s some reuse, isn’t it? So, what do atomic CSS fans say in this situation? “That’s because you are not using components or partial templates to DRY your code. I bet your code is bloated as fsck in other areas as well.” Well, I don’t know. You tell me. I didn’t even need components to reuse my code, but maybe I’m missing something. And I don’t know about you, but I like to save my multi-cursor prowess for something more meaningful than WET-ing my code like this even if I could pretend it’s somehow DRY with a straight face.

It’s quite ironic that the inventor of the atomic CSS says he invented it because he wanted to get rid of bloat…

The elephant in the room

Now that I’ve got the obvious parts — as in clearly visible if you know where to look — out of the way, let me address the not-so-obvious parts.

Atomic CSS can be done manually by writing “utility classes” yourself, or you could use a tool to do that for you based on the classes that you have. I suspect the main reason atomic CSS was not very popular originally was the lack of tools. When I tried it back then, it was so tedious!

Today you have tools like Atomizer or Tailwind. As with any tool, though, it costs to use those. It may be free of charge, but it’s not free as in zero-cost. Any time something else does some work for you, it doesn’t mean the work itself evaporated into /dev/null. It just means it got delegated to something else, and more often than not, it creates additional work that you still need to do. If I wanted to sound like a physicist, I’d say “Work can neither be crated nor destroyed, it can only be transformed and abstracted” or something fancy along those lines. 🤣

You should know better than, even for a single second, delude yourself that npm install-ing something is free. It costs you the time to set up the project, keep an eye on security issues, keep things updated, and usually introduce additional steps you need to take before you can see the changes in the browser. Sometimes it also means that you need to (not so) subtly modify your code to suit the tool, or maintain configuration files. It may make deployment more complicated because now you need to build something instead of just shipping your files as is to a properly configured server. I wouldn’t exactly call any of that “free”.

Bonus rant

Oh and have I mentioned how depressed I am reading the claims that atomic CSS invented in 2013 — which embodies the ideas of of the 1990s — is “modern”?

CSS
Web Development
Programming
Recommended from ReadMedium