Values for writing maintainable and scalable CSS

When working with CSS codebases, we should treat them with the same respect as we do other codebases. Yes, code smells do exist in CSS — in fact, you may have seem them before elsewhere: magic numbers, poor naming conventions, spaghetti code, and good ol' fashion brute-forcing.

Spaghetti code is less joyful. (Hyperbole and a Half)

Now, the truth of the matter is that CSS was never meant for scale, with the cascading behavior itself being a huge impediment. To address some of these issues, there is a veritable alphabet soup of methodologies out there (BEM, OOCSS, SMACSS, Atomic Design, to name a few) which have some worthwhile ideas about how to write saner and more structured CSS.

This post is not about any of those methodologies, or about why CSS sucks. This post is about getting back to basics: things we value in our CSS. These values provide us with a guiding philosophy for what is most important to us, and serve as motivation to adopt new practices or abandon ones that aren’t working. They also help us to better communicate with each other, through both face-to-face conversations and the code we write.

1. We value CSS that is understandable and that can be reasoned about.

It doesn’t take very long for CSS to reach the “ball of mud” stage, especially if you’re relying on a framework, such as Bootstrap or Foundation. It gets increasingly difficult to know why some style rule is being applied or overridden in a specific context, or if it’s even being used at all.

Because it is practically impossible to know at compile time what effects the CSS will produce, we can end up with a lot of dead or duplicate code that everyone is afraid to touch. If we are able to understand why those rules exist, then we will feel more empowered to make changes to them.

2. We value CSS that is modifiable with confidence.

Can we add new things easily? Can we make changes to old code without breaking existing behaviors or producing unintended side effects? This goes hand-in-hand with our ability to reason about our CSS.

Refactoring CSS is inherently a risky business. It is far too easy to override a style rule, and much more difficult to effectively reuse groups of style rules. As a result, the cost of making a change can start to creep upwards the larger our codebase grows.

We ultimately care about writing CSS that will scale as our application scales, so that means using strategies that will mitigate these types of risks, thereby increasing our confidence in the code we write.

3. We value CSS that is proactive, not reactive.

If we’ve lost the ability to reason about our code, we’re no longer able to modify it confidently, and thus further changes become reactions to the contraints of the existing system. Reactionary code usually increases in specificity, until we finally just throw down !important as our white flag.

In theory, it would certainly make our lives easier if we decide all p elements should be styled exactly the same to reduce duplication in our HTML markup and our CSS. But what happens as soon as we want to introduce a new style for p.foo elements? Since they will inherit our initial styles, we will need to go to the trouble of overriding the rules that are different.

If we have to override a style, it usually means we made some kind of assumption and applied the rule too soon. We value the ability to make proactive decisions about the code we write, rather than reacting defensively to code that already exists and creating unnecessary workarounds.

Closing thoughts

When trying to figure out what practices to implement with your team, it really helps to agree on a set of values and principles you want to follow. This helps to build a sense of collective ownership and discipline around your CSS codebase, and provides a framework for grounding future decisions.