Diving Into CSS Modules
Lately, I had to work increasingly on modular projects, and in the process, I discovered a nifty new little thing called CSS modules. Ok, to be perfectly honest, they are not as new as one would expect, but they were new to me.
In this short article, I am going to explain what CSS modules are, why they can be a powerful tool if working on a modular frontend and why you should consider using them in your next project.
CSS modules are a nifty little feature, that can be used in combination with a module bundler like Snowpack to help prevent style conflicts and keep your code isolated and more organised. They work in combination with shadow DOM, but become obsolete since shadow DOM already provides code isolation.
What are CSS Modules?
Just the name is already intriguing and sparks interest, doesn’t it? But what are CSS modules really? They are CSS files, in which all classes and animations are locally scoped by default.
This basically means, you can have two CSS files, which can have the same classes defined, and in the end, both class definitions will be different and will not affect each other.
Let me show you a simple example. We have two CSS files, styleOne.module.css and styleTwo.module.css:
CSS modules are usually named by appending .module on the end of the file name. This, however, is not required and you can name your CSS modules any way you want.
As we see above, both files have defined the .button class. Now we can import both CSS files into our application and define the HTML:
The resulting HTML will look something like this:
Both buttons will have different styles that will only affect the assigned element.
I have deliberately chosen ReactJS to demonstrate the example since it already is pretty common and it’s one of the simplest examples I could give.
But, Why Using CSS Modules?
As I already mentioned, CSS modules are self-isolating components. This means classes and animations, defined in separate modules, although they may have the same name, will not affect each other.
Anyone who has worked with CSS before knows that side effects can be pretty common and hard to debug. How many of us had to quickly change a piece of CSS code to fix an issue just to realize a few days later that you also changed the styling of another element that shouldn’t be related in any way. And sometimes it isn’t just quick fixes.
For example, you write a style for an HTML container and use that element throughout your application. Now you have to change the padding on one single instance. So either you add another class or you try to tweak the existing CSS so that you can include the new padding without any serious side effects.
CSS modules address all of these issues. They only affect the elements you assign them to. In a sense, they are similar to JS modules where the code, written in a module, only resides in that module and does not affect any other parts of your application unless you want them to.
CSS modules can be a literal lifesaver. The only use case, where they would not be needed is the shadow DOM, but we will discuss that topic at a later point in this article.
So Where do I Use CSS Modules?
You can use CSS modules just about anywhere you need to isolate and modularize your stiles so they do not cross each other.
The most common use case would be some of the more popular JS frameworks, like React or VueJS, where you write HTML templates directly and use them in components. Let’s create a simple React class component:
The file cssmodulesexamplecss.module.css would look like this:
The result would be red text on a blue background. It is as simple as that. The CSS module would not apply anywhere else, even if you have another component with the same classes in its template unless you import the CSS module there too and use its exported properties to inject the classes.
This way it is impossible to break another part of your application by changing something small like, let’s say, the padding from 20px to 40px (of course this is just a general example and it never happened to me, not me…).
CSS Modules and Shadow DOM
Shadow DOM is a scoped subtree inside your actual page, that is hidden from the rest of your application. Just like iframes, shadow DOM elements are not accessible from the outside, so globally loaded CSS does not affect the elements inside the shadow DOM unless you want it to.
This means, that CSS inside shadow DOM is already isolated and if used only for modularization of styles, CSS modules become obsolete. There are more advantages of using CSS modules, however, the main advantage is in shadow dom not relevant anymore.
How to Compile CSS Modules
CSS modules are a nifty little feature but how does everything play together? After all, CSS modules are not a part of the official specifications and thus are not implemented in browsers.
The truth is they get resolved at build time. To use CSS modules you need a module bundler like snowpack, Webpack, or Browserify. After the code gets compiled, you get normal CSS. The above snipped “cssmodulesexamplecss.module.css”:
would get compiled into something like this:
Let’s try to explain all the steps necessary to get from the module to the finished result.
A Simple Example
Compiling CSS modules requires an additional step in your building process. I’ll assume that you are just starting with the building process and guide you through each step to build your very first building pipeline. If you already have a project set up, you might still benefit from the below steps.
1. Initialize your project
The first step is always initializing your new project. You need a package.json file. Initialising can be a one-liner. Run one of the following lines:
I strongly suggest you use Yarn since it is an improvement over the traditional (and quite a bit old) npm.
2. Install Snowpack
We will use Snowpack as our module bundler. It is a relatively new tool that should make building your code much easier and quicker. And it delivers on that promise.
You can find more information on Snowpack below in the references. For now, we are just gonna install it.
To install this tool, run one of the following lines:
npm install --save-dev snowpack
yarn add --dev snowpack
We can use snowpack directly by calling it through npm/yarn:
yarn run snowpack dev
Or, if you are like me and like a more convenient way, add the following lines to your package.json:
"start": "snowpack dev",
"build": "snowpack build"
Now you can start developing/building your project by running:
yarn start; // to start the development server
yarn build; // to build your code for production
3. Configure your project
We actually do not need to do much configuration since snowpack supports CSS modules out of the box. Neat, eh?
4. The finished code
We will keep the code as simple as possible to avoid any confusion. After all, we are only talking about CSS modules, the other stuff comes into separate articles. Let´s create three files, index.html, index.js, and cssmodulesexample.module.css:
The index.js file is pretty simple. We import the CSS module and populate the body tag with some HTML. We also set the class of the container and of our title. We got the classes from our CSS module.
The CSS file is as simple as it gets, two classes, one with a blue background and the other one with red text.
Did you create all three files? Good, now start the bundler with yarn start and watch the magic happen. The bundler should compile your code and automatically open your browser.
5. The result
The result is the prettiest website you have ever seen:
Snowpack converts the CSS file into a js module, which exports the CSS classes each with a unique name, in our case it is _container_yncog_1 and _mainTitle_yncog_7.
By setting a unique name for every class the compiler ensures that there are no naming conflicts between classes that are defined in separate modules but have the same name.
In this article, we learned what CSS modules are and how you could use them to make your codebase more stable and organized. They are a great way to avoid CSS overlaps and can easily be applied anywhere as long as you use a module bundler.
here are some references to the things we talked about in this article but did not discuss in detail. I highly recommend reading the below-linked articles since they explain a lot of the technologies that are or will become common in web development.
Introduction to Shadow DOM:
Introduction to the Shadow DOM
The DOM, Document Object Model, is an object-oriented representation of the website that is currently displaying in the…
Introduction to Snowpack and unbundled development:
Native ES6 Modules: