PostCSS: The Latest Adventure with Sass

In recent times, http://postcss.org/ has become increasingly popular in the realm of front-end web development.

Developed by Andrey Sitnik, the mind behind Autoprefixer, PostCSS is a Node.js package designed to transform CSS using JavaScript. This allows for significantly faster build times compared to other processors.

Despite its name, PostCSS is neither a post-processor nor a pre-processor. Instead, it functions as a transpiler, converting PostCSS-specific syntax (or more specifically, PostCSS plugin syntax) into standard CSS.

It’s important to note that this doesn’t mean PostCSS and other CSS processors are incompatible. In fact, combining PostCSS with Sass can be particularly beneficial for those new to CSS pre/post-processing, offering a smoother experience, as we’ll explore later.

Think of PostCSS as a complementary tool, an addition to your existing toolkit, rather than a replacement for other CSS processors.

PostCSS overview illustration.

The use of PostCSS has skyrocketed lately, embraced by major players in the tech industry such as Twitter, JetBrains, and Wikipedia. This widespread adoption and success can be largely attributed to its modular nature.

A Plethora of Plugins

Plugins are at the heart of PostCSS.

PostCSS allows developers to choose the plugins they need, eliminating unnecessary dependencies. This provides a streamlined and efficient setup with the fundamental features of other preprocessors while also allowing for a more customized and efficient workflow.

At the time of writing, PostCSS boasts a collection of over 200 plugins, each serving a specific purpose. On the PostCSS’ GitHub repository, these plugins are grouped into categories such as “Solve global CSS problems,” “Use future CSS, today,” “Better CSS readability,” “Images and fonts,” “Linters,” and “Others.”

However, the plugins directory offers a more precise categorization. For a better understanding of their capabilities, exploring this resource is highly recommended. The range of functionalities offered by these plugins is quite extensive and impressive.

Among the most well-known is Autoprefixer, a popular standalone library that also exists as a PostCSS plugin. Another popular choice is CSSNext, which enables the use of modern CSS syntax, like custom properties, without the concern of browser compatibility issues.

However, not all PostCSS plugins introduce groundbreaking features. Some simply provide capabilities readily available in other processors. One such example is the parent selector. While Sass allows immediate use of this selector upon installation, PostCSS requires the postcss-nested-ancestors plugin to achieve the same functionality. This also applies to features like extends or mixins.

So, why opt for PostCSS and its plugins? The answer lies in the flexibility it offers - the ability to choose your battles. If your Sass usage is primarily limited to the parent selector, PostCSS and the postcss-nested-ancestors plugin can streamline your workflow, eliminating the need for a full-blown Sass library installation and compilation process.

This is just one scenario where PostCSS shines, and further exploration will undoubtedly reveal numerous other compelling use cases.

Getting Started with PostCSS

Let’s delve into the basics of PostCSS and its typical usage. While highly potent with task runners like Gulp or Grunt, PostCSS can also be utilized directly from the command line using postcss-cli.

Consider a straightforward scenario: adding fallback HEX values to all RGBA color formats using the postcss-color-rgba-fallback plugin.

After installing postcss, postcss-cli, and postcss-color-rgba-fallback via NPM, the following command can be executed:

1
postcss --use postcss-color-rgba-fallback -o src/css/all.css dist/css/all.css

This instructs PostCSS to apply the postcss-color-rgba-fallback plugin on the CSS within src/css/all.css and output the result to dist/css/all.css.

That was simple enough. Now, let’s explore a more intricate example.

Integrating PostCSS with Task Runners and Sass

Integrating PostCSS into your workflow is surprisingly straightforward. As mentioned, it seamlessly integrates with task runners like Grunt, Gulp, or Webpack and can even be used with NPM scripts. Here’s a snippet demonstrating PostCSS used in conjunction with Sass and Gulp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var gulp = require('gulp'),
    concatcss = require('gulp-concat-css'),
    sass = require('gulp-sass'),
    postcss = require('gulp-postcss'),
    cssnext = require('postcss-cssnext');
    
gulp.task('stylesheets', function () {
  return (
    gulp.src('./src/css/**/*.scss')
    .pipe(sass.sync().on('error', sass.logError))
    .pipe(concatcss('all.css'))
    .pipe(postcss([
      cssNext()
    ]))
    .pipe(gulp.dest('./dist/css'))
  )
});

Let’s break down this code example.

First, references to all required modules (Gulp, Contact CSS, Sass, PostCSS, and CSSNext) are stored in variables.

Next, a new Gulp task named stylesheets is defined. This task monitors files ending in .scss within ./src/css/ (irrespective of their subdirectory depth). These files are then compiled using Sass and merged into a single all.css file.

The generated all.css file is then passed to PostCSS for transpiling PostCSS-specific (and plugin) code into standard CSS. This final output is then placed in ./dist/css.

Setting up PostCSS with a task runner and a preprocessor is undoubtedly convenient. But does it justify adopting PostCSS in the first place?

