If there's one thing that divides web developers, it's styling. A part of this has to do with the different requirements of websites and web applications. What is good in another domain, is an anti-pattern in another.
To understand the topic better, I am interviewing Oleg Slobodskoi, the author of JSS.
Working on web UIs for over a decade, I have realized there are two significant challenges in frontend engineering: understanding the state and styling its representation. Unidirectional data flow has made managing state much easier, but styling components is still painful.
To improve the situation, I started JSS back in 2014 and haven't stopped learning and developing the project since. Currently, I am working at Chatgrape where we are building a sophisticated client using NLP and deep services integration. All CSS is managed using JSS. Also, I try to talk at conferences from time to time, even if I know I suck at this haha.
In general, "CSS in JS" libraries are authoring tools which allow you to generate CSS. The paradigm is similar to Sass, Less or Stylus in this regard, the difference being that the host language JavaScript is well-standardized.
JSS is a set of libraries for writing CSS in JavaScript. They address a wide spectrum of issues. The most significant features are class names scoping, critical CSS extraction, significantly improved maintenance, code reuse and sharing, theming, co-location and state-driven styles.
It is important to understand though that not every product has all of the issues that these features address, so not every developer can relate to them or even confirm that they are real. If you don't get it - don't worry, the time for you just hasn't come yet.
One general truth you could take away from this is that JSS is a more powerful abstraction over CSS, which is good and bad at the same time. Less powerful abstractions may be of benefit for less experienced developers because less can be done incorrectly, but they certainly have limitations.
The essential libraries in JSS are core, React-JSS, and Styled-JSS. Low level and library-agnostic, the core is responsible for compilation and rendering of a stylesheet.
The core is used by both React-JSS and Styled-JSS internally. React-JSS is a higher-order component providing an interface for React. Styled-JSS is an alternative interface for React which implements the styled primitives factory.
Styled primitive or styled component is a component which has initial styles applied when created. There is no need to provide class names when you use it. It has been very actively promoted by the Styled Components library and is worth looking into as an alternative to other interfaces. Our implementation, in fact, combines both styled primitives and a classes map in one solid interface.
The general process goes like this:
.attach
method, styles are compiled to a CSS string and injected into the DOM using a style
element.Example using the low level core library
import jss from "jss";
import preset from "jss-preset-default";
// One-time setup.
jss.setup(preset());
const styles = {
button: {
color: "red",
},
};
// Compile and render the styles.
const { classes } = jss.createStyleSheet(styles).attach();
document.body.innerHTML = `
<button class="${classes.button}">
My Button
</button>
`;
Example using React-JSS
import injectSheet from "react-jss";
const styles = {
button: {
color: "red",
},
};
const Button = ({ classes }) => (
<button className={classes.button}>My Button</button>
);
// Function injectSheet generates a HOC, which uses JSS and passes `classes` to the `Button`.
const StyledButton = injectSheet(styles)(Button);
Example using Styled-JSS
import styled from "styled-jss";
// Produces a button which has the styles already applied.
const MyButton = styled("button")({
color: "red",
});
There are too many differences to name them all. To name a few:
JSS is a set of libraries, each designed to solve a specific set of tasks strongly decoupled from each other. As a result, the user enjoys greater flexibility and cleaner abstractions. For example, the core is not coupled to React, which means it can be used with any framework.
The plugin API allows you to manipulate sheets, rules and react on updates. In fact, most features are implemented internally as plugins as well.
Focus on performance has always been of the highest importance. JSS is one of the most performant libraries available. That said, it is hard to compare accurately because some features and implementation details differ a lot between libraries. We benchmark every possible small detail, and we track regressions for each change.
Function values are now widely supported by other CSS in JS libraries. However, JSS differs in that it allows for high-performance JavaScript controlled animations like in the function value example.
It is possible because JSS doesn't generate new CSS rules for each animation step. It is updating CSS values, the same way it would be done using inline styles. I wrote an article to give you more implementation details.
The main problem with auto-generated class names is that they need to be deterministic. In case you generate HTML and CSS from the server and then want to update both at runtime dynamically, you need to make sure the class names generated at runtime will always match those on the server.
To solve these most libraries use hashes, though they have limitations:
JSS does not use any inline styles. Inline styles are slow if you overuse them. They are particularly slow in React.
It is funny because initially I just wanted to use JavaScript as a language to describe styles because I didn't want to learn Sass. Secondly, I didn't want to think how to name my classes in the global scope, because enforcing BEM is hard.
Also, I wanted to eliminate the fear of changing any CSS and breaking unexpected things. Now it has become way more than that, but to put it in one sentence: it is the right abstraction for my tasks, and I enjoy using it.
The foremost focus is on making the DX better: better documentation, auto-completion, syntax highlighting, React Native integration, a better CLI tool. The team has done a lot in the past, but a significant amount of work is still ahead of us, and we need highly skilled, motivated contributors to tackle all the challenges.
I am trying to establish a distributed team of people responsible for different parts of this story. To give you an idea, consider the following contributions:
I would love to continue this with more people on board with more dedication. I am seeing all the time how much they struggle to find time to work on it. For this reason, we recently started open source sponsor initiative to shape our industry.
One problem all CSS solutions have in common but that is especially problematic for CSS in JS is the lack of interoperability between the libraries. All CSS in JS solutions use a slightly different DSL to express the styles, which means that the styles are tightly coupled with the library which can parse them.
The big picture looks quite bad right now. Upon installation any package from npm which uses any CSS in JS library different than what's used in the project already, one more library will be installed. Given the fact that currently there are 5-10 well-known CSS in JS solutions, the chances are good that your build will contain all of them at some point.
To solve this, we started to work on the ISTF (Interoperable Styling Transfer Format) standard. The specification describes a CSS notation designed for high-performance parsing and will serve as an intermediate format for publishing. It is a layer between the consumer library and the authoring library/tool.
Publishers will be able to transpile styles to this format before publishing a package to npm similar to what we do with Babel for ES6. Consumer libraries will then be able to use this format to render CSS most efficiently.
I think this format is the future not only for all CSS in JS libraries but also for well-established languages like Sass. For the end-user, it means that they will be able to use any interface with any syntactic sugar they like to produce CSS, and the result can still be processed by just one library of their choice implementing ISTF, no matter whether it's on the server or the client.
To those who prefer static CSS, don't worry, this case is on top of our priorities. We are not going to force you to generate CSS at runtime.
Take open source seriously. I learned 90% of what I know about computers and programming from it. Also, it is the best way to share the knowledge and become a better engineer and ultimately a better person. I am still learning and trying to become better. It is a lifelong process, so it is important to choose the way we do it wisely.
Editor's note: I interviewed Artem earlier about Styleguidist.
Thanks for the interview Oleg! I share your sense of design when it comes to plugin systems. Composition seems like a strong way to solve a lot of problems even if you get certain news in return.
You can learn more about JSS in GitHub and the official site of JSS.