Skip to main content

Annamalai

Right after my undergraduate degree, I got the essence of working in enterprise-level applications in my first job and I worked there as a frontend developer. As days passed by, I become so obsessed about CSS architecture at one point of time, when I started to notice that the styles that I write were just kept on appending and were very difficult to delete styles that were not used anymore. The CSS scalability graph shown below is so true and I have seen it happen in real projects.

CSS scalability

Fast forward a few years, we were introduced to a new solution called CSS-in-JS to maintain the CSS and not worry much about separation of concerns. I always believed in the ideology and promises of what CSS-in-JS library brought to the table. Many libraries started out to roll out like Radium, Aphrodite, Glamor, Styled-Components, Emotion, Fela, etc., but I was still not impressed with it because almost all of them were runtime based and adds unnecessary complexity to a lot of apps. I never used any of these libraries in any real production-grade apps because I always wanted something in which could possibly extract CSS into static files at build time. One such library which started this trend is Linaria which most of you all might be familiar with it. Yes, in this post, I going to list a few potential but unknown CSS-in-JS frameworks which embrace static extraction/build time optimizations. Of these some are open-source, some are under development but this is will just to create awareness of how it is really important and the ideas behind it. Bonus points, if the library could produce Atomic CSS.

Stylex

A CSS-in-JS library developed inhouse at Facebook and there are already talks about it on how it works by their developers at multiple conferences. Out of curiosity, I started fiddling with the Facebook Workplace code to find out how it works and was find some details. It has an API which is very similar to that of React Native stylesheet and expects the styles to be coded as a JavaScript objects. For example, it might look like,

const styles = stylex.create({
root: {
background: "blue",
color: "white",
paddingLeft: 10,
paddingRight: 10,
height: 40
},
danger: {
background: "red"
}
});

At build time using babel, the stylex definitions are identified and converted into POJOs (plain old javascript objects) to something similar as below

const styles = {
root: {
background: "df32dws",
color: "hj3sd2s",
paddingLeft: "zsd3423",
paddingRight: "ojsd423",
height: "vdr4323"
},
danger: {
background: "dfre32"
}
};

So what happens is each CSS property is converted into atomic ones and their class names are replaced in the place of the CSS property value. Now it can be used in components like something below

<button className={stylex(styles.root, styles.danger)} />

To resolve the styles, the stylex does simple Object.assign and it works like

let classes = Object.assign(
{},
{
bacground: "df32dws",
color: "hj3sd2s",
paddingLeft: "zsd3423",
paddingRight: "ojsd423",
height: "vdr4323"
},
{
background: "dfre32"
}
);

classes = {
bacground: "dfre32",
color: "hj3sd2s",
paddingLeft: "zsd3423",
paddingRight: "ojsd423",
height: "vdr4323"
};

classes = ["dfre32", "hj3sd2s", "zsd3423", "ojsd423", "vdr4323"].join(" ");

Due to this simple assignment, there can be no clashes of same multiple properties,i.e there can be only one one final background property in the object. It does have runtime minimal implementation to insert truly dynamic ones like height, width, user-based value etc.,

One interesting thing is that the majority of the UI components takes in a xstyle prop which is given more importance. It works similar to something like

let variants = stylex.create({
success: {
background: 'green'
}
})

<Button xstyle={variants.success}

To me, the xstyle prop is good method because it helps to props under one namespace and doesn't populate the global props environment and it looks quite similar to the goal of sx prop in theme-ui that helps a quick hatch to override the originial implementation. Though, this library is not open-source, but mentioning here might bring new ideas to the frontend development community. There are hopes that this library might be open sourced one day. For more details, you can watch this video.

Style-Sheet

This library developed by Giuseppe is one of the only stable CSS-in-JS that works similar to Stylex. Yes, this library style-sheet and stylex can be said as siblings. This library has similar and ideologies followed is more or less the same. It is authored in JS, then babel plugin is used to extract the styles into a separate sheet. Some of the unique features of this library are that RTL styles can easily be generated and it is a part of the API, it makes sure the styles are inserted in the correct order to avoid UI issues and the order can be seen in this file. The library imposes to include its runtime implementation to handle dynamic values but it is really not necessary to include it part of the bundle. An example style is given below

const styles = StyleSheet.create({
root: {
color: "red"
},
success: {
color: "green"
}
});

const className = StyleResolver.resolve([styles.root, styles.success]);

