PostCSS – Sass Killer or Preprocessing Pretender?

PostCSS first came onto my radar back in February when I read Ben Frain’s excellent article – Breaking up with Sass: it’s not you, it’s me. I was extremely happy with my workflow at the time – using Sass for all of my preprocessing needs – but it piqued my interest in PostCSS enough to make sure that I filed it away in the 'must investigate later' part of my head (one that seems to be constantly overflowing).

Upon joining JUST EAT back in May, my first task has been to review and potentially redefine our front-end processes going forward. As part of this review, I thought it was time to take a deeper look into PostCSS and what it has to offer.

So is PostCSS the future of preprocessing? Here’s my thoughts on the subject.


N.b. PostCSS has been mooted previously as a postprocessor. However, like Chris Coyier concluded in his recent article on the topic, I will be referring to PostCSS as a preprocessor rather than as a postprocessor, as it processes custom code into CSS via a build step, which to me is still preprocessing – i.e. processing that happens before it is compiled into the CSS that your browser will then evaluate.

I will also refer to Sass by way of a comparison to PostCSS, as that’s simply what I currently use. See these references as also being applicable to similar tools such as Less and Stylus.


What is PostCSS

For anyone unfamiliar with PostCSS, it’s a tool that helps to provide feature extensions when writing CSS, not wildly dis-similar to preprocessors such as Sass, Less and Stylus. It does however have some very distinct differences to these other tools.

A traditional preprocessor like Sass gives you a whole bunch of functionality all bundled into one tool, irrespective of whether you need or will use all of those features.

On the flip side, PostCSS is a blank slate; you can add as many or as few features to your process as you require.

These features come in the shape of PostCSS plugins. Think of these like using LEGO, where each piece is a different feature that can transform your CSS in some way. PostCSS lets you stick together these pieces so that you can build up your own feature set, adding and removing plugins as and when you need them.

So for example, if you only want the ability to use CSS Variables and Nesting when writing your CSS, you can add a plugin for each of these features to your PostCSS build to start using them.

Another example is Autoprefixer, the popular prefix-management tool. This is actually a PostCSS plugin that has been made more accessible through the creation of Grunt and Gulp packages. These packages have now been superseded by grunt-postcss and gulp-postcss, through which you can use Autoprefixer in your PostCSS build.

Why is this useful?

Good question! So being able to add and remove features in this way is useful for a couple of reasons:

I. Because developers can do stupid things sometimes

I love using Sass. It’s a hugely powerful tool that gives me features that I’d genuinely find it hard to live without.

The problem is, not everyone uses it responsibly.

I’ve written about this in the past, but I’ve seen developers do some really dumb things when using Sass that they (probably) wouldn’t do when writing vanilla CSS. The @extends feature in particular can cause a whole world of pain when used improperly, but even something as simple as nesting in some peoples hands can reap horrible consequences.

For example, at JUST EAT, anyone across a whole group of teams can edit our projects CSS files – front-end devs, .NET devs, anyone. This is great, but not everyone is at the same level when it comes to CSS best practices and standards. We have code reviews to help mitigate this, but by throwing Sass into the mix, it opens up a whole bunch of extra functionality that some of our team may not fully understand how to use. The possibility of a developer writing some Sass that produces a whole bunch of unnecessary CSS when compiled therefore increases.

Being able to limit the features that are available when writing CSS on your project could therefore be incredibly useful.

II. Extensibility

If you wanted to extend the functionality of a preprocessor like Sass, it’s not simple. The codebase for projects like this are quite large and you’d have to understand what’s going on under the hood before trying to contribute to that project.

Even once you’ve written your extension and made a pull request, there’s no guarantee that your feature will be accepted and implemented into the codebase, as the maintainers might not want that functionality as part of the base feature set.

As PostCSS is made up of plugins, extending it is much easier. You can simply write your own plugin to transform your CSS in the way you require and add it into your PostCSS compilation step. You can then make that plugin available for others to use on their own projects if you wish.

What else can it do?

One feature that the PostCSS team has been keen to highlight is that you can start writing CSS with respect to new specifications right now by using plugins like cssnext.