While Sass boasts stability, maturity, and a large community, PostCSS offers plugins like Autoprefixer, which can be highly advantageous. Using Autoprefixer as a PostCSS plugin, as opposed to a standalone library, allows for easy integration of additional plugins down the line, avoiding excessive reliance on multiple JavaScript libraries.

This approach also enables the use of unprefixed properties, which are then prefixed based on values fetched from APIs like Can I Use. This is challenging to achieve using Sass alone and helps avoid complex mixins that might not be the best way to prefix code.

A common way to integrate PostCSS with an existing Sass workflow is to pipe the compiled output of your .sass or .scss file through PostCSS and its plugins. This results in a CSS file reflecting both Sass and PostCSS transformations.

For those using task runners, integrating PostCSS is as simple as adding it to the task pipeline after the Sass compilation step (or any other preprocessor you prefer).

PostCSS’s compatibility with other tools makes it a valuable asset, alleviating common pain points faced by developers.

Let’s illustrate this synergy between PostCSS (with plugins like CSSNext and Autoprefixer) and Sass with another example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
:root {
    $sass-variable: #000;
    --custom-property: #fff;
}

body {
    background: $sass-variable;
    color: var(--custom-property);

    &:hover {
        transform: scale(.75);
    }
}

This code snippet combines vanilla CSS with Sass syntax. Custom properties were still in Candidate Recommendation (CR) status at the time of writing. This is where PostCSS’s CSSNext plugin proves beneficial.

CSSNext transforms elements like custom properties into current CSS syntax. Similarly, the transform property will be auto-prefixed by the Autoprefixer plugin. The code snippet above would then result in:

1
2
3
4
5
6
7
8
9
body {
    background: #000;
    color: #fff;
}

body:hover {
    -webkit-transform: scale(.75);
            transform: scale(.75);
}

Creating Your Own PostCSS Plugins

PostCSS’s allure is further enhanced by its high degree of customization. Its open nature allows developers comfortable with JavaScript to readily create custom plugins tailored to their specific needs.

The PostCSS team provides comprehensive resources for plugin development, including a solid starting point. For those interested, their recommended articles and guides is a great place to begin. If you have any questions or need further clarification, Gitter is an excellent platform for discussions.

PostCSS boasts an active community on its API, with a large following on Twitter. This, along with the other community benefits highlighted earlier, contributes to a collaborative and enjoyable plugin creation process.

Creating a PostCSS plugin involves building a Node.js module. Conventionally, PostCSS plugin folders within the node_modules/ directory are prefixed with “postcss-” to signify their dependency on PostCSS.

To start, the index.js file of your plugin module should include the following code, which serves as a wrapper for the plugin’s processing logic:

1
2
3
4
5
6
7
var postcss = require('postcss');

module.exports = postcss.plugin('replacecolors', function replacecolors() {
  return function(css) {
    // Rest of code
  }
});

Let’s name our plugin replacecolors. This plugin will search for the keyword deepBlackText and replace it with the HEX color value #2e2e2e:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var postcss = require('postcss');

module.exports = postcss.plugin('replacecolors', function replacecolors() {
  return function(css) {
    css.walkRules(function(rule) {
      rule.walkDecls(function(decl, i) {
		var declaration = decl.value;
        if (declaration.indexOf('deepBlackText') !== -1) {
          declaration = color: #2e2e2e;;
        }
      });
    });
  }
});

This code snippet performs the following actions:

  1. Iterates through all CSS rules in the current .css file using walkRules().
  2. Iterates through all CSS declarations within the current .css file using walkDecls().
  3. Stores each declaration in the declaration variable and checks for the presence of the string deepBlackText. If found, the entire declaration is replaced with color: #2e2e2e;.

Once your plugin is ready, you can use it directly from the command line:

1
postcss --use postcss-replacecolors -o src/css/all.css dist/css/all.css

Or load it into a Gulpfile like this:

1
var replacecolors = require('postcss-replacecolors');

Should You Abandon Your Current CSS Processor for PostCSS?

The answer depends on your specific needs and preferences.

Using Sass and PostCSS together is a common practice, especially for newcomers. This approach provides a balance between familiar pre/post-processing tools and the advanced features offered by PostCSS plugins. It also avoids a complete overhaul of existing workflows reliant on specific processors, allowing you to maintain implementations using Sass mixins, extends, parent selectors, placeholder selectors, and so on.

Embrace the Power of PostCSS

PostCSS has emerged as a game-changer in front-end development. Its popularity stems from its unique approach as a flexible transpiler rather than a traditional pre/post-processor, enabling seamless integration with diverse environments.

Plugins are the driving force behind PostCSS’s capabilities. If modularity, flexibility, and a wide range of options are important to you, PostCSS is the ideal tool for the job.

For those using task runners or bundlers, integrating PostCSS into your existing workflow is generally straightforward. The installation and usage guides provide clear instructions on how to integrate PostCSS with your current toolset.

Many developers believe PostCSS is here to stay. Its potential to reshape how we structure CSS is immense, potentially leading to greater standardization within the front-end development community.

Licensed under CC BY-NC-SA 4.0