Photo by Matt Duncan on Unsplash

CSS Modules, what’ve we learned

Michael Jenis
Sep 17, 2019 · 6 min read

When I started to work in Javascript, everyone warned me of using the global scope, for obvious reasons. Yet, executing CSS as it is right now, is nothing different than using a global scope.

And it’s fair to say, that every coder runs into the same damn issues over and over again

  • declaring a class you’ve already declared, leading to unwanted overwrites
  • matryoshka (nesting doll) nested classes
  • !important
  • redundancies created by fear of deleting parts of code that might break your app elsewhere
  • improvements to your current code leading to new bugs
  • untraceable code due to string composing in JS
  • over-qualified selectors (h1#header.main-header span i.highlight { … })
  • grandchildren selectors and loose modifiers in BEM
  • no clear structure of your .css files

At Socialbakers, we tried to fix this problem by using BEM. Finally, a rule-based naming convention that made sense and was widely used. It was a perfect solution until…

…until we applied it on a huge multi-level, sub-components nested application that spans through numerous repositories like ours.

When extended by our own component library, it became clear that this was not going to be sustainable nor maintainable in the way we intended.

Furthermore, it wouldn’t provide any level of automation, which makes it more prone to human error.

I’ll fast forward a bit. These days Socialbakers is hiring new HTML, CSS coders. To each candidate, we send a task. What caught my eye was how people approached CSS selectors and their usage of classes:

h1#header.main-header span i.highlight { … }

.sidepanel .container .content .box.left .menu ul li .text

ul li a

Apart from using tags, ids or its combinations as selectors, as they went further down the DOM structure, it became messier, with each following level of nesting creating a Matryoshka Doll Hell.

For a third party, reading your code becomes pretty difficult. What’s more, this is how you can become a real pain in the stack, especially if you work in a team or if someone else has to take over your code.

It’s true that we’re now in the midst of implementing the CSS modules. Since our web application consists of many smaller apps and packages, the new ones are written in CSS modules from the start. The older ones, written in plain less with some BEM sugar, are being slowly re-written.

This parallel universe creates a perfect benchmark.

In the older projects, writing plain old CSS was somehow soul-soothing. No imports, one global blob that devours everything. Every change is quick and easy.

On the other hand, this old code is the one that gets the most buggy. And the root of this issue is global scope. The more code you write, the bigger the chance you’ll accidentally define a class you’ve already defined for another component, and unintentionally overwrite its properties. Deleting something that is assumed to be obsolete, yet is still used, is another classic.

CSS Modules feel like overkill. Let’s say, you want to add a background color. Besides the implementation (e.g. in Webpack), you have to create a new CSS file in the folder where your JS file lives. Write down your CSS as usual. Import your newly created CSS file into the JS file and assign the element’s class as a property of that imported object.

It’s actually easier than it sounds. Check out this article by SitePoint that explains how to use CSS modules very well:

Since CSS modules create a local scope by default, you have to repeat yourself in some cases but that actually brings me to the primary reason why we use them.

Thanks to the local scope, every CSS (if not defined explicitly as global) is bound to a specific component. What that actually means for you is that you can use the most basic class names repeatedly throughout the app and never run into a conflict.

I find it very useful when it comes to a code clean up. When removing a component, I am not only deleting the JS file but also the related CSS file. If I’d previously imported that CSS file into another component, I’ll get an alert from the compiler. This creates a safe haven for the maintenance of our code, avoiding unnecessary bugs elsewhere.

If you’ve ever worked with a larger code stack you’ve encountered the same problem. Naming and name-nesting. Containers, wrappers, contents, boxes, boxes in wrappers in containers… not mentioning variations of those due to e.g. social platforms we actually work with (container-ig, container-yt, container-fb).

Keeping in mind that class names should be universally descriptive and not subjective to their contents, the list of generic names can quickly get short.

CSS Modules resolve that in the most elegant way. It transforms the locally scoped classes to globally unique hashed classes by taking the local class, prefixing it with the filename and appending it with the unique hash (depending on your configuration).

Like this, you can use as many .container class names across your app as you want because each lives in its own local scope bound to the specific component.

One of my biggest fears regarding CSS Modules was an orientation in my own code. If I want to change any CSS, how the heck should I find the class in the code, when it already comes in a hashed way to the inspector?

Do I have to search by a filename, then the linked CSS files, and then the exact CSS class? Exactly, and it’s no big deal!

Don’t get me wrong, implementing CSS Modules into our environment was no small thing. We needed to solve many case-specific issues that appeared on the way. We needed to use some hacks on the way. It brought us a few headaches when it came to selectors in deeply nested third party packages injected into our structures. Thankfully the community around CSS Modules has expanded a lot, providing solutions to most of the encountered issues.

That brings me to an answer to a question I had when we started implementation. Are CSS Modules for us (and possibly you)?

  • it provides a safer local scope that proved very useful when working in a team of coders, and in symbiosis with frontend developers. Keeping those definitions apart, bound to their own components, proved safe when anyone can add/remove something from your code
  • onboarding new guys to our team, and readability, would be smoother because classes that make sense to us, might not be helpful to them, especially while our CSS codebase consists of tens of thousands of code
  • compared to our older codebase, CSS Modules definitely keep the code clean from the obsolete and redundant. Thus, ease of maintenance would be a strong pro.

Knowing all of this, it’s clear CSS modules offer us lots of value but I would encourage you to check out the article ‘Do I really need CSS modules’ by David Wells which brings further useful information that would help you to decide.

If you’re interested in knowing more about the story of the creation of CSS Modules, I recommend that you watch The case for CSS Modules by its co-creator Mark Dalgleish:

Thanks for reading. And may the code be with you.

Sounds interesting? Check out more of our stories & don’t forget we’re hiring!


We want to give you a sense of all the systems and technologies that power Socialbakers, and introduce our thinking, principles and our tech stack.

Michael Jenis

Written by

European. Programmer at Socialbakers. Political enthusiast. Star Wars know-it-all and Marvel Hero


We want to give you a sense of all the systems and technologies that power Socialbakers, and introduce our thinking, principles and our tech stack.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade