Newer Post

Free eBook on how to Retain Great Developers

Older Post

How To Become an AWS Certified Solutions Architect

Css-Modules: Why This Is the Most Significant Improvement to CSS in Years

If you’ve ever worked on software that uses global variables you’ll know why they are avoided.

And as a codebase becomes larger and more complex you’ll observe two things: first you will feel a growing desire to make these globals get away, and second you’ll find this an unexpectedly difficult task.

The reason: ease of maintenance is very closely tied to how dependencies are structured in your code. Globals quickly lead to dependencies out of control.

Imagine a cleanly structured dependency tree where each component has a simply defined relationship with its neighbours. Now imagine John Nash’s wall from A Beautiful Mind. At the point which your code starts looking more like a conspiracy theory than reasonable software it’s probably already too late for you. Torch it and start again.

Except that if we’re talking about css, you don’t really have that option – all css selectors live in global scope, like it or not.

However we are not without hope.  A few months ago I met some people who were working on (what I believe to be) the most significant improvement to css in a long time.

The end of global css is here! With css-modules, we have a way to emulate local scope and control dependencies in front-end components. You won’t need to learn a new language either. For the most part you’ll just be writing plain old css. But this time it works like it should.

An example

Consider a simple component and how we would write its accompanying css:

const html = `  
  <div class="mediaItem">
    <img class="image" src="${data.image}" />
    <p class="description">${data.description}</p>
  </div>
`;

We’re templating a “media item” element that combines an image with a description. It’s easy to write css for this element because we just need to target the .image and the .description and our presentation is then reasonably decoupled from the html structure. This approach is all well and good while we’re only talking about one element, but trouble starts when something else on the page wants to define .image or .description too. All selectors are global so we need to do something to prevent the styles colliding.

Over the years there have been many good attempts to solve this problem. For example we could specify ancestry in our css selectors, like .mediaItem > .image. This solves one problem but leaves us with others due to the tighter coupling between structure and presentation.

Another approach is to follow a naming convention like BEM, so instead of .image we have .mediaItem__image. The benefit here is that even though all selectors are global they are scoped by prefixing a unique component name. But doing this manually is fairly tedious, repetitive and prone to human error. Didn’t we invent machines to do that stuff for us? That’s where css-modules comes in.

The css-modules way

The old saying is that every problem in computers can be solved by adding one more layer of abstraction. The beautiful thing about css-modules is that the layer added is so wafer thin you’ll barely even notice it. All the difference is made by a handful of small yet powerful concepts introduced to the language.

To see how it works let’s revisit the original example and use css-modules this time. We’ll start by looking at the css file for our component (you can also jump ahead and view the example repo if you like):

/** media-item.css **/

.root {
  box-shadow: -2px 2px 20px black;
  padding: 10px;
}

.image {
  margin-bottom: 15px;
}

.description {
  font-style: italic;
}

When css-modules picks this up it will automatically prefix each class selector making it globally unique. To apply these classes in our template we import (or require) it into a js file.

Note: you need a build step for this part since most browsers don’t support import / require natively yet. There are css-modules implementations for webpack, browserify and jspm to handle this part.

import styles from './media-item.css'  

Behind the scenes, css-modules has exported an object that is a lookup of classnames to their locally-scoped counterpart. We can use this object to apply the correct classname to our template:

const html = `  
  <div class="${styles.root}">
    <img class="${styles.image}" src="${data.image}" />
    <p class="${styles.description}">${data.description}</p>
  </div>
`;

Now if you observe the rendered html you can see how the end result is still recognisably similar to the code we wrote, yet we have avoided the possibility of an accidental name collision.

<div class="_mediaItem__root">  
  <img class="_mediaItem__image" src="foo.jpg" />
  <p class="_mediaItem__description">Ceci n'est pas une foo</p>
</div>  

Composition

There are some other cool features of css-modules and a very useful one is the composes property. This gives us a way to build shareable styles and reuse them in a controlled manner.

Let’s say your boss loves drop shadows and wants them to be consistently applied to multiple components in the page. You could define that in a common theme file:

.shadow {
  box-shadow: -2px 2px 20px black;
}

Then you can compose the .shadow class into other rules. Let’s rewrite our media item to remove duplication:

.root {
  composes: shadow from "./theme.css";
  padding: 10px;
}

A really nice thing is that when this gets rendered to html the image element will have 2 classnames: <div class="_mediaItem__root _theme__shadow">. This makes it easier to inspect and trace back where an element’s styles came from.

Conclusion

The thing that appeals to me the most about css-modules is how it respects the underlying language and doesn’t try to bend it too far. For this reason it’s also easy to incorporate into a wide variety of applications. In the examples here we’ve used old fashioned bung-it-in-a-string templates but you can use it in practically any templating system you like. It works quite well in jsx with react too.

Things have changed in css land. Finally we can enjoy the simplicity of plain css knowing that classnames are scoped to their own module, and we can compose styles from 3rd-party sources without any danger of conflict. With these improved abilities I’m looking forward to seeing new and useful ways of publishing themeable components. Check out Mark Dalgleish’s adventures in react-themeable for an example.

For further learning you might like to play around with the example repo for this article: https://github.com/x-team/starting-css-modules

And for more extensive demos of css-modules take a look at:

You can also read Part 2 of this post: CSS Modules: Rethinking the past

If you end up using css-modules in your own project I’d love to hear about it! Tweet at me: @joshwnj

We'll help you unleash.

Join the 20,000 developers who subscribe to our newsletter.

Scale your
Development team

We help you execute projects by providing trusted developers who can join your team and immediately start delivering high-quality code.

Hire X-Team
code, css, developers