CSS-in-JS, One Step Forwards, One Step Sideways, or One Step Back
We have had an evolution in CSS over the past few years with the advent of CSS-in-JS. To many it has felt like a breath of fresh air, and the next logical step after JSX brought HTML into JS. But now that it has been popular for awhile, maybe the honeymoon phase is over and we can take a critical look at the benefits and downsides.
Colocation, all-for-one, one-for-all. HTML has already been brought to JS through JSX. Why not bring CSS there as well? One component and file can contain all necessary markup, presentation, and logic.
Faster build times. Sass compilation is slow. And so are tools that need a pre- or post-processing step applied to CSS. CSS-in-JS hooks into all the existing JS build tooling that allows for fast refreshing in the browser to quickly iterate.
Build tooling. Many tools have arose around CSS-in-JS that perform linting, static analysis, TypeScript integration, deduplication, atomization -- the list grows each day.
Eliminates the Cascade. Rather than needing to adhere to CSS methodologies like BEM, CSS-in-JS takes care of providing unique class names.
JS-powered theming. Ability to create and switch themes using JS.
Performance Hit. No matter which way you slice it, CSS created through JS cannot be faster than a plain old CSS stylesheet. Work has been done to statically extract the CSS out at build time, projects like CallStack's Linaria, and Facebook's stylex, but the most popular CSS-in-JS libraries, emotion and styled-components do not support this.
Developer ramp-up time. CSS-in-JS is a paradigm shift, and costs time to get a team up to speed on how to use it. Each framework has different APIs as well so knowledge of one is not directly transferable to another, contributing to JS Fatigue.
A Step Back
Of all the downsides of CSS-in-JS, most can be resolved, but portability remains a large issue. CSS is completely portable, it is agnostic to the tech stack you work with. A design system built with CSS and served over a CDN can be imported with any tech stack your company wants to use. We brought CSS in because we brought HTML in and that worked like a charm. One key difference between HTML-in-JS and CSS-in-JS though, is that HTML is not meant to be portable. You do not create a design system around a set of HTML structures. HTML is simply the markup and will be unique to each site you have. CSS though, is universal. Your company has a brand, a feeling they want to evoke, a recognition they want to build with the customer. You want all your sites to have that same look and feel, and letting your CSS be portable is the best way to achieve that.
Is CSS Really That Bad?
Of all the upsides of CSS-in-JS, most are negligible with modern CSS. CSS modules provide scoping to eliminate the cascade. CSS modules allow you to colocate your styles with your component and import them for use in JSX files. CSS modules interop with all the JS build tooling to get fast build times. Build tooling improvements have not been isolated to CSS-in-JS, linters and dead-code elimination exist for regular CSS files too. Libraries like Jed Watson's classnames provide good enough ergonomics to conditionally compose styles. CSS Variables provide the ability to do theming with better performance than CSS-in-JS. We find ourselves in a position where CSS-in-JS feels much more like a step sideways, or a step backwards because it locks us in and we lose portability.
Where We Go From Here
Does this mean we should abandon CSS-in-JS? No, it has a place. Certainly Facebook is making a case for how well it has helped their FB5 relaunch perform. But, that doesn't mean it is the solution for every company, and every team. I implore you to consider the tradeoffs when choosing to go with CSS-in-JS. Lack of portability, need for training, performance impact, lack of discipline, if you value the ergonomics it provides above all else, go ahead. I have loved using CSS-in-JS as a developer, but I also really don't mind modern CSS that much.