A CSS Module is a CSS file in which all class names and animation names are scoped locally by default, eleminating the problems with global nature of the CSS namespace.
You can use a module bundler such as webpack to load CSS scoped to a particular document.
CSS Module Loaders
CSS module loader will generate a unique name for a each CSS class at the time of loading the CSS document (Interoperable CSS to be precise).
Why CSS Modules?
CSS by default has global scope. This can lead into numerous issues:
- Global namespace - Selectors can clash with other selectors targeting same elements.
- The specificity wars can break your selctors.
- Forced to use !important to override element level styles.
With css-modules, we have a way to emulate local scope and control dependencies in front-end components.
CSS Modules compile to a low-level interchange format called ICSS or nteroperable CSS
, but are written like normal CSS files:
Before CSS Modules:
/* button.css */
.button-normal {
color: blue;
}
.button-error {
color: red;
}
.button-disabled {
color: grey;
}
With CSS Modules:
/* button.css */
.normal {
color: blue;
}
.error {
color: red;
}
.disabled {
color: grey;
}
That’s made possible by the way CSS Modules are compiled — by using require or import to load the file from JavaScript:
import React from 'react';
import styles from './button.css';
class Button extends React.Component {
render() {
return <input type="button" className={styles.normal}>;
}
}
The actual class names are generated automatically and are guaranteed to be unique. Your souce code may look like the following:
<input type="button" class="components_button_normal_acf7488">
When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names.
Scoped Selectors
In CSS Modules, selectors are scoped by default.
CSS Module semantics ensure that classes are locally scoped to the component and don't collide with other classes in the global scope.
In the following example, the component uses two classes, .root
and .text
.
import React from 'react';
import styles from './scoped-selectors.css';
class ScopedSelectors extends React.Component {
render() {
return (
<div className={styles.root}>
<p className={styles.text}>Scoped Selectors
</div>
);
}
};
export default class ScopedSelectors;
Let's look at the CSS file.
.container {
border: 2px solid #c7254e;
padding: 0 20px;
margin: 0 6px;
max-width: 246px;
border-radius: 10px;
}
.text {
color: #c7254e;
font-size: 24px;
font-family: helvetica, arial, sans-serif;
font-weight: 600;
padding-top: 15px;
}
The output will look like this:
<div>
<div class="container">
<div class="text">Scoped Selectors</div>
</div>
</div>
Global Selectors
Global selectors are still available but should be used sparingly.
import React from 'react';
import styles from './global-selectors.css';
class GlobalSelectors extends React.Component {
render() {
return (
<div className={ styles.root }>
<p className="text">Global Selectors
</div>
);
}
};
export default GlobalSelectors;
Let's look at the CSS file.
.root {
border: 2px solid brown;
padding: 0 20px;
margin: 0 6px;
max-width: 234px;
border-radius: 10px;
}
.root :global .content {
color: #00ACEE;
font-size: 24px;
font-family: helvetica, arial, sans-serif;
font-weight: 600;
}
The output will look like this:
<div>
<div class="root">
<div class="content">Global Selectors</div>
</div>
</div>
Class Composition
Composition promotes better separation of markup and style using semantics that would be hard to achieve without CSS Modules.
.classB {
composes: classA from "./style.css";
}
Make sure not to define different values for the same property in multiple class names when they are composed to a single class.
Both of the components below have locally scoped CSS that is composed from a common set of CSS Modules.
Since CSS Modules can be composed, the resulting markup is optimised by reusing classes between components.
styles/layout.css
.box {
border: 2px solid brown;
padding: 0 20px;
margin: 0 6px;
max-width: 234px;
border-radius: 10px;
}
styles/wording.css
.content {
color: #00ACEE;
font-size: 24px;
font-family: helvetica, arial, sans-serif;
font-weight: 600;
}
.root {
composes: box from "styles/layout.css";
border-color: red;
}
.root .content {
composes: content from "styles/wording.css":
}
You compose multiple classes with composes: classNameA classNameB;
.
Current Implementations
Webpack's css-loader
and react-css-modules are few of the implementations for CSS Modules.