CSS pre-processors: good or evil?

I’ve been debating whether I think CSS preprocessors are a good thing or not, so I wrote this in order to make my mind up.

I’m pretty old fashioned, so I’d resisted pre-processors until quite recently. But after working on a project built with Sass I found that I liked it a lot; just not for the reasons I thought I might.

It’s not all sweetness and light of course (I’m still a jaded cynic). Here are some of the arguments I’ve been having with myself recently, starting with the nice bits.

1. A CSS runtime changes the game

Although the CSS you export is inherently static, a [sort of] runtime environment means you can export various versions of CSS according to pre-determined conditions. Of course you could generate CSS using any scripting language, but doing this pseudo-natively (in a superset of CSS itself) is just a great fit.

Case and point: For a recent project I generated entirely separate, standalone stylesheets for IE8 and IE7 from a single codebase. This is what I ended up with. There are other ways to achieve this of course, but I had another reason. Using this approach you can write “mobile first” CSS rules without serving the mobile version to old browsers. Great explanation of the technique here. This was the clincher for me in seeing Sass as an important asset to my code deployment.


2. Easier to get away with bad code

One great advantage of CSS pre-processing is that you can make amendments less of a pain. An obvious example would be: Your client asks for a colour change, so you alter the colour value in one place and you’re done. Hooray for you!

Nothing bad about that in itself . But consider this code:

.about-us-page .side-bar .blue-link {
    color: $blue; 
    @include bigtext;

This is a fictional example, but not a million miles away from a codebase I recently inherited. This code is bad because the emphasis has shifted to making the parameters maintainable for the developer’s own benefit, but it has disregarded how they might be applied in other contexts, and so how maintainable and useful the code is in general.

If you were forced into writing Ye Olde CSS you might stop and think about how you could avoid referencing the same styles all over the place. This might mean you come up with a better structure that’s more maintainable and legible in other ways too.


3. What do cross-compilers mean for industry standards?

I had just added Sass to my proverbial CV (after choosing it over LESS) then today I came across Roule. It looks great too, but is this choice a good thing, or does it just fragment skillsets and create Jacks of all trades?

I don’t think this is as simple as choosing between jQuery or Mootools. I think it’s more akin to choosing between PHP or Python, because it affects the development environment you need in order to work on any given project. Project leads will have to start specifying how they want CSS delivered, otherwise there’s a danger that front end developers will hand over work built with whatever syntactic sugar is their flavour of the month.

Personally I’d prefer to wait for Sass to add features than simply start using Roule, or whatever pops up next week.


4. Is too much abstraction bad for you?

Web development is chock full of abstractions. Usually it’s a good thing, but how much is too much? The Law of Leaky Abstractions is a thought-provoking read on this topic.

If you know how to write $('#stuff').slideDown() you have written JavaScript, but all you actually needed to know was the jQuery API functions that it’s using. Behind that snippet is a lot more code. If you don’t understand what it’s abstracting then you’re only getting by.

I don’t think CSS pre-processors are particularly bad offenders in this department. This is largely because they don’t really simplify CSS. In some aspects they are more complicated than CSS, but they are still abstractions and so I would never recommend a beginner pick up Sass without first knowing CSS.


To summarise

On the up side: I love the extra power of dynamically generated CSS. I like that it can be completely decoupled from the back end and from deployment, but that it can be completely integrated if that’s what you need.

On the downside: I think that preprocessors can be abused by lazy developers. They don’t encourage bad habits as such, but they make it easier to disregard good habits. I’d also like to see a standard abstraction emerge from all the current choices and have that implemented for various runtimes.