However, much like Ben mentioned in his article and similarly Chris Coyier went into in some depth recently, I’m not convinced that people should be developing with this in mind. Specifications can change many times before they become implemented and so there seems little value in learning to write CSS with respect to these until they become more stable.


How fast is it?

If speed of compilation floats your boat, PostCSS is also incredibly fast – apparently around 3x faster than libsass and 4x faster than Less. Whether that speed is noticeable in your build process probably comes down to the amount of CSS you’re working with; I’d imagine for most users the difference in speed would be barely noticeable (as we’re talking hundredths of milliseconds).


Ease of setup

If you’re used to setting up Grunt or Gulp tasks, getting to grips with PostCSS will be a breeze.

The only trickery can come when specifying the order that your PostCSS plugins run in. Some plugins are required to run before others will work as intended – such as transforming and applying CSS variables before being able to run conditional plugin transformations that involve variables. This is an obvious case, but others take a little more thinking about.

On the whole though, setup was a relatively painless experience.


Can I convert my project from Sass/Less/Stylus into PostCSS?

PostCSS isn’t designed to replace your current preprocessor like-for-like; It’s designed so that you can extend the functionality of standard CSS. Although it has plugins that can replicate other preprocessor features, trying to directly convert can be a painful experience.

Not all features of your current preprocessor will be replicated by PostCSS plugins and so if looking to make the switch, it’s important to research into what functionality you still require.

With the upcoming release of version 4.2 of PostCSS, you will be able to specify custom parsers, such as being able to parse SCSS. This will likely help when being able to move projects over to PostCSS – as it should open up more possibilities for future plugins – but you will still need to make some adjustments to your projects.


Plugin Support

A tool like PostCSS is only as good as the quality of it’s plugins, so how does it fair in this regard?

Well for a tool that’s still relatively young, it’s pretty good. A full list of plugins can be found on the PostCSS Github Repo.

Sass-type features

As a current user of Sass, I wanted to make sure that there were plugins that could provide the functionality I couldn’t bear to be without.

Variables are one of the pillars of writing maintainable CSS and the postcss-simple-vars plugin let’s you use them in almost the exact same way as you would do in Sass.

Nesting – via postcss-nested – is also easy to add, and conditional functionality, like @if, can be taken care of using the postcss-conditionals plugin.

Importing files using the postcss-imports plugin is also possible, but is slightly less slick, defining partials in full, such as:

@import '_variables.css';

Using mixins is slightly more hit and miss. Currently, the only plugin available – postcss-mixins – gives you a less-than intuitive syntax to work with – to me anyway, you can decide for yourself:

@define-mixin icon $name {
    padding-left: 16px;
    &::after {
        content: "";
        background-url: url(/icons/$(name).png);
    }
}

.search {
    @mixin icon search;
}

To me, it would have made more sense to have followed a more familiar convention to those used in other preprocessors – using brackets to separate out the arguments of each mixin. I could author my own plugin to do this I’m sure, but it’s disappointing to have to do this for such a key feature many developers will want to utilise.

Less important to me personally are Sass features like @each, @for and @extend, but plugins for those are all present too via postcss-each, postcss-for and postcss-simple-extend.

Things Sass can’t do

So what PostCSS plugins are currently available that give you features that preprocessors like Sass can’t?

Autoprefixr is probably the most widely known PostCSS plugin and is a tool that I think every developer should be leveraging. It let’s you write your CSS unprefixed – such as transition: 250ms ease-in; – along with defining the browsers you intend on supporting. It then adds the necessary prefixes upon compilation. This helps keep your CSS clean of prefixes and makes fallbacks more maintainable and less susceptible to redundancy.

Another useful plugin is css-mqpacker which joins together multiple matching media query declarations into a single statement; a massively useful optimisation.

There are a whole host of plugins with unique features that you may want to leverage and I expect this list to grow quite quickly as the uptake of PostCSS gathers pace.

Is writing your own plugin easy?

Yes, but some edge cases aren’t possible (yet).

There are some excellent docs on writing your own PostCSS plugin, along with associated guidelines and a boilerplate.

So I went ahead and tried to write my own.

Single line code commenting is a small but important feature that Sass gives me, letting you comment code that doesn’t get compiled into your CSS, such as:

// standard button class
.btn { … }

