Now I want to make large and small versions of my primary and cancel buttons. In vanilla CSS, I might do this by defining rules common to a button, then additional rules for specific sizes, then rules for primary and cancel. It might look like this:
Okay, not bad. What if I want to add another size? Or another color? Or another vector, like whether the button is enabled? Now it’s going to get more complicated to define and to maintain. Luckily, with CSS modules, we can define the basic button components and compose them into the buttons we need.
Above, I described the components I’d need, and then translated them into CSS. Instead, I’ll just define the components by themselves. I could start by defining a small set of rules common to all buttons:
Then some button sizes:
And some button colors:
And finally, I’ll compose my buttons:
I’ve created a set of reusable components that have been composed together like a great CSS Voltron. If one wanted to be really future-proof, it’s best to take the time to define many single-responsibility components that each contain as few rules as possible. That way, new rule sets can be created by composing existing rule sets in different ways, instead of trying to override existing rules in the inheritance hierarchy.
In vanilla CSS, when we come across an element that needs a one-off, our best choice is to apply the closest existing class to the element, and then create another class to bend the existing class to our needs. We apply both classes to our element. If we do this enough, we’ll end up with a bunch of small rule sets all modifying different properties of a single base rule set. This fragile situation leaves us open to disaster if someone unwittingly modifies the base rule set.
However, with CSS modules, I just assemble the components for the class I need. If the components are small and have a single responsibility, it’s less likely that the components will need to be modified in the future. If I need to change my composed class, I just switch out which components it’s using or modify its additional rules.
If you’re familiar with CSS preprocessors, you’re probably thinking that this looks a lot like the “extend” functionality. From a programmer’s perspective, you can think of them similarly. However, the output of each is quite different. Preprocessors actually copy the referenced rules and selectors creating self-contained rule sets. CSS modules remain separate, but each composing class is added to the element you’re styling.