In response to criticisms of CSS pre-processors

This post is more than 11 years old.

Posted at 06:00 on 08 May 2012

It turns out that not everybody likes CSS pre-processors. For some people, it’s a philosophical point (a bit like purist photographers who still insist on shooting print film on all-manual Leica cameras) but other people are scared of introducing an extra layer of abstraction.

Some people argue that the features of CSS pre-processors such as variables aren’t necessary if you write your CSS correctly, and indeed, there are design patterns that aim to reduce repetition and magic constants in vanilla CSS. One such example is Object Oriented CSS (OOCSS).

OOCSS sets down two main principles:

  • Separate structure and skin
  • Separate container and content

I won’t discuss these in any detail here (you can read about them elsewhere), but I’ll just give an example. Whereas with a CSS pre-processor, you might write code such as this:

@small: 12px;

.sidebar {
  background: #ccc;
  font-size: @small;
}

.permalink {
  border-top: 1px solid #ccc;
  color: #333;
  font-size: @small;
}
<div class="sidebar"></div>
<div class="permalink"></div>

in OOCSS, you would use a separate class called small instead of the variable:

.small {
  font-size: 12px;
}

.sidebar {
  background: #ccc;
}

.permalink {
  border-top: 1px solid #ccc;
  color: #333;
}
<div class="sidebar small"></div>
<div class="permalink small"></div>

There you go. Vanilla CSS. DRY vanilla CSS. There’s no need for a pre-processor after all, is there?

Is there?

There is one big problem here. You haven’t eliminated repetition altogether. You’ve just moved it from your stylesheet into your HTML. By avoiding named constants and moving your font size declaration into a separate class, you now have to add a reference to the small class everywhere in your code where you are using the sidebar class or the permalink class. For some classes in a large site, this can potentially be in dozens if not hundreds of places. Congratulations, you’ve just robbed Peter to pay Paul — and found out that he’s asking for ten times as much.

Another, more serious problem occurs if you need to change your existing class names in order to retro-fit OOCSS into an existing site. For example, you may need to replace this:

<button class="button">Ordinary Button</button>
<button class="submit-button">Submit button</button>
<button class="small-button">Small button</button>
<button class="small-submit-button">Small submit button</button>

with this:

<button class="button">Ordinary Button</button>
<button class="button submit-button">Submit button</button>
<button class="button small">Small button</button>
<button class="button submit-button small">Small submit button</button>

You see the problem here? If you are referencing any of the old class names, such as small-button, in JavaScript anywhere (think: jQuery selectors), that code will break. On a complex web application, this can be a high-risk refactoring, requiring changes in potentially dozens of places.

“When a single change to a program results in a cascade of changes to dependent modules, that program exhibits the undesirable attributes that we have come to associate with ‘bad’ design. The program becomes fragile, rigid, unpredictable and unreusable.” — Robert C Martin

Now don’t get me wrong here. I don’t think OOCSS is necessarily a bad thing. It is well worth considering as a framework for new projects. But it can be pretty tricky to retro-fit it to an existing website.

Advanced features.

Some people say that features such as mathematical expressions are not necessary, because you can easily use comments instead to document your thought processes. For example, rather than using this:

.contents {
  width: @site-width – (@sidebar-width + @gutter-width);
}

you can do something like this:

.contents {
  width: 600px; /* site width less combined widths of sidebar & gutter */
}

This is a very strange argument indeed — the practice it promotes has been universally condemned as an antipattern in every other programming discipline from the 1960s right through to the present day. Even more strange is the fact that one of its proponents is none other than Bert Bos, the former chairman of the W3C CSS Working Group, who considers symbolic constants in CSS to be harmful. This is a bit like the head of the General Medical Council telling us that using disinfectant in hospitals is harmful.

Again, the problem here is when you need to make a change. Let’s say that you wish to change your site width from 800 to 900 pixels, for example. Using this approach, you would have to recalculate potentially dozens of values throughout your stylesheets, and on top of that, you couldn’t use search and replace: you would have to do it manually, drastically increasing the risk of making a mistake. By contrast, an expression-based approach allows you to try out different widths safely by changing only one or two values at most.

Another problem comes when you are trying to add new features to your site. With a comment-based approach, you will need to hunt through your entire stylesheet to find the values you need to make the calculation. With an expression-based approach, on the other hand, you can just use IntelliSense, and don’t even need to know what the exact values are.

What about leaky abstractions?

The problem with the concept of leaky abstractions is that it can be used to argue against anything, since all abstractions are leaky to some degree or another. The important question is to what extent the value added by the abstraction outweighs the potential problems introduced by the leaks.

It seems that the biggest fear of CSS pre-processors is what effect they will have on the size of the generated stylesheets and on performance, or whether they’ll make debugging harder because what shows up in the browser isn’t what you edit. Oddly enough, people who express these fears are usually more than happy to use all sorts of technologies to pre-process their HTML, such as PHP, or ASP.NET, or even XSLT. In fact, CSS pre-processors often do a better job of things, since they pretty-print the resulting output whereas PHP and ASP.NET don’t. If you’ve ever tried to wade your way through generated HTML with nonsensical indentation and lines thousands of characters long, you’ll know exactly what I mean. One thing I would say about this, however, is that it’s better to run your pre-processor on the server rather than on the client, since that way you are able to view the generated CSS fairly easily.

In practice, I’ve found that the improvements elsewhere far more than make up for the friction introduced by the transformation step. Furthermore, in combination with a good organisational strategy (organise your class nesting to mirror the structure of your HTML documents), they can actually reduce your dependence on Firebug for sorting out CSS issues, since it’s easier to identify and eliminate conflicts between poorly specified class declarations in your source itself.

Personally, I think concerns about the size of your generated CSS are overblown. Yes, your generated stylesheets can grow quite a bit if you’re not careful, but the best way to tackle that is to use HTTP compression, and since most of the size increase you get from CSS pre-processors is in the form of low-entropy, repetitive data, it compresses very well. That doesn’t give you carte blanche to ignore file sizes altogether of course (HTTP compression isn’t available in all cases: buggy browsers and/or misconfigured proxy servers can stop it from happening about 5-10% of the time) but as long as you are aware of what causes the most bloat (mixins), and take a little bit of care, you’ll be fine. Maintainability versus performance is a trade-off that you have to make at every level of your code, not just this one, so it’s best to fine tune things here (and CSS pre-processors make fine-tuning of this nature fairly easy) only in response to known, measurable performance issues.

“Premature optimization is the root of all evil” — Donald Knuth

In conclusion.

CSS as a language has some pretty severe limitations which make it very difficult to avoid bad programming practices such as magic numbers and DRY violations, and which make your stylesheets very fragile in the face of changing requirements. While there are design patterns that can alleviate the problem, they are not in and of themselves a complete solution, and even if you do use techniques such as OOCSS, a pre-processor will still be necessary if you want your stylesheets to be easy to maintain and easy to refactor, especially if you are working with legacy code.

Of course, there are potential gotchas with CSS pre-processors, but the same can be said of any technology, and in this case none of them are deal-breakers by any stretch of the imagination: they are far from insurmountable, and the benefits far more than make up for them. Having researched the alternatives, my position on the matter is unchanged: if you’re not using a pre-processor to keep your CSS under control, you’re doing it wrong.