CSS Modules: Local Scope for CSS

Nick Hinsch
Garbage Collection
Published in
3 min readFeb 2, 2017

We're getting ready to implement a new design system at Managed By Q, and that means a fresh start for our CSS codebase. Keeping a large CSS codebase organized can be challenging, so we knew that we needed to think carefully about how to keep our styles organized as our applications grow. After evaluating several options, our team has chosen to use CSS modules for this purpose. In this blog post, I'll share the advantages of this approach and show how to implement it yourself.

The challenge of scalable CSS

One of the most challenging aspects of CSS is that all selectors inhabit a single global scope. This can make it difficult to know exactly which selectors in a codebase apply to a specific element, as applicable selectors could be located anywhere in the project. Conversely, there is no easy way to tell which elements will be affected by a given selector, making it hard to delete or refactor CSS without manually checking that your changes do not cause problems. These challenges are compounded by the manner in which selectors take precedence over one another based on their specificity, and not based on their proximity to one another.

Fortunately, it is possible to achieve the effect of local scopes if we follow certain rules in writing our CSS. A popular way to do this is to use the Block, Element, Modifier methodology, or BEM for short.

BEM: Scoping CSS the Hard Way

BEM is a naming scheme for CSS classes. When used consistently, it creates the effect of a local scope for each component or block.

When we use BEM, we use only class selectors and we name each class according to the format .block__element--modifier. For example, if we created a gallery component and wanted to style a button within this component, we might create the classes .gallery__button and .gallery__button--disabled. By prefixing these classes with the name of the component or block, we have achieved the benefits of a local scope. If we use this naming system throughout our project, we won't have to worry about another module's styles conflicting with our gallery.

However, having clumsy class names like .gallery__button--disabled can make our markup feel cluttered, and it can be difficult to enforce the BEM rules. What if there were an automated way to achieve the benefits of BEM?

CSS Modules

That brings us to CSS Modules. CSS Modules are not a technology implemented in browsers but rather a stylesheet transformation step that we incorporate into our build process. Instead of manually writing BEM syntax, we instead create a simple stylesheet for each component in our application and let an automated build tool or module bundler like Gulp or Webpack use our styles to generate scoped, BEM-like CSS.

Let's say we created a Gallery React component and wanted to write styles for it. We would create a stylesheet for the component and write our styles using simple selectors like .button and .title. We would then import the stylesheet as a dependency of the React component, just as we would a JavaScript module.

import styles from “./gallery.css”

Now the classes are available as the properties of a styles object, and we can use them in our markup accordingly.

<div className={styles.title} />

What is going on here? Is our CSS being converted into JavaScript somehow? No-- as mentioned previously, the magic happens in our build process when we bundle our CSS. The build step looks for the use of CSS modules and re-writes our files to use BEM-like styling that looks like this:

<div className="_gallery_title_354" />

The build step takes the work out of BEM syntax but provides the same advantage: we no longer have to worry about accidentally messing up other components with our styles. We also now can easily keep track of which components depend on which styles by observing which stylesheets are imported where.

Implementing CSS Modules

Getting CSS Modules to work is simply a matter of configuring your build tool or module bundler of choice. At Managed By Q, we use Webpack to bundle our modules, so that's the tool we had to configure. Webpack uses plugins called loaders to process files, and its css-loader comes with support for CSS Modules out of the box. All we needed to do was to add the appropriate setting in our Webpack config file to get up and running.

Summary

CSS Modules allow us to scope our styles to particular components of our application without using an unwieldy naming methodology and to easily keep track of which styles are used where. We're excited to use them going forward and would recommend experimenting with them to anyone seeking more maintainable CSS.

--

--