Article translated/polished by SSH, original address.

With recent product deployments from Facebook and Twitter, I think a new trend is slowly growing :Atomic CSS-in-JS.

In this article, we’ll see what Atomic CSS is and how it relates to Tailwind CSS, a utility-first style library that many large companies currently use in the React code repository.

Since I’m not an expert in this area, I won’t go into the pros and cons. I just want to give you a sense of what it’s about.

Let’s start with the happy conclusion that the new way CSS is written and built has reduced the size of Facebook’s home page by 80%.

What is atomic CSS?

You’ve probably heard of various CSS methods such as BEM, OOCSS…

<button class="button button--state-danger">Danger button</button>
Copy the code

Now, people really like Tailwind CSS and its utility-first concept. This is very close to the idea of Functional CSS and the Tachyon library.

<button
  class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
  Button
</button>
Copy the code

With a plethora of utility classes, stylesheets can be used in web pages.

Atomic CSS is like an extreme version of Utility-first CSS: all CSS classes have a unique CSS rule. Atomic CSS was originally created by Thierry Koblentz (Yahoo! Used in 2013 when challenging CSS best practices.

/* Atomic CSS */
.bw-2x {
  border-width: 2px;
}
.bss {
  border-style: solid;
}
.sans {
  font-style: sans-serif;
}
.p-1x {
  padding: 10px;
}
/* is not atomic CSS because this class contains two rules */
.p-1x-sans {
  padding: 10px;
  font-style: sans-serif;
}
Copy the code

Using utility/atomic CSS, we can combine the structure layer with the presentation layer: when we need to change the button color, we modify the HTML directly, not the CSS!

This tight coupling is also recognized in the React code base of modern CSS-in-JS, but seems to be the first in the CSS world to have some objections to the traditional separation of concerns.

CSS weights aren’t a problem either, because we’re using the simplest class selector.

We now use HTML tags to add styles, and we find some interesting things:

  • As we added new features, the growth of stylesheets slowed.
  • We can move HTML tags around and make sure the style works as well.
  • We can remove new features and make sure that the style is also removed.

The downside, to be sure, is that the HTML is a bit bloated. This can be a drawback for server-rendered Web applications, but the high redundancy in class names allows GZIP to compress well. It also handles duplicate CSS rules very well.

Once your utility/atomic CSS is ready, it won’t change or grow much. You can cache it more efficiently (you can attach it to vendor. CSS and it won’t be invalidated on redeployment). It is also fairly portable and can be used in any other application.

Limitations of utility/atomic CSS

Utilities/atomic CSS look interesting, but they also present some challenges.

People often write utility/atomic CSS by hand with elaborate naming conventions. But it’s hard to guarantee that this convention is easy to use, consistent, and won’t become bloated over time.

Can this CSS be developed collaboratively and consistently? Is it affected by the bus factor?

The Bus factor, also known as the “Lottery factor” or “Truck factor”, is a measure of the concentration and unshared information and capabilities among software project members in software development. The bus factor is the number of people who lose at least a few key members of a project or project (” hit by the bus “, which refers to career and lifestyle changes, marriage, childbirth, accidental death, etc.) to the point where the project becomes disorganized and paralyzed and cannot survive.

You also need to have a good utility/atomic style sheet developed up front before you can start developing new features.

If utility/atomic CSS is made by someone else, you’ll have to learn the class naming convention first (even if you know everything about CSS). The contract is subjective, and chances are you won’t like it.

Sometimes you need some extra CSS that utility/atomic CSS doesn’t provide. There is no agreed upon way to provide these one-time styles.

Tailwind came to his aid

The method used by Tailwind is very convenient and solves some of the above problems.

It addresses some of the shortcomings of CSS by using the utility-first concept, which abstracts a set of class names -> atomic functions, so that you don’t have to write a specific class for each div and then repeat many styles throughout the site.

Traditional card style:

Tailwind card style

It doesn’t really provide some unique CSS utility for all websites, but instead, it provides some common naming conventions. With a configuration file, you can generate your own set of utility CSS for your site.

SSH note: The author did not go into detail here, why is it a set of naming conventions instead of generating some dead CSS?

To give you a feel for it, Tailwind provides a powerful build system. For example, it provides responsive breakpoints by default:

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      'sm': '640px'.// => @media (min-width: 640px) { ... }

      'md': '768px'.// => @media (min-width: 768px) { ... }

      'lg': '1024px'.// => @media (min-width: 1024px) { ... }

      'xl': '1280px'.// => @media (min-width: 1280px) { ... }}}}Copy the code

You can change these breakpoints at any time in the configuration file. For example, if you want a small screen SM to mean a smaller 320px, then if you want to use the Flex layout for the small screen, write SM :flex as usual. It follows the same convention, but the SM has been modified to suit the project requirements.

For example, spacing in Tailwind governs the spacing of all properties such as margin, padding, width, etc. By default, the spacing is used in rem units. When overridden in the configuration like this:

// tailwind.config.js
module.exports = {
  theme: {
    spacing: {
      '1': '8px'.'2': '12px'.'3': '16px'.'4': '24px'.'5': '32px'.'6': '48px',}}}Copy the code

If you write h-6 (height), M-2 (margin), and MB-4 (margin-bottom), you change the meaning of the numbers.

Perhaps the six became 6REM when you switched from desktop to mobile, but the conventions are stuck in your mind and become part of your knowledge.

Tailwind’s knowledge can be migrated to other applications, even if they don’t use exactly the same class names. It reminds me of React’s “learn once, write everywhere” philosophy.

I’ve seen feedback from users that Tailwind provides class names that cover 90-95% of their needs. This coverage seems broad enough that you don’t need to write one-off CSS very often.

At this point, you might be wondering why use atomic CSS instead of Tailwind CSS? What is the benefit of enforcing a rule, a class name, for an atomic CSS rule? Will you end up with bigger HTML tags and more annoying naming conventions? Tailwind already has enough atom classes.

So, should we abandon the idea of atomic CSS and just use Tailwind CSS?

Tailwind is an excellent solution, but there are still some unresolved issues:

  • You need to learn a subjective naming convention

  • CSS rules insertion order is still important

  • Can unused rules be easily removed?

  • What do we do with the remaining disposable styles?

Hand-written atomic CSS may not be the most convenient compared to Tailwind.

To be in – JS and CSS

Css-in-js is closely related to utility/atomic CSS. Both approaches advocate using tags for styling. Trying to mimic the inline style in some way gives them a lot of similar features (such as more confidence in moving around certain features).

Christopher Chedeau has been promoting the IDEA of CSS-in-JS in the React ecosystem. In many presentations, he explained the problem with CSS:

  1. Global namespace
  2. Rely on
  3. Useless code elimination
  4. Code compression
  5. A Shared constant
  6. Non-Deterministic analysis
  7. isolation

Utility/Atomic CSS solves some of these problems, but it really doesn’t solve all of them (especially the nondeterministic resolution of styles).

If they have a lot in common, can we use them together?

Explore atomic CSS-in-JS

Atomic CSS-in-JS can be thought of as “automated atomic CSS” :

  • You no longer need to create a class class name convention

  • Generic and one-time styles are handled the same way

  • The ability to extract the key CSS required by the page and split the code

  • There is an opportunity to fix the insertion order of CSS rules in JS

I want to highlight two specific solutions that have recently driven the deployment and use of two large-scale atomic CSS-in-JS, derived from the following two presentations.

  • React-native -Web at Twitter (more details in Nicolas Gallagher’s talk).

  • Stylex at Facebook (more details in Frank Yan’s talk).

Also check out these libraries:

  • Styletron
  • Fela
  • Style-Sheet
  • cxs
  • otion
  • css-zero
  • ui-box
  • style9
  • stitches
  • catom

React-Native-Web

React-native Web is a very interesting library that allows browsers to render React-Native primifiers as well. But we’re not talking about cross-platform development here (more details in the talk).

As a Web developer, you just need to understand that React-Native Web is a regular CSS-in-JS library that comes with some of the original React components. So anywhere you write a View component, you can replace it with a div.

React-native Web is written by Nicolas Gallagher, who is working on Twitter mobile. They’re gradually deploying it to mobile devices, not quite sure when, in 2017/2018 or so.

Since then, many companies have used it (MLS, Flipkart, Uber, The New York Times…) “, but the most important deployment was the new Twitter desktop app launched in 2019 by a team led by Paul Armstrong.

Stylex

Stylex is a new CSS-in-JS library being developed by the Facebook team for the 2020 Refactoring of Facebook applications. In the future it will be open source, possibly under another name.

In particular, React-Native Web author Nicolas Gallagher was recruited by Facebook. So it’s no surprise that there are some familiar concepts.

All my information is from the speech :), need to wait for more details.

