Organize CSS rulesets by their declarations’ purpose

2019-08-10

In CSS one is tempted to group all possible declarations of one selector together, so as to not repeat the selector. So if we find this:

.selector-1 {
  ...
}

.selector-2 {
  ...
}

.selector-1 {
  ...
}

.selector-3 {
  ...
}

We would merge the second ruleset with the selector .selector-1 into the first one that uses the same selector:

.selector-1 {
  ...
}

.selector-2 {
  ...
}

.selector-3 {
  ...
}

It’s a relatively simple rule to follow which I’ve been applying, almost instinctively, for years. After all, in computer programming avoiding repetitions is one of the most common rules we follow to help organize our code reasonably.

However, one common problem I face when writing CSS like this is that declarations that belong to different selectors are sometimes so closely related that having them separated makes the code hard to work with. If I change one of the declarations, I really should check the other one —but I might not find it, or even know it exists. One typical case is layout-related declarations, like when building flexible layouts. One container element gets added a rule display: flex and a child of it gets flex-grow: 1. If these two declarations are written separately, and with other unrelated declarations in between, unexpected breakage and dead code quickly creep in. Colors are also a common example, as they are closely related to each other, and it might take a number of iterations to get them all right.

For this reason since some time I have started grouping CSS declarations by their shared purpose. A simple approach is to divide the CSS file into, for example, a layout section, a typography section, a color section, etc. This way, selectors get repeated more, yes, but in my opinion this is not really the kind of repetition that one should worry about in programming. Repeating entire code blocks? Yes, that’s not optimal. But repeating one single line many times over is another thing. Think of it like calling a function from several places —it’s just a reference. Having the function written many times would be bad, not this.

In my experience grouping code by purpose works quite well in programming in other ways as well. For example, when organizing project files it can be useful to do it with feature-based directories, rather than, for example, by file type. The idea behind is that what belongs together should stay together. This makes the code easier to understand and maintain. Another benefit is that the directory tree tends to have a quite balanced shape —not too many directories and not too many files in each.

Example pens:

I applied this approach when working on Spex.