Let me try to explain CSS in JS to you. It works kinda like this:
Add 2 simple rules:
- property names are camelCase: dashes in property names are removed and the letter following the dash is upper case:
- values are quoted:
Rules are converted to regular CSS rules and inserted in a style tag:
usually this is done with a utility function that creates a scoped selector for
.rule-AbY3c so that there aren’t collisions with similar selectors.
The function then returns this scoped class name eg.
rule-AbY3c so that you can set it on an HTML element:
css functions usually do a lot of things for you like automatically adding vendor prefixes or accepting a list of reusable rules and merging them into a new one!
Using a tool like CSS in JS brings in some benefits:
- You can style individual elements without affecting other elements
- You can compose
rules to avoid repetition and apply the resulting class name to an element
- You can use built-in primitives like loops, variables, and do math without having to introduce a new tool like a preprocessor
- Styles are colocated with your markup or even authored inline and then compiled away. This can help reducing context switching and can speed up development
- No inline styles! In the end it is just CSS written with another tool - as we saw above rules are inserted into an actual style tag by the library
- It can be prerendered on the server. This way your site sends only the CSS for the page that is serving right now
- You don’t need naming conventions anymore
- You don’t need to know where the styles are coming from since they are defined locally per component
What about the Cascade?
Component-based systems are great because they allow us to reuse and compose many pieces together. For example, one could create a reusable and styled paragraph component and reuse it all over the codebase.
Cascade usually is useful to avoid repetition, but with a reusable JS component we are in fact achieving a similar result: we write our styles once and then use our styled paragraph component instead of a “naked” one!
However, by scoping our CSS to the component, our styles won’t accidentally leak into some parts of our web page.
Furthermore, with CSS in JS inheritance continues to work! This means that elements keep inheriting inherited properties like
line-height from their anchestors until you override them. This is a sutble detail that many often ignore.
Who is this for?
Yes, but when I see that syntax I throw up in my mouth.
There are libraries like styled-jsx, CSS Modules, linaria, Astroturf, Vue, Svelte, lit-html that allow you to write regular CSS.
I even have an example ready for you.
I want to conclude this write-up with a reflection on team work and conventions.
I read a few times that “people should just learn CSS” rather than looking for answers (and solutions) in CSS in JS.
While I think that learning something new or master a language never hurts, I also think that many ignore the fact that we work with people (teams and organizations) with diverse backgrounds and expertise.
This is great because different expertise makes for a great end result.
In an organization where individuals and teams work on the same product it is however unrealistic to expect that only specialists get to write CSS or that everyone must be a CSS specialist.
The reason why folks move to CSS in JS has to do with letting a tool do the dirty work of enforcing some rules for everybody by making that the default. I failed too many times at trying to make conventions like BEM & friends work at org level.
At a larger scale good intents and conventions rarely work. The only reliable way in my opinion (and experience) is tooling, because skills are not measurable and are diverse.
CSS in JS is a tool!