CSS Modules - pre-processing CSS and HTML to scope CSS locally by default.

From code like this:

import styles from './Component.css';
 
<div class={styles.component}>...</div>
.component {
	padding: 10px;
	border: 1px solid #ccc;
}

To output like this:

<div class="Results__component___3It6Y">...</div>
.Results__component___3It6Y {
	padding: 10px;
	border: 1px solid #ccc;	
}

Aiming to address CSS’s problems caused by the global inheritance when elements' inherit parent styles. Another nice benefit is you spend a lot less time thinking about what to name classes.

Having used it on a couple of personal projects their are few reasons I have come across why I have not used it on a production project with other developers yet. This is using CSS Modules with webpack.

Dynamic Classes not Usable for Testing / Scraping

As you can see from the code example above, in order to help make the class names unique part of the name is a random string meaning you cannot rely on CSS class names for tests and web scraping.

Solution:

Always keep part of the class name consistent document.querySelector('[class^="Results__component__"]').

Potential Webpack Setup Incompatibility

This point has little to do with CSS Modules itself, but is somewhat noteworthy in React/webpack land.

Some of the examples using CSS Modules with webpack use webpack’s ExtractTextPlugin to output the processed CSS into a CSS file. This plugin is not compatible with another popular React/webpack setup - hot reloading https://github.com/css-modules/webpack-demo/issues/8 as hot reloading does not refresh CSS files.

Solutions:

Only use the ExtractTextPlugin in production. Seems to be the recommendation https://github.com/css-modules/webpack-demo/issues/8#issuecomment-135666122. The style-loader does work with hot reloading.

Use a tool like BrowserSync to reload the CSS file https://github.com/gajus/react-css-modules#react-hot-module-replacement.

Extra Configuration

These examples use the ExtractTextPlugin like a production build.

Webpack with CSS configuration is like so:

{
	test: /\.css$/,
    loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
}      

With SASS and autoprefixer it looks like this:

{
	test: /\.scss$/,	
	loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!autoprefixer?{browsers:["last 2 version", "> 5%"]}!sass')
}

If the application is doing server rendering you need more config. The HTML needs to be output with the same dynamically generated class names as used by the client config. Therefore in the webpack config for the server bundle this is needed:

{
	test: /\.css$/,
    loader: 'css/locals?module&localIdentName=[name]__[local]___[hash:base64:5]'
}

Side Note: Due to the connectivity problems of cellular networks I still find server rendering important for certain products which is why I have never been interested much in the Shadow Dom solution to scoping as it requires JavaScript.

Conclusion

Really the issues are to do with config. It looks like minutes of work - but anyone working on React/webpack/babel projects knows that getting configurations working can take ages. And other developers may come along and go through the same learning by failure you did before understanding how it works. So you have to decide if the benefits are worth the extra configuration.

For developers inexperienced with writing maintainable CSS working on a long-lived project CSS Modules are definitely a consideration to minimise running into problems down the line.

For developers that know what they are doing with CSS, are used to schemes like BEM and SUIT and large-scale CSS architecture you may choose instead to continue with this in return for a simpler project setup.