scalability

Unsurprisingly, with the Atomic CSS addition, the size of Both Twitter and Facebook’S CSS was significantly reduced, and it now grows on a logarithmic curve. However, simple applications have a little bit more initial volume.

Facebook shared the numbers:

  • The old websiteOnly the home pageJust use the413KbThe CSS
  • The new websiteThe entire siteIt only took74Kb, including dark Mode

Source code and output

The apis for the two libraries look similar, but it’s hard to say because we don’t know much about Stylex.

It is worth emphasizing that react-native Web will extend the CSS syntax sugar, such as margin: 0, which will be output as margin atoms in four directions.

Take an example of a component to see the difference between the output of the old version of traditional CSS and the new version of atomic CSS.

<Component1 classNames="class1" /> <Component2 classNames="class2" />
Copy the code
.class1 {
  background-color: mediumseagreen;
  cursor: default;
  margin-left: 0px;
}
.class2 {
  background-color: thistle;
  cursor: default;
  jusify-content: flex-start;
  margin-left: 0px;
}
Copy the code

You can see that both styles have the same cursor and margin-left, but both take up volume in the output.

Take a look at the output of the atomic CSS:

<Component1 classNames="classA classC classD" />
<Component2 classNames="classA classB classD classE" />
Copy the code
class a {
  cursor: default;
}
class b {
  background-color: mediumseagreen;
}
class C {
  background-color: thistle;
}
class D {
  margin-left: 0px;
}
class E {
  jusify-content: flex-start;
}
Copy the code

