Global CSS is the last fortress fighting component-based frontend architecture. As developers, we’ve came up with numerous solutions like BEM or rscss. What comes up next are CSS Modules, and I’d like to walk you through setting them up for React project.
TL;DR: if you use react-like templates/components, use webpack css loader like described here (look for config example) to enable CSS Modules and forget about global css problems.
In a nutshell, CSS modules is a technology for automatic creation of unique class names in styles. It leads to style encapsulation, so you can’t overlap styles within different components due to class name collision. It’s a variation on class-naming schemes mentioned above, but based on machine code and simple processing instead of developer discipline.
Let’s think of this situation. We have two css files with the code like this:
If we’d use exactly this css, border for element with .title class in both components would be the same — depending on file import order and concatenation technique used. On scale of two files and one developer it doesn’t seem to be that big trouble. But once you consider a bit more sophisticated project, team of people writing css and use any of these bootstrap-like frameworks and you’ll get the taste of coming issues.
As I’ve mentioned, you could stick to some methodology (BEM, rscss or the like) and avoid this collision by creating unique names by hand. But these solutions require more discipline than average (say pragmatic) developer is ready to put into styling.
On the contrary, css-modules are automatic solution for giving our classes unique names, thus saving our developer time and nerves for actual work. Speaking of example above, CSS Modules will convert the code into something like this:
Now we have unique class names but don’t know how to use them in our markup/templates/jsx/etc. Basically we have two options: use webpack css-loader with special parameters and fall back to postcss-modules plugin for postcss.
With css-loader and webpack, you’ll need to configure loaders for your styles in module.loaders like this:
Let me walk you through all the words here:
- We tell webpack to pipe all files with .css extension through style-loader, css-loader and stylus-loader. The latter is used in place of your favorite css tool, so feel free to change it to anything else (personally, I’m fond of postcss). After all processing, we use ExtractTextPlugin to pack all resulting styles into one css file.
- modules parameter to css-loader makes it use CSS Modules, and importLoaders=1 stands for enabling us to reuse class styles from one module in others, which I’ll show a bit later.
- Note that we’re passing two loader strings into ExtractTextPlugin. Configuring all loaders in one string somehow breaks the build, so be warned.
With such setup, we’ll be working with code like this:
Most of the time you want your class names to be unique (locally-scoped), there are cases you want plain old global class-based CSS. To achieve this, you wrap class name selector with `:global`:
Escape from global namespace would be quite controversial, if we’ll need to sacrifice style reuse. Happily, we can use composes keyword to make our styles to reuse some basic ones. That’s how we ask css-loader to import style rules from one locally-scoped class to another:
If you want deeper look into issues and CSS Modules technologies, take a look at Andrey Sitnik’s slides on the matter.
Use CSS Modules, write good styles, love mom and PostCSS.