CSS Modules

Updated Sep 10, 2021#css#css-in-js#libs

Among problems of CSS, global scope is the one that makes building and maintaining CSS at scale challenging. The community has been trying to fix that by naming conventions, preprocessors, or css-in-js.

CSS Modules is another approach that also tries to fix global scope of CSS by let you use the same CSS class name in different files without worrying about naming clashes.

CSS modules are CSS files in which all class names and animation names are scoped locally by default. - official documentation

This approach is not an official specification or an implementation in the browser but rather made possible by build tools (Webpack, Browserify, etc.) that changes class names and selectors to be scoped.

CSS Modules is often used with component-based architecture JavaScript frameworks like React or Vue, possible along with other pre-processors. When imported, every class name and animation inside a CSS module is scoped locally to the component that is importing it.

Under the hood, CSS modules are processed by loaders like webpack’s css-loader or postcss-loader as a step of build tools of your choice, compile to a low-level interchange format called Interoperable CSS, the CSS is either injected into the DOM or bundled into a separate CSS package.

With React projects, Next.js and create-react-app support CSS Modules by default using the [name].module.css file naming convention. With Vue projects, vue-loader provides first-class integration with CSS Modules as an alternative for simulated scoped CSS.

Here is an example from create-react-app docs:

src
 |-components
    |-Button.js
    |-Button.module.css
/* Button.module.css */
.error {
  color: red;
}
// Button.js
import React, {Component} from 'react'
import styles from './Button.module.css'

class Button extends Component {
  render() {
    return <button className={styles.error}>Error Button</button>
  }
}

Result in HTML after built:

<button class="Button_error_ax7yz">Error Button</button>