🤯 50% Off! 700+ courses, assessments, and books

How to Build Your Own CSS Preprocessor With PostCSS

Craig Buckler
Share

Use a CSS preprocessor for a while and life soon becomes imaginable without it. Many developers have adopted LESS, Stylus or — the current favorite — Sass. Ideally, you should also be post-processing your CSS using Autoprefixer to add vendor prefixes when required without resorting to mixins or a frameworks such as Compass.

Autoprefixer is a plugin for PostCSS; a tool which translates CSS into an object model which can be used to manipulate styles. There are many more plugins available which implement preprocessor-like features such as variables, mixins and nesting.

Ben Frain’s recent article, Breaking up with Sass: it’s not you, it’s me, raises an interesting proposition. Do you really need Sass, LESS or Stylus given you can craft the CSS preprocessor you want using PostCSS?

A basic Sass-like parser can be created within your build process. For this example, I’ll use a Gulp.js task file but the same concepts apply to Grunt or any other build system. First, we install PostCSS and the plugins we need using npm, e.g.

npm install gulp-postcss postcss-mixins postcss-simple-vars postcss-nested autoprefixer-core --save-dev

then create an array of CSS processing functions in gulpfile.js, e.g.

var
	gulp = require('gulp'),
	postcss = require('gulp-postcss'),
	processors = [
		require('postcss-mixins'),
		require('postcss-simple-vars'),
		require('postcss-nested'),
		require('autoprefixer-core')({ browsers: ['last 2 versions', '> 2%'] })
	];

and write a CSS processing task, e.g.

// compile CSS
gulp.task('css', function() {
  return gulp.src('source/css/styles.css')
    .pipe(postcss(processors))
    .pipe(gulp.dest('dest/styles/'));
});

You can also create your own processing functions using the PostCSS API. For example, we could apply a sans-serif fallback for all font-family declarations:

processors = [
		require('postcss-mixins'),
		require('postcss-simple-vars'),
		require('postcss-nested'),
		function(css) {
			// sans-serif fallback
			css.eachDecl('font-family', function(decl) {
				decl.value = decl.value + ', sans-serif';
			});
		},
		require('autoprefixer-core')({ browsers: ['last 2 versions', '> 2%'] })
	];

If the file /source/css/styles.css contained this code:

$colorfore: #333;
$colorback: #fff;

@define-mixin reset {
	padding: 0;
	margin: 0;
}

main {
	font-family: Arial;
	@mixin reset;
	color: $colorfore;
	background-color: $colorback;
	
	article {
		color: $colorback;
		background-color: $colorfore;
	}
}

running gulp css would create this CSS in /dest/css/styles.css:

main {
	font-family: Arial, sans-serif;
	padding: 0;
	margin: 0;
	color: #333;
	background-color: #fff;
}

main article {
	color: #fff;
	background-color: #333;
}

The Advantages

PostCSS frees you from the limitations and choices imposed by preprocessor authors. The approach offers several benefits:

  • Modularity
    You need only add the plugins and functions you require for your project.
  • Lightweight
    Preprocessors are increasingly large and sophisticated applications. You probably won’t want or use every feature but they’re still present. PostCSS reduces the bulk.
  • Immediate implementation
    Have you ever waited for something to become available in Sass, LibSass, LESS, Stylus or another preprocessor? You can now develop your own features using…
  • JavaScript functions
    Your CSS preprocessor uses JavaScript — a true programming language (despite what some people say!)

    Most preprocessor language constructs are basic. You’ll often see functions and mixins which are more complex and difficult to comprehend than the raw CSS they create. With PostCSS, facilities which will never be implemented in Sass/LESS/Stylus are available. You can open files, read from databases, make HTTP requests or create complex calculations with ease.

  • Enforce development policies
    Presume you wanted your team to avoid @extend declarations. It’s no longer possible for anyone to use @extend unless they add an extend plugin to the build process. That would be immediately obvious.
  • It’s fast — very fast
    The authors estimate PostCSS is 4 – 40x faster than an equivalent preprocessor. I suspect the gains are even higher if you only require a few plugins. The whole solution is built in JavaScript and there’s no need to jump into another library or language interpreter.

The Disadvantages

All good then? Unfortunately, PostCSS is not a perfect solution:

  • Increased complexity
    Your build process will become more difficult to manage.

    Adding Sass is typically a line or two of code but PostCSS requires more planning — especially since plugins must be called in a specific order. For example, @import in-lining should be resolved prior to parsing variables. But what if you have variables within your @import declarations? In some situations it may be necessary to call a plugin more than once.

  • A different syntax
    I initially attempted converting a small Sass project to PostCSS. Don’t even try! While I eventually succeeded, PostCSS plugins often use a slightly different syntax, e.g. @define-mixin rather than @mixin. This could lead to confusion and numerous code updates. Part of the reason…
  • PostCSS requires valid CSS
    Most preprocessors parse plain text files so any syntax is theoretically possible. PostCSS creates an object model so it requires syntactically-correct CSS from the start. Even a // single line comment can cause it to fail. You could pre-process your CSS files before passing to PostCSS but then you’re back to using a preprocessor!

Should You Abandon Your Preprocessor?

A custom PostCSS processor could be an attractive option if you’re in a one-person team embarking on a small, relatively simple self-contained project.

I also recommend PostCSS for any genuine post-processing tasks such as vendor prefixing, packing media queries into a single declaration, reducing calc() equations, applying fallbacks for older browsers, supporting CSS4 selectors, minification etc. There’s little benefit doing that work yourself.

However, Sass has achieved critical mass. No preprocessor syntax is perfect but it’ll be understood by the majority of developers in your team. Anything subtly different is unlikely to offer significant benefit or appeal to everyone.

That said, PostCSS and the similar Rework framework have enormous potential. If a modular CSS plugin system could replicate — and even mix — the syntax and functionality we want from Sass, LESS and Stylus, we would have a single preprocessor to beat all others. You can bet someone, somewhere is working on that project now…

Have you successfully used PostCSS as a preprocessor for your project? Has it enticed you away from Sass? Are you going to move on from LESS? Will you give up on Stylus?