One of the main differences is how it resolves styles to generate the class name and let us discuss how it is done. First, the styles are replaced with POJOs.

const styles = {
root: "sddew_sdew",
success: "dew4sd_dewwe"
};

So what;'s happening here is, for example in this string 'sddew_sdew', 'sddew' is the hash of CSS property and the descenant selector and 'sdew' is the hash of CSS property value. Now in the resolution process , this is how it works

const class = StyleResolver.resolve(["sddew_sdew", "dew4sd_dewwe"]);

const class = Object.assign({}, { sddew: "sdew" }, { sddew: "sd3esd" });
//will result
const class = { sddew: "sd3esd" };

To me, this approach is quite novel and takes less space when thinking at a large application scale. If you are looking for an open-source library which is similar to stylex, then this is the go-to choice.

compiled-css-in-js

Yes, it is the name of a CSS-in-JS library being developed actively by the Atlassian folks. It aims to give a styled-components/emotion developer experience, zero config SSR of emotion but with the full support of static extraction as seen in Linaria. It has taken inspiration from these libraries and instead of babel, it uses a typescript transformer and

has following the progress of the library. It is really shaping very well and I must really say that developers who are currently using styled-components/Emotion can benefit from this library without any major changes (because Atlassian being one of the biggest users of Styled-components they surely try to maintain the same API ). This library doesn't produce atomic CSS and it supports dynamic values by taking advantage of the CSS Variables feature. One thing I noticed is that, since they are using typescript-first approach they might face some hurdles. The typescript transformer story is not that great as seen in babel and at one point they might have to implement their own typescript plugins like babel-plugin-helper-evaluate-path and linaria-complex-evaluate to resolve logical expressions. For example, linaria allows us to use "polished" library at zero cost because these above two plugins help to compute most of the values at build time.

import { jsx } from "@compiled/css-in-js";

const App = () => <div css={​​​​​{ color: "blue" }​​​​​​}>hello, world!</div>;
// Into:
const App = () => (
<>
<style>{'.a { color: blue; }'}</style>
<div className="a">hello, world!</div>
</>
);

Treat

Treat is an open-source CSS-in-JS library developed by Seek folks. It statically extracts CSS‑in‑JS with Themeability support. The styles are authored in a separate .treat.js files and you can utility libraries like lodash, polished etc., at zero cost . These .treat.js files statically extracted into .css files at build time. I did give it a try and is very stable enough among others listed in this list here. The library has additional styling API like styleMap and styleTree which are really useful in certain cases like when components needs to have multiple variants. The library doesn't produce atomic css but can be overcome by following the Box component approach. A Box component approach is a standard component which accepts style props and based on that class names are added to it. For examples, you can check code of Box component approach of Seek Jobs and Pinterest (here, they use CSS-Modules).

// Button.treat.js
import { style } from "treat";

export const button = style({
backgroundColor: "blue",
height: 48
});

// Button.js
import * as styles from "./Button.treat.js";

export const Button = props => <button className={styles.button}>{props.children}</button>;

css-zero

This library is relatively new and being developed by Craig Cavalier. It aims to achieve the functionalities similar to stylex but the DX part similar to Emotion/styled-components. I did give it a try to know how it works and also know the stability of the project. As I said, it is being developed and it requires some work in its babel plugin on intelligently to replace the logical expressions. Though this is not production-ready, it is better to keep an eye on it.

import { css, styles } from "css-zero";

// Write your styles using the `css` tag
const blue = css`
color: blue;
`
;

const base = css`
color: red;
font-size: 16px;
`
;

export default props => <div className={styles(base, props.isBlue && blue)} />;

Compiles to

export default props => <div className={(props.isBlue ? "x1vong5g" : "x1dqz7z3") + " " + "x1e4w2a9"} />

// along with a the following .zero.css file:
.x1vong5g {color:blue}
.x1dqz7z3 {color:red}
.x1e4w2a9 {font-size:16px}

Conclusion

Each library has its own benefits in terms of performance or developer experience or bundle size etc., It is really up to the team to choose which library they need wisely according to their need. I have been doing this research for months and been reading a lot of code from the open-source ones and I have planned to write my own CSS-in-JS library by taking the ideas from the existing libraries and also take advantage existing tools like PostCSS to bring additional benefits. I have also planned to write a blog post on how to build your own CSS-in-JS library.

Thank you, reader!