At work, we recently took over a complex web application that has been developed over many years. I’m working on improving the front-end. I like the architecture, the code is straightforward and there are tests. Based on the code bases I’ve seen, the situation looks good. There is one problem area, though: the style sheets.
The style sheets for the application have organically evolved over years, written by multiple authors. While there has been some effort to scope CSS into components, every component is a bit different and there are no clear principles on what styles should be set where. This results in two problems:
- Various parts of the UI that should look the same are subtly different.
- When you try to modify something or add something new, you’ll end up fighting the cascading and the specificity of the existing styles.
I imagine this is a familiar situation for many web developers! I’ve certainly been here before. What makes it different is that this time I decided to learn what’s the state of the art for architecting your CSS to avoid these problems. If you are an experienced web developer, you probably already know everything in this article.
The feature of CSS that makes it so easy to mess up is that there’s a single global scope where all your styling rules are interacting. Your styles will accidentally cascade and then you’re in trouble. The solution is to basically create local scopes in CSS, one way or another. I learned that there are two groups of solutions: the manual ones and the more recent automated ones.
The manual way is to structure your CSS in a principled way. There are a bunch of methodologies with similar ideas: BEM, SMACSS, OOCSS etc. I chose BEM, short for block-element-modifier, because it is the simplest one and as such seemed like a good starting point. The basic idea is the following:
- Only ever use class selectors.
- Name your classes according to the block-element-modifier convention.
- No cascading.
This ensures that the styles you want, and only those, get applied to exactly where you want them. Based on my brief experience, it works.
The automated way is to write CSS as usual and then apply it only to specific parts of your DOM tree by using tools like CSS modules or Polymer’s shadow DOM style encapsulation. The good part is that the tools require less manual work and less discipline than BEM. I chose to skip them this time, because they require quite a bit of infrastructure that does not (yet) exist in my project.
I’ve looked at BEM before. Back then, I thought it was suspiciously verbose and did not see the point. I still think it’s verbose, but now I see that it’s also useful.