CSS Modules Webpack Demo


Scoped Selectors

In CSS Modules, selectors are scoped by default.

The following component uses two classes, .root and .text, both of which would typically be too vague in a larger project.

CSS Module semantics ensure that these classes are locally scoped to the component and don't collide with other classes in the global scope.

Output

Scoped Selectors

ScopedSelectors.js
import styles from './ScopedSelectors.css';

import React, { Component } from 'react';

export default class ScopedSelectors extends Component {

  render() {
    return (
      <div className={ styles.root }>
        <p className={ styles.text }>Scoped Selectors</p>
      </div>
    );
  }

};
ScopedSelectors.css
.root {
  border-width: 2px;
  border-style: solid;
  border-color: #777;
  padding: 0 20px;
  margin: 0 6px;
  max-width: 400px;
}

.text {
  color: #777;
  font-size: 24px;
  font-family: helvetica, arial, sans-serif;
  font-weight: 600;
}

Global Selectors

Although they should be used as sparingly as possible, global selectors are still available when required.

The following component styles all <p> tags nested inside it.

Output

Global Selectors

GlobalSelectors.js
import styles from './GlobalSelectors.css';

import React, { Component } from 'react';

export default class GlobalSelectors extends Component {

  render() {
    return (
      <div className={ styles.root }>
        <p className="text">Global Selectors</p>
      </div>
    );
  }

};
GlobalSelectors.css
.root {
  border-width: 2px;
  border-style: solid;
  border-color: brown;
  padding: 0 20px;
  margin: 0 6px;
  max-width: 400px;
}

.root :global .text {
  color: brown;
  font-size: 24px;
  font-family: helvetica, arial, sans-serif;
  font-weight: 600;
}

Class Composition

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.

Output

Style Variant A


Style Variant B

StyleVariantA.js
import styles from './StyleVariantA.css';

import React, { Component } from 'react';

export default class StyleVariantA extends Component {

  render() {
    return (
      <div className={styles.root}>
        <p className={styles.text}>Style Variant A</p>
      </div>
    );
  }

};
StyleVariantA.css
.root {
  composes: box from "shared/styles/layout.css";
  border-color: red;
}

.text {
  composes: heading from "shared/styles/typography.css";
  color: red;
}
shared/styles/layout.css
.box {
  border-width: 2px;
  border-style: solid;
  padding: 0 20px;
  margin: 0 6px;
  max-width: 400px;
}
shared/styles/typography.css
.heading {
  font-size: 24px;
  font-family: helvetica, arial, sans-serif;
  font-weight: 600;
}

Composition Overrides

When composing classes, inherited style properties can be overridden as you'd expect.

The following component composes two different classes, but provides overrides which then take precedence.

Output

Class Composition with Overrides

CompositionOverrides.js
import styles from './CompositionOverrides.css';

import React, { Component } from 'react';

export default class CompositionOverrides extends Component {

  render() {
    return (
      <div className={styles.root}>
        <p className={styles.text}>Class Composition with Overrides</p>
      </div>
    );
  }

};
CompositionOverrides.css
.root {
  composes: box from "shared/styles/layout.css";
  border-style: dotted;
  border-color: green;
}

.text {
  composes: heading from "shared/styles/typography.css";
  font-weight: 200;
  color: green;
}
shared/styles/layout.css
.box {
  border-width: 2px;
  border-style: solid;
  padding: 0 20px;
  margin: 0 6px;
  max-width: 400px;
}
shared/styles/typography.css
.heading {
  font-size: 24px;
  font-family: helvetica, arial, sans-serif;
  font-weight: 600;
}

Scoped Animations

CSS Modules even provide locally scoped animations, which are typically defined in the global scope.

The animation's keyframes are private to the animations module, only exposed publicly via a class which this component inherits from.

Output
ScopedAnimations.js
import styles from './ScopedAnimations.css';

import React, { Component } from 'react';

export default class ScopedAnimations extends Component {

  render() {
    return (
      <div className={styles.root}>
        <div className={styles.ball} />
      </div>
    );
  }

};
ScopedAnimations.css
.root {
  padding: 20px 10px;
}

.ball {
  composes: bounce from "shared/styles/animations.css";
  width: 40px;
  height: 40px;
  border-radius: 20px;
  background: rebeccapurple;
}
shared/styles/animations.css
@keyframes bounce {
  33% { transform: translateY(-20px); }
  66% { transform: translateY(0px); }
}

.bounce {
  animation: bounce 1s infinite ease-in-out;
}