There are many ways to style a website with css. In this post I will share my findings migrating from global css to css modules. Before we start comparing, lets have a quick look at what global and css modules are.
Global CSS
Global css is the most widely used form for styling websites. We add a css file, we include it on the site and its content is available to use on any html element.
CSS modules
Aka component-scoped css. The css we include is scoped to the module referring the css file. To use css modules we need to do a few things.
Configure CSS loader
This is already done behind the scenes for both gatsby and create-react-app (CRA).
A webpack example:
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true
}
}
],
include: /\.module\.css$/
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
],
exclude: /\.module\.css$/
}
]
}
Renaming the CSS file
In order for the css loader to distinguee pure css files from css modules we need to rename the file to include "module", in example: layout.css
to layout.module.css
.
Implementation details
When working with global css in react, all we need to do is to include it like this:
import './article-overview.css'
We then add the class to the element:
<div className='article-overview-container'>
In css modules we work with objects like any object in javascript:
import styles from './article-overview.module.css'
<div className={styles.articleOverviewContainer}>
And.. we can even use named imports:
import {classname1, classname2 } from './article-overview.module.css'
Which would give us warnings or errors(configurable) when for example misspelling an import after a rename:
Naming
The rendered css content might be more clear when using global css depending on how you name your css classes. When using global css I like to prefix classes with the filename to make sure it's unique. In example:
.article-overview-container {
border-bottom: 1px solid #f1f1f1;
margin-bottom: 30px;
padding-bottom: 30px;
}
article-overview
is the prefix and container
is the intended name.
With css-modules a prefix and suffix is automatically added to the css classnames.
.container {
border-bottom: 1px solid #f1f1f1;
margin-bottom: 30px;
padding-bottom: 30px;
}
Red = filename + module. blue = classname, teal = hash.
Note that this is the default naming convention for gatsby. There are other ways to set up your naming conventions by configuring the css-loader.
See localIdentName
at https://webpack.js.org/loaders/css-loader/
My convention in this example is: [filename]-module--[classname]--[hash]
Conclusion
Css modules give us a way to ensure that a css file is unique across components and not accidentally accessed elsewhere. This can be especially useful when working in bigger solution across many different teams. It can also gives us type-safety when using named imports to reduce the chance of importing a class that isn't defined. It can though be a bit harder to find the actual css file from a classname. Nonetheless, knowing the naming convention would help a lot to resolve that.
Happy Coding!