A List Apart

Lyza Danger Gardner on Building the Web Everywhere

What Will Save Us from the Dark Side of CSS Pre-Processors?

Writing CSS by hand for a site or app of any considerable size seems quaint these days, in the way that shaping a piece of wood with an adze seems quaint. Admirable, perhaps, but even if it gives you a tangible connection to the exact outcome, the vestigial quirks, limitations, and tedium of that workflow make it feel archaic.

Article Continues Below

Until a few years ago, this direct method was our only real option. We managed CSS by hand, and it got complicated and crazypants. So when pre-processors started showing up—Sass, LESS, Stylus—we clutched at them, giddy and grateful like sleep-deprived parents.

Pre-processors to the rescue!

Their fans know that pre-processors do a lot of stuff. Variables, functions, nesting and calculations are part of the pre-processor assortment, but there’s often also support for concatenation, minification, source maps, output formatting. Sass feels like an authoring tool, framework, configuration manager, transform and build tool in one. They’ve become very popular—especially Sass, the juggernaut. Huzzah! Such power!

Pre-processors… to the rescue?

Yet as with other powerful things, Sass has a dark side. Its potential malevolence tends to manifest when it’s wielded without attention or deep understanding. In the recent article A Vision for our Sass, Felicity Evans points out some of the ways unmindful use of Sass can result in regrettable CSS.

Pre-processors have a way of keeping us at arm’s length from from the CSS we’re building. They put on us a cognitive burden to keep up on what’s evolving in CSS itself along with the tricks we can pull off specific to our pre-processor. Sure, if we’re intrepid, we can keep on top of what comes out the other end. But not everyone does this, and it shows.

Overzealous use of the @extend feature in Sass can create bloated stylesheet files bobbing in a swamp of repeated rules. Immoderate nesting can lead to abstruse, overlong, and unintentionally overspecific selectors. And just because you can use a Sass framework like Compass to easily whip up something artful and shiny, it doesn’t guarantee that you have any sort of grip on how your generated CSS actually works.

Pre-processors… FTL?

Diabolical output is one risk, and yet there are additional ways pre-processors can trip us up.

Working with a pre-processor means writing source in its Domain-Specific Language (DSL). (You could also pen your source using entirely vanilla CSS, but that would be pretty pointless, as the power of pre-processing comes from operations on variables, mixins, and other features written in their particular syntax.) You feed this source to the pre-processor and out comes CSS ready for browsers. You couldn’t take your source and use it in a browser. It’s not ready yet.

That means that the source is not entirely portable. So choosing a particular pre-processor may be a long-term commitment—Sass and other pre-processors can create a certain amount of lock-in.

On a conceptual level, the breadth of pre-processors’ scope is significant enough that it can insinuate itself into the way we think and design. In that sense, it’s not a tool but a system. And this can get under the skin of people—especially devs—who thrive on separation of concerns.

This is beginning to sound like an argument to ditch Sass and its brethren and return to the homespun world of handcrafted CSS. And yet that is a false dichotomy: pre-processors or nothing. There are other tools for managing your CSS, and I’m especially hopeful for a new (ish) category of tools called post-processors.

Post-processors to the rescue!

In contrast to pre-processors’ distinct syntaxes, post-processors typically feed on actual CSS. They can act like polyfills, letting you write to-spec CSS that will work someday and transforming it into something that will work in browsers today. Ideal CSS in, real-life CSS out.

You may already be using a post-processor alongside your pre-processor without being aware of it. The popular autoprefixer tool is in fact a post-processor, taking CSS and adding appropriate vendor prefixes to make it work in as many browsers as possible.

Many post-processors—especially those written with a plugin approach—do only one specific thing. One might polyfill for rem units. Another might autogenerate inline image data. You can pick and choose the modular plugins you need to transform your CSS.

Post-processors typically edge out their pre- brethren in build-time speediness.

And because they can be used as modular chunks, they can serve a balm for the aforementioned separation of concerns violations.

With this kind of authoring, we have a built-in necessity to stay current on the way new specs express themselves in actual CSS syntax, and that means post-processors’ transformations aren’t as inscrutable. Plugin authoring, too, is pegged to the same specs. Everyone is marching to the same, standards-driven beat.

This is starting to feel like outright post-processor boosterism, isn’t it?


Post-processors… to the rescue?

The case for post-processors isn’t entirely coherent. There isn’t even any consensus about the definition. The way I’m explaining post-processors is my own interpretation. Don’t take it as gospel.

Real-world implementations don’t help to clear the picture, either. Several modules written using postcss, a JavaScript framework for post-processors, involve custom syntax that doesn’t align with the definition I’m outlining here (valid CSS in, valid CSS out). By my definition, myth.io would be a post-processor, but is described by its maintainers as a pre-processor. Maybe post-processors aren’t even a thing, or only exist in my fevered, idealistic imagination.

Post-processors may hold more appeal to certain members of the web-building audience. The appeal of shaving some milliseconds off build has more clout with some than others. Modularity is one thing, but pre-processors can do so many things. It’s hard to wean from something that serves us so well.

Taking a path paved with lean, modular post-processing plugins involves sacrifices. No more nesting. Instead of an endless horizon of mixin possibilities, you may be bound to CSS spec realities, like calc or CSS variables. One promising framework for rolling out post-processing plugins is postcss, but it’s young yet and its documentation is in a correspondingly awkward adolescent phase.

Knowing your craft to the rescue!

Remember that thing I said earlier about false dichotomies? Gotta remember that, because pre- and post-processors aren’t mutually exclusive.

We happily use both in my office. Some of our more dev-y designers have taken a shine to the post-processing philosophy, while other designers remain pleased with the all-in-one, intuitive oomph Sass gives them. Both are right.

Though each camp might have a different approach to tools, the important commonality they share is a deep understanding of the CSS that comes out the other side. Neither set of tools is a crutch for ignorance. Know your craft.

Although there are different ways to get there, a thoughtful understanding of CSS is a prerequisite for continued success in building great things for the web. Whether you’re one to meditatively chip with an adze along a raw CSS stylesheet or you prefer to run it through a high-tech sawmill, you’re always better off understanding where you’re starting from and where you’re trying to go.

24 Reader Comments

Load Comments