Obviously in plain old CSS, this isn’t possible. Comments are instead written:

/* standard button class */
.btn { … }

PostCSS has a plugin that helps give you similar functionality – postcss-discard-comments – but I wanted to replicate the Sass-style syntax, as I find it slightly more flexible.

However, I hit a bit of a roadblock. // my comment is invalid CSS code and so when running the PostCSS parser over it, it fails due to the/ character being invalid. As a result, my plugin never got to transform the code as I wanted, as it never got that far.

This situation will shortly be resolved by being able to specify a custom parser – something that is due with the release of PostCSS v4.2.


Considerations

As much as there is to like about the freedom PostCSS gives you, there are a couple of small cracks that need filling in before I think it will gain widespread support.

For a tool that is based solely on the strength of it’s plugins, it’s going to need to make finding and discovering the most useful plugins easier than simply looking through a list on it’s repository.

In this way it shares characteristics to Sublime Text, which goes to great lengths to make this process easier through it’s package control website. Similarly, jQuery plugins are made more accessible through it’s plugin repository.

Without a similar solution, it’ll become increasingly difficult to find plugins that genuinely offer key functionality. There are already upwards of 100 plugins listed on the PostCSS repository. Realistically, most developers aren’t going to trawl through that list to find the diamonds in the rough that apply for their project.

Parsing Custom Syntax

As mentioned in the section above on writing custom plugins, currently it’s not possible to parse, and therefore transform, syntax that isn’t 100% valid CSS.

This issue is currently being worked on by the PostCSS team and, as of the upcoming v4.2, you will be able to specify custom parsers which will resolve this issue. This will open up the ability to be able to parse SCSS syntax making PostCSS a lot more flexible.

Shortcuts

This isn’t so much a gripe with PostCSS, or something that will stop its growth, but it is an issue that PostCSS facilitates simply by giving developers so much freedom when transforming CSS in any way they like.

Writing plugins to transform your own shortcut syntax is a terrible idea on many levels. For example, the postcss-verthorz plugin let’s you write the following:

.foo {
    padding-vert: 2rem;
    margin-horz: auto;
}

.bar {
    ph: 30px;
    mv: 100px;
}

// converts to
.foo {
    padding-top: 2rem;
    padding-bottom: 2rem;
    margin-left: auto;
    margin-right: auto;
}

.bar {
    padding-left: 30px;
    padding-right: 30px;
    margin-top: 100px;
    margin-bottom: 100px;
}

The problem with this, and other plugins like postcss-position, is that it is complicating what should be uncomplicated – standard CSS definitions. They are creating as much of a problem as they are solving.

Shortcuts like this should be utilised in text editors, not hard-coded ready to confuse future developers that work on the project.

I would hope that developers choose not to use PostCSS plugins to transform their CSS in this way, as it ultimately just fragments standard syntax more than is necessary. But it does highlight the danger in giving people so much freedom – people can use that freedom to create things that arguably make their CSS less maintainable, rather than more so.


Conclusion

PostCSS has absolutely amazing potential.

It truly offers a different way of thinking about processing CSS – one that many developers will embrace as an alternative to what is currently available.

Having the power to customise the way in which you can transform your CSS by being able to choose the plugins that you use is really empowering. It means you can introduce new features to your team at a speed you’re all comfortable with, rather than throwing a whole treasure chest of features into the mix and seeing what happens.

As should be remembered when adopting any new tool however, there are going to be some edge cases that need to be ironed out. Ensure you thoroughly research whether PostCSS will give you all of the things you need it to before deciding on whether it is ready to use in your workflow.

At JUST EAT, we’ve added PostCSS to our workflow, but currently only to provide additional functionality that Sass can’t, such as Autoprefixer. I envisage that we will gradually shift over to using PostCSS as the ability to write more flexible plugins that can transform parsed SCSS files becomes available. For our team it makes sense to keep a syntax that the majority of our developers feel comfortable writing – at least until PostCSS has become slightly more mature.

But that doesn’t mean PostCSS isn’t right for your team or project – I would fully encourage you to take a look yourself at what PostCSS can offer you. If PostCSS continues to develop and improve at the rate it is right now, it’s likely to be a part of many front-end developers workflows in the near future.