We all know what it’s like: you start off with the best of intentions—to write neat, sustainable CSS using all of the very latest fashions (DRY, OOCSS, etc.)—but then the clock is ticking and you find yourself rushing the project and producing a stylesheet that, whilst not totally awful, still resembles spaghetti.
This is a problem that only becomes compounded when you are writing Less: here the problem becomes one of lazy nesting and, next thing you know, not only do you have selectors that are 80,000 characters long but you have also “cemented” certain elements—you know that if the customer moves that slideshow from that position on that page, then the whole thing is going to fall apart.
Leaving aside the special considerations that you need to bear in mind when implementing designs onto a large CMS (which I intend to address in another post), writing sustainable CSS is absolutely essential to a web designer. The simple fact is that sites with well-written CSS will load faster, be easier to maintain (essential if your customers are regarded as partners), be more flexible (from a CMS point of view) and be generally more elegant.
Introducing SOLID Principles to sustainable CSS
There are, of course, many opinions as to what makes sustainable CSS: however, one of the best articles that I have read on the subject is this post on SOLID principles by Miller Medeiros.
SOLID is an acronym for five basic principles of object-oriented programming and design that when applied together can make systems easier to maintain and to extend over time. The term was coined by Uncle Bob and I’m pretty sure he didn’t had CSS in mind(although I didn’t asked him).
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Now, this all sounds a bit too techie for me, but Miller goes on to explain the principles in rather more detail.
Single Responsibility Principle
This principle states that an object should have only a single responsibility, and that responsibility should be entirely encapsulated by the object. When applied to CSS this means that we should separate structure from presentation, instead of creating a single class and/or element that does both, we should decouple them so you can change the skinning of the components without affecting the structure.
CSS rules should have a high cohesion, you shouldn’t normally see too many different kinds of rules grouped together. You shouldn’t have text styles + borders + colors grouped with dimensions + positioning and floats.
By doing so we can reuse the same modules inside a different container and increase the chance of reuse. It will also make maintenance easier since you can change the skinning without affecting the structure and can toggle the visual easily based on the context (eg. element with same structure but different colors).
This principle sounds sensible enough—and its particularly easy to address when using CSS pre-processors—but, in fact, it is usually the very first method to go out of the window when you are under pressure to deliver.
“software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”
That means that you should make it easy to overwrite/extend the base rules and should avoid editing the base classes.
You should only edit a base class if you are fixing an error, never add new behavior, otherwise you might introduce conflicts. That means that you should not change your
reset.css file or edit rules that affects multiple elements after the project is stable. Create a new class that augments it instead of editing it.
This is an easy principle to stick to, since it can save a lot of time: with the capabilities of Less, I quite often find myself writing things like this:
// Dark green gradient, with highlighted edges
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,@color1), color-stop(100%,@color1-dk));
box-shadow:inset 0 1px 1px rgba(255,255,255,0.5);
border:1px solid darken(@color1,50%);
text-shadow:0 1px 1px rgba(0,0,0,0.8);
// Extend .green-gradient for menu items
In Sass, you can use the
@extend class to do this even more sustainably. If you aren’t already doing this, then I heartily recommend that you adopt this methodology.
Liskov Substitution Principle
“objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program”
@extend others shouldn’t have a different behavior than the base class. You should be able to toggle between them without affecting the correctness of the app.
The idea behind this principle is that if you can swap Parent classes with their Children, without causing any undesired side effects, you will have a system that is more flexible and easier to understand. It will increase code reuse on cases that you couldn’t predict when the project started.
That means, modifiers should ideally affect only things that wouldn’t break the layout. If the base class doesn’t have a fixed height the child class shouldn’t have as well, the display and position also shouldn’t be changed.
Once again, this is a sensible principle that, again, often falls by the wayside.
Interface Segregation Principle
“many client specific interfaces are better than one general purpose interface.”
That means, it’s better to have multiple specific base modules than to have a single generic one. It will increase the cohesion of your modules and make code easier to refactor/change/maintain (since it reduces tight coupling).
Now, it has to be said that Miller does not seem to be entirely on board with this principle, or what it implies.
Sometimes it can be really tempting to create a base module that fits multiple scenarios trying to increase code reuse as much as possible. To be honest, lately I’ve been thinking that not all code reuse is a good thing. If you are doing it just for the sake of reusing(specially CSS) you might be increasing the coupling between modules without a need, that will make further changes harder to implement since it will cause a ripple effect to undesired modules (eg. changing the margin around an article in the homepage would also affect the margin around a blog post on the inner pages).
Which is fair enough.
Dependency Inversion Principle
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
I like to think this rule on the CSS domain means that the container shouldn’t care about the existence and the visual style of its children as long as it behaves how it expects it to behave. For an
.article component it shouldn’t matter how the
.title is styled, as long as the title doesn’t break the layout. We should delegate the styling of child components to their own modules and do it in a way that the child elements can be swapped without affecting the parent element. This is the core idea behind OOCSS.
Nested selectors are a sign that you might be breaking this rule. Instead of making nested selectors like
#sidebar .group > h3 you should create a new class
.group-title. That way it can be an
<div> or any other element you need. It will be easier to create new modules with slightly different styling/behavior by simply swapping the dependencies (child elements). This also favors composition over inheritance.
It also couples with an absolute principle that I advocate: that you should avoid targeting the mark-up element whenever possible—especially in these days of shifting HTML5 specifications!
I have been adhering to most of the SOLID principles for many years without really knowing about it, but this article is extremely good at articulating why I have been coding in the way that I have.
Over the years, I have had to dive into far too many website implementations and then recoiled when I have seen the state of the CSS (let’s leave aside the HTML for the moment).
These experiences have convinced me that one of the most important issues facing our company—if not the web as a whole—is the production of sustainable CSS.