CSS-in-JS – a synopsis
A very controversial topic, but we’ve been here before when the JS community had to get used with JSX which is mostly HTML markup in JS, without the use of templates. There has been a lot of back and forth about these topics, but eventually the community started adopting this paradigm change.
A lot of the old school developers will invoke the fact that without having files for every language, we will not have a clear separation of concerns.
We live in the era of web components, it would be nearly impossible to manage creating a 3 files for each component, without making mistakes, either adding a typo at a import or even a classname. But if we try and isolate components and only interact with them only via a predefined API, we would not need to worry about opening templates, not css files to understand how that component can be customized. But I digress… Going back to the original idea, separation of concerns doesn’t mean separation of files, in the end everything is connected: the CSS classes we are using inside the HTML template, the data we send to that template via the JS layer. There are decisions on how component will look based on the data, so the gradient JS-HTML-CSS starts to come of as a good idea.
If one CSS class is missing that would impact the way a component will look it might be hard to catch, without proper testing (a style-guide would help) and code review.
Even with a style-guide, because we can overwrite definitions by increasing specificity, we have no guarantee that would be indeed the outcome in our application. But this is not even the biggest gotcha (I’m trying not to use the word “problem” here) with CSS, because in small projects we can get away with using CSS, without even a preprocessor. We are talking here about problems when using CSS at an enterprise scale. These problems have been perfectly outlined in a 2014 presentation made by Christopher “Vjeux” Chedeau. Here are the key things we are trying to avoid when writing CSS at scale.
There a great article that explains each point, so I won’t go into much detail here, and only summarize them.
We know that all of the CSS classes we declare are globals. If we were to use a library like bootstrap, we’d have to make sure we don’t use or overwrite the classes they are defining.
So we are depending on this file to know what to use or what to avoid, which you may end up caching it in your brain. I was not able to do this from the start, so I always went with custom CSS every time, but I was memorizing classes I had declared, using a name convention.
And as your project grows, engineers leave and new ones are hired, but they have all added classes scattered all over the place. New engineers will have to accommodate with the set of CSS definitions the project inherits. Their first instinct is to try to refactor some of it, fail and then end up adding even more classes. Design changes sometimes end up in duplicated code and classes. This is how you might end up opening a file that has more than 3000 lines of code, and in the first ones you might find a magical “// FIXME” comment stating that this needs to be cleaned up. Nobody is that brave to jump that deep into refactoring that, unless he is crazy 🙂 or he knows what he is doing.
Since we are on the subject of file sizes there are no tools that would optimise the output of the css files, most of the solutions out there will just minimize the file. But there is so much more that can be done.
This is the idea behind Styletron one of the libraries I’ll be mentioning as alternatives to styled-components.
In 2014, variables were not a thing in CSS, not even today they are not fully supported especially on mobile devices.Not to mention that CSS can be a bit unpredictable.
The styles of an element will be determined based on the order they are defined in the file, which is the opposite of what I was expecting, which is, to be based on the order they are added to the class attribute of that DOM element.
Something that is really tricky and can’t be fully achieved, even by CSS-in-JS libraries is to completely isolate the CSS of a widget or embeddable from the clients product. You will always have to counter the CSS they have globally declared. For example their links are bold, which messes up your styles. One solution would be to overwrite that by increasing the specificity, even !imporant might be a solution if you are desperate. But what I’d recommend is to use custom elements everywhere (nobody restricts you from defining your own products elements).
Even closer to truly isolating the styles of your widget without the use of an iframe would be to use webcomponents, as long you don’t have to factor in the browser compatibility.
Now, you might think that CSS has evolved and we have a lot of CSS architectures, that can prevent most issues, but in fact what they do, is just to standardize a set of rules, which translates in a better collaboration between your peers. But if you think about it, it just prevents chaos in the projects, there are no tools that you can pair it with which will help you avoid having unused classes or imports, fix global css definitions.
Going back to the presentation from 2014, Christopher Chedeau came up with a unique way to solve these caveats, by using inline styles. The idea itself was very good, but it was a bit raw, and it’s implementation was a nightmare to use. It was built for React which at that time it became more and more popular. So, a flood of abstractions on top of the idea started to emerge.
A little bit of history
Next, some libraries have improved the API, or they had attempt to do so.
This a clear example where glamor fixes the issue with the order of classes. This API is way simpler to implement and has no caveat, but for a person switching from CSS to JS, camelcase properties are still a bit hard to get used to.
As the libraries evolved they started to catch up with the fact that CSS is not that bad, and some libraries even let you target elements using selectors as you’d normally use in preprocessor. One of the first was react-jss.
In order to accomplish this classes will be generated, but it was still hard to adopt.
So they created glamorous. It enabled a more declarative approach, where a component properties would affect the output of the look and feel. The downside was that at an implementation level it still needed a lot of work from the developer, and it was bound to React, not framework agnostic.
csjs – this library caught my attention because it could be used without being locked in the React ecosystem. It worked very similar as CSS modules and it was creating hashed class names and it was making use of the ES6 feature ‘template strings’. This was very easy for a developer that used to write CSS to switch to this.
This also enabled server side rendering with ease. It was a step forward but there were some CSS features that were missing. Some people were fond of their lobotomized owls, but this library was a bit restrictive when it came to writing more complex CSS, even so some features like SASS extends was there because the author saw its necessity.
Same author, created Styletron, that had a very interesting optimization, like I mentioned before. Each property and value combination would eventually end up in a unique class, which reduced the CSS file size drastically. The caveat is that somehow he forgot about the developer experience, and the library was still using camelcase properties, the API which I thought is a bit outdated.
It was a universal library, that had a module for react, which I can give it a big plus here, and again it’s optimization part. On the downside it was a debug nightmare to understand what style comes from where. Letter of the alphabet was not enough.
These libraries are getting better and better, each solving problems in their own way. But where am I trying to get at? Well as in every field, as part of the innovation process, there is a revolution cycle and then a refinement one.
So fast forwarding to 2016-2017… A library started to stand out from the crowd and it soon became the community standard. It’s called styled-components and it had an API that was familiar and accessible to developers that wrote CSS. This library injects the CSS at runtime.
I won’t go further into details here, but I’d like to present you with some alternatives.
Complimentary there is a new library called linaria that will extract the CSS. It’s advantage is that you can use it as a drop-in replacement for styled-components, but get the benefits from having the CSS extracted, enabling server side rendering. It’s pretty smart since it’s parsing the js code using a babel plugin and statically extracts CSS creating classes. When it encounters dynamic values it would create CSS variables for that property, which is hashed. It also has a very nice debug feature where it creates sourcemaps, which helps easily identify the code that is charge for applying those styles.
When it comes to having a fully flagged server that has server side rendering already integrated Next.js is your go to framework. It has a unique and interesting way when it comes to approaching styles, even if it’s not that popular with the community. Their library is called styled-jsx and it’s main feature is that it encapsulates the styles of a component using a namespace. Compared with styled-components the benefits is that we clearly see how the properties influence the CSS output without losing context.
Emoticon – is very similar to csjs, but it has the advantage of a better API when it comes to integrating it with React, by supporting styled-components interface and having even more preprocessor inspired features. This library is a result or at least inspired by the trial and error of previous libraries like glamor and glamorous, and it’s quite popular with the community.
From it’s history, we can see that, writing CSS-in-JS had a rough start, like JSX. But we have turned a new page and the community is starting to embrace this whole idea of having component based architecture. Deciding on what is the best solution for your project can be difficult, but I hope this article helps you, with at least, libraries to give them a try.
Libraries, old and new, start taking CSS-in-JS serious; for example version 3 of Svelte has its own approach of creating components, all bundled in one file. And it seems that community is going to give it a chance (15k stars), but we shall get into more details into a different blog post.
PS: Styletron is still on the table, they have added a way to debug the styles.