TL;DR: Install this plugin 👉 https://www.npmjs.com/package/stylelint-css-modules-no-global-scoped-selector

Introduction

When using CSS Modules to apply some style to a React component, there is a risk to impact other components if the selectors are not scoped correctly (using a class or an id).

A typical case when this issue can happen is with external libraries that require some CSS customization that are not possible using the props exposed by the library. For example, if we use react-select in our component and we want to customize it, we can directly apply CSS to internal classes according to the docs.

The problem here is that if we just write .react-select__control { ... } in our CSS module file it will not work because the class will be renamed to something like .react-select__control__XXXX to be scoped. To avoid this behavior, we can add the :global keyword before the class to prevent it to be renamed. This solution works but it's risky because the selector is not scoped anymore: all the react-select used outside the component are also impacted by this style.

To avoid this issue, the solution is to add a scoped selector before the global keyword: .customReactSelect :global .react-select__control { ... }

In this case the component would look like:

.customReactSelect {
  :global .react-select__control {
    // ...
  }
}
CustomReactSelect.module.scss
import ReactSelect from 'react-select';
import styles from './CustomReactSelect.module.scss';

const CustomReactSelect = () => (
  <div className={styles.commonReactSelect}>
    <ReactSelect classNamePrefix="react-select" />
  </div>
);

// Or even better:
const CustomReactSelect = () => (
  <ReactSelect
    classNamePrefix="react-select"
    className={styles.commonReactSelect}
  />
);

export default CustomReactSelect;
CustomReactSelect.tsx

In this example, only this occurrence of react-select will be impacted 🎉.

But how to be sure non-scoped selectors will never be written in our CSS module files?

Configure Stylelint to prevent usage of non-scoped selectors

Stylelint is a great linting tool for CSS files (like ESLint for JS files). You can run it locally in your terminal, programmatically in your CI pipelines, and you can also integrate it directly in your IDE (like ESLint) so that errors are underlined in red.

To avoid writing non-scoped selectors in CSS modules I wrote a Stylelint plugin, you can check it here 👉 https://github.com/lmichelin/stylelint-css-modules-no-global-scoped-selector. Installation steps are detailed in the Readme.

Once this plugin is integrated into your Stylelint configuration, you can see it in action in VS Code for example (you need to install the Stylelint extension for VS Code first):

Non-scoped selector reported by Stylelint in VS Code

Don't hesitate to integrate this Stylelint plugin in your projects, and feel free to open an issue on the GitHub repository if you detect a bug or a missing feature!