CSS in JavaScript with JSS and React
Style scalable web applications is hard. Though CSS with its many tools provide a variety of solutions to implement designs, there are still problems when projects get more complex or many people start to share the same codebase. Well, some clever ideas have emerged to fix these issues.
CSS in JavaScript is kind of disrupting and many people from the community see it as an unappealing idea when CSS just works. We are going to review what are the problems with CSS at scale, some enhancing tools to solve some of the problems, what is this new wave with the implementation JSS with React, its advantages and a couple of cool tools to use in your projects.
CSS at Scale
CSS came to be with the intent to decouple the presentation from the content of a website more than two decades ago. Back then the pages were mostly static and the web was not so popular so it wasn’t used that much. Using simple selectors and a few properties were enough to set the designs on the websites.
Nowadays we have web applications with complex user interactions evolving every day. The need to properly maintain the state and give immersive experiences to the users have made the work on web development more interesting and challenging.
When a project starts to grow large, CSS doesn’t allow us to perform so well and problems start to arise. Let’s review some of them.
Modularity
This is probably the most troublesome. All CSS selectors (the API) are global when in most programming languages modifying the global environment is a bad idea. That brings some issues regarding modularity and encapsulation.
.button {
border: 1px solid cyan;
background-color: black;
color: cyan;
font-size: 1rem;
}
Wherever the class button
is used it’s gonna take the global styles. That word is now reversed. Anyone new in the project should know about all components before touching them. Overusing selectors comes to play because sometimes you can’t just refactor. So it’s normal seeing deep nesting to solve conflicts.
.accordion .container .button {
color: blue;
font-size: 0.875rem;
}
And you may even find weird bugs with your styles without any warning because there is another component using your selectors. If you nest components, some selectors may filter and cause you some predicaments.
Theming
Now if you want to change the look of one component dynamically you should create other classes modifying the base class or apply the styles directly to the HTML elements. Using SASS would be like:
$theme-primary: cyan;
$theme-secondary: blue;
$theme-tertiary: orange;.button {
border: 1px solid $theme-primary;
background-color: white;
color: $theme-primary;
font-size: 1rem;
} .button--secondary {
color: $theme-secondary;
border-color: $theme-secondary;
}.button--tertiary {
color: $theme-tertiary;
border-color: $theme-tertiary;
}
This works good with many cases, but if you want to change the secondary color throughout the application, add a new theme or simply get one of the variables values on runtime in JavaScript, you will have to make some tricks.
// e.g. dirty change for the buttons secondary
// color on runtime
jQuery('.button--secondary').style({
color: 'white',
background: 'red'
});
Publishing
Suppose you want to publish your button as a package. Using the pre-compiled CSS will leave you without options to customize the button and no warnings or error messages will appear if any occurs for most scenarios.
<link rel="stylesheet" href="https://example.com/button.css" />
Using NPM, the best way to share your front-end code will tie your project to a preprocessor or building tool. If your package components depend on other packages, there is not a proper way to set dependencies. And because of lack of module system, your component may conflict with what you already have.
There are also other minor problems you may face depending on the project requirements.
CSS Enhancing Tools
So, what are the current solutions to the problems we now have?
The CSS community is big. There are a lot of tools and strategies built around it to solve many problems of CSS at scale. But most of them fix partial problems which are not complete for the majority of complications that teams face every day.
Let’s take a look at some of them and try to evaluate some advantages and disadvantages when developing with them.
Web Components and Shadow DOM
Web Components are web platform features to modularize and encapsulate components markup, styles and functionality. Since it’s native to the platform it would have a lot of advantages. But for the functionality, using the DOM directly to manage the component view makes it hard for complex applications.
Also, wide support for Shadow DOM is still in progress. Though some libraries/frameworks like Polymer and Angular implement it.
Methodologies
To mitigate issues, improve the maintenance and ideally make the project more scalable, some conventions and methodologies are recommended. Probably the most used are BEM, OOCSS, and SMACSS.
I like and use BEM the most. But the purpose is to define a better way to architecture selectors which have to be followed strictly. Just that.
Build Tools
To deploy CSS you would like to compress/minify the code generated for the end users. Or ensure a wider support prefixing some properties. To accomplish these and more you would have to use build tools. Some of these are provided by some preprocessors.
But you have to set up some packages and add more processes to your workflow. Sometimes making it slower and prone to errors. Check out this example Gulp file to configure SASS for a simple website. Or take a look at how to configure SASS with Webpack to extract the CSS.
Preprocessors
A CSS preprocessor adds spices and sauces to the styling dish. SASS, Less, and Stylus are some of the most famous ones. This solution is one of the most widely used in the industry. But have some caveats.
Except for CSS Modules, preprocessors don’t solve the global namespacing problem. To eat a banana you would have to import the gorilla and the whole jungle. And because the code is compiled, in runtime dynamic styling is not supported and styles can’t be made according to the browser. Of course, this has its trade-offs.
Personally, I have seen preprocessors make you write more nested selectors to solve problems. This practice is not recommended.
CSS Modules
CSS Modules are probably one of the most complete. It solves the global namespacing problem! It gives the ability to modularize and encapsulate the styles. Also adds the ability to declare the dependencies better. And composing styles is pretty good.
But CSS Modules removes many features you would find in a preprocessor by default and leave them as plugins, which feels kind of limited. Still, problems persist with dynamic theming, dead code elimination, and it doesn’t really propose a good architectural pattern to scale. But it is a higher level in the styling abstraction worth using.
CSS in JavaScript
Well, so that brings us to the next level. Styling the web from JavaScript! Let’s see what is all the fuss about.
For the past few years, we have seen the rise of CSS in JavaScript implementations (Styled Components, Glamorous, Aphrodite… and many more) with their disrupting way to define styles for web apps. But after a closer look, the simple ideas behind this trend seem to be really appealing.
When I was learning React, one of the things that at first didn’t like was the JSX notation to declare components. It was like coming back to the past to where DHTML was trending, images were used to draw rounded borders and tables were the grid system. One of the ideas was to move the “HTML to JavaScript”. With that, you could create components in a declarative way and give the full potential of JavaScript to generate the markup.
After trying those ideas in a real project I was really amazed. The original idea of the web to separate the content, the presentation and the logic now seemed a sort of the wrong conception. As Addy Osmani would say, good software components should be focused, independent, reusable, small and testable (FIRST). So it makes sense to have a component with its representational logic and content in the same place (which is not the same file necessarily). And with the same language, that makes it easier to scale complex applications since we are using universal tools in the same context.
So now we have the full potential of JavaScript to generate the styles with CSS in JS solutions! The extensibility, variables, and tools around JavaScript are now at our disposition in CSS.
The concept of generating styles from JavaScript is kind of open and opinionated so today we find plenty of implementations with the idea in mind. But it sets the basement to solve most problems of CSS at scale.
JSS with React
After researching about some of the implementations or libraries, I picked JSS, in my humble opinion, one of the best, because how it implements good architectural patterns and feels more simple and powerful. Let’s check how the library works and how it plays with React.
As any of the other solutions, JSS generates CSS. The inputs are objects (or simple template strings) with keys as the class names and the outputs are the generated CSS and objects defining the generated scoped class names. Using JSS core (which is framework agnostic) would be like:
import jss from 'jss';
import preset from 'jss-preset-default';jss.setup(preset());const styles = {
button: {
background: 'red',
color: 'white',
fontSize: 12
},
ctaButton: {
composes: '$button',
background: 'blue',
fontWeight: 'bold'
}
};const { classes } = jss.createStyleSheet(styles).attach();document.body.innerHTML = `
<button class="${classes.button}">Button</button>
<button class="${classes.ctaButton}">CTA Button</button>
`;
Since they are objects you can do pretty much anything you can do in JavaScript. Isn’t that awesome? Check the full JavaScript API.
Using JSS with React components would let you optionally add a theme provider, ideally one for the whole application, where you can configure the styling of your components and change it in runtime. And for the components to use the theme and generate its styles you can use the react-jss HOC (Hich Order Component) to inject the component styles in the DOM once the component is mounted and remove the injected styles once the component is unmounted.
Let’s create a better version of the button but with a different approach. Suppose we have the application entry point file and our button file component.
// index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import { ThemeProvider } from 'react-jss';
import Button from './Button';const mainTheme = {
colorPrimary: 'blue',
colorActive: 'red'
};const App = () => (
<ThemeProvider theme={mainTheme}>
<div>
<Button>Button</Button>
<Button active>Active Button</Button>
</div>
</ThemeProvider>
)ReactDOM.render(<App />, document.querySelector('#root'));
The main component provides a theme to the whole application and children components may use it.
// Button.jsimport React from 'react';
import withStyles from 'react-jss';
import cx from 'classnames';const styles = theme => ({
root: {
background: theme.colorPrimary,
color: 'white',
fontSize: 16
},
active: {
background: theme.colorActive,
fontWeight: 'bold'
}
});class Button extends React.Component {
render () {
const {
classes, theme, // Coming from the HOC
className, children, active,
...etc
} = this.props;
const cls = cx(
classes.root,
active && classes.active,
className
);
return (
<button className={cls} {...etc}>
{children}
</button>
);
}
}export default withStyles(styles)(Button);
Simple and powerful. In this case, the button uses the theme colors provided and generates two classes based on them. We use the classnames
package to handle many classes. In the future, JSS may not need that utility and we may simply use functions values or function rules for them.
Now let’s check the points we are studying and how CSS in JS solutions, specially JSS in conjunction with React manage them.
Modularity
The API is pretty simple to follow and encapsulates the component styles in a simple way so it is decoupled from other styles. While developing, the classes names are easy to figure out and each style block is added to its own style
element. Global namespacing is not really an issue. It also encourages to write simple classes and avoid nesting. And because you are dealing with objects and functions you can even test your styles!
Dependencies
Extending and composing classes are now trivial since we are dealing with objects and maybe functions.
If you are working with React components, the dependencies are managed implicitly by the same React architecture so you don’t have to think about it.
Dead code elimination
react-jss attach (add to the DOM) the styles when you need to render the elements with the classes and remove them when the elements are unmounted. In React when we “render” the components (not when we import them) the styles are attached. And when they are unmounted the styles are unattached. The same applies to their dependencies.
Optimizations
In production, the names and the resulting code are minified. The JSS default preset adds prefixes according to the browser in runtime so no build tool is needed to work on this. And if anybody creates a plugin to add more optimizations, you just have to install it and it just works.
Theming and data-driven styling
Here it gets more interesting. Since you have a theme provider, all children may use the incoming theme and define styles to fit their needs.
If you have ever had the need to set styles in a component according to the component props or some other parameters, then this is for you. Let’s update the button component a little bit.
const styles = theme => ({
root: {
color: 'white',
fontSize: 16,
background: props => props.active
? theme.colorActive
: theme.colorPrimary,
fontWeight: props => props.active && 'bold'
}
});class Button extends React.Component {
render () {
const {
classes, theme,
className, children, active,
...etc
} = this.props;
const cls = cx(classes.root, className);
return (
<button className={cls} {...etc}>
{children}
</button>
);
}
}
Now you have functions to define the style properties of the selectors, and even for your entire selectors. This is extremely useful when you have complex designs to implement on components. You can even add observables. Check out the core JSON syntax.
You can even access the theme settings as a component property so you can make your styling work outside the styles too.
Publishing
Because there is no build tool needed, it is just as simple as require the React component and place it wherever you need it and it just works! With all the benefits included. Now you have a JavaScript API and your styles have access to it. You can import only what you need and it would be added to the DOM only when used.
Server side rendering
If you are wondering about this feature, it is completely supported. Some may say because you are generating the CSS in runtime and you need to download, parse and run the JavaScript before seeing something on the screen with design, well, you can generate it in the server and it would be a lot faster.
Because only the critical parts of CSS are used in a page, you don’t need to send the whole bundle with the three hundred components styles of your app. Only the styles used are generated and sent. And if you are not using React, JSS is framework agnostic so you can implement your own system.
Wow! All those features are pretty neat.
Check out the JSS playground and put it in practice with React in CodeSandbox.
UI Frameworks
Are you excited too? To build a city like in the cover we would need robust building blocks. Here there are two frameworks to build web applications with the CSS in JavaScript implementation JSS.
Material UI — React components that implement Google’s Material Design
Material UI v3 with about 40.3k stars and more than 932 contributors make the package a well experienced and robust solution for your applications. Using JSS underneath with incredibly simple and powerful APIs to enhance the components and create your own you will be ready to implement awesome user interfaces based on the Material Design specification.
Arwes — Sci-Fi UI Framework
Arwes is a web framework to build user interfaces for web applications based on science fiction and cyberpunk styles guidelines, animations and sounds effects. JSS simplifies a lot the heavy lifting of complex styles and animations. The project is still young but it lets users create pretty outstanding futuristic experiences brought out of movies.
Keep Reading
You can review more examples of JSS and check the CSSinJS solutions comparison and the CSSinJS awesome repository for more insight. Also here is a list of great resources to check out:
- JSS library with react-jss
- Material UI and Arwes
- Presentation based on this article
- A Unified Styling Language by Mark Dalgleish
- CSS in JavaScript: The future of component-based styling by Jonathan Z. White
- All You Need To Know About CSS-in-JS (based on Styled Components) by Indrek Lasn
- ReactiveConf 2017: Oleg Slobodskoi — Unique Value Proposition of CSSinJS and the Future (here the slides) by Oleg Slobodskoi (creator of JSS)
- Problems with CSS at Scale by Vjeux
- A Journey toward better style by Olivier Tassinari (Material UI dev)