As you can see, although there are more class names on the tag, the output volume of CSS slows down as more functions are added, because CSS rules that occur once are not repeated.

Production environment validation

Let’s see what a hashtag looks like on Twitter:

Now, let’s look at the new Facebook:

A lot of people might be freaked out, but it works really well and remains accessible.

It might be a little hard to check the styles in Chrome, but it’s pretty clear in DevTools:

Order of CSS Rules

Unlike the hand-written tool/atomic CSS, the JS library allows styles to be independent of the insertion order of CSS rules.

In the case of a rule conflict, the rule that takes effect is not the last class in the class attribute on the tag, but the last rule inserted into the stylesheet.

In this illustration, for example, we expect the blue class written in the back to override the first class, but in fact CSS determines the priority based on the order in the stylesheet, and we end up with red text.

In a real world scenario, these libraries avoid writing multiple classes with conflicting rules on the same element. They ensure that the last class name written on the label takes effect. The other overridden class names are removed regularly and don’t even appear on the DOM at all.

const styles = pseudoLib.create({
  red: {color: "red"},
  blue: {color: "blue"}});// Output only CSS related to blue
<div style={[styles.red, styles.blue]} >
  Always blue!
</div>

// Output only red related CSS
<div style={[styles.blue, styles.red]} >
  Always red!
</div>
Copy the code

Note: Only the most strict atomic CSS libraries can achieve this predictable behavior.

If there are multiple CSS rules in a class and only one of them is overridden, then the CSS-in-JS library has no way to filter them, which is one of the advantages of atomic CSS.

If a class only has a simple CSS rule, such as Margin: 0, but covers marginTop: 10. Shorthand syntax like margin: 0 is extended to four different atomic classes, making it easier for the library to filter out class names that don’t appear on the DOM.

Still like Tailwind?

As long as you are familiar with all of the Tailwind naming conventions, you should be able to write the UI very efficiently. Once you’re familiar with this setting, it’s hard to go back to writing every CSS rule by hand, just like you did with CSS-in-JS.

Nothing will stop you from building your own abstract CSS rules on top of the atomic CSS-in-JS framework, and softing-System will be able to accomplish something similar in the CSS-in-JS library. It creates atomic rules based on conventions. Try using it in emotion:

import styled from '@emotion/styled';
import { typography, space, color } from 'styled-system';

const Box = styled('div')(typography, space, color);
Copy the code

Equivalent to:

<Box
  fontSize={4}
  fontWeight="bold"
  p={3}
  mb={[4.5]}
  color="white"
  bg="primary"
>
  Hello
</Box>
Copy the code

It’s even possible to reuse some of the Tailwind naming conventions in JS, if you like.

Let’s start with some Tailwind code:

<div className="absolute inset-0 p-4 bg-blue-500" />
Copy the code

Let’s find a random solution on Google, like I just found react-native Web-tailwind CSS:

import { t } from 'react-native-tailwindcss';

<View style={[t.absolute, t.inset0.t.p4.t.bgBlue500]} / >;
Copy the code

In terms of productivity, it’s not that different. You can even use TS to avoid typos.

conclusion

That’s all I have to say about atomic CSS-in-JS.

I have never used atomic CSS, atomic CSS-in-JS, or Tailwind in any large production deployment. I may be wrong in some respects, please feel free to correct me.

I think atomic CSS-in-JS is a very noteworthy trend in the React ecosystem, and I hope you learned something useful from this article.

Thanks for reading.

Thank you

This article was first published on the public account “front-end from advanced to hospital”, welcome to follow.

Bytedance is hiring. Next year, more than a dozen HCS will be added to our group alone. We are really short of people. Beijing, Guangzhou, Shenzhen and Hangzhou, any time you can interview, I will help you do a full range of internal services! Welcome to contact me through the message, the public number 😁.