# PostCSS Deep Dive: Roll Your Own Preprocessor

Read Time: 18 min
This post is part of a series called PostCSS Deep Dive.
PostCSS Deep Dive: Preprocessing with “PreCSS”
Using PostCSS Together With Sass, Stylus, or LESS

In the previous tutorial we went through how to use the excellent preprocessing pack “PreCSS”. In this tutorial we’ll be approaching PostCSS-based preprocessing in a different way; installing a hand-picked selection of plugins to custom build our preprocessor from the ground up.

I'm going to take you through the setup of what I personally find to be a great mix of language extension plugins. But when it comes to you rolling your own preprocessor you might choose to use only some of the plugins we cover here, or you might choose none at all, instead going with other options.

That’s the beauty of this process; you can have your preprocessor setup however you choose. The purpose of this tutorial will be to give you hands on experience of putting together a PostCSS preprocessor, and to fill you in on the features of the currently available plugins so you can decide for yourself which you want to use.

Let's begin!

## Setup Your Project

The first thing you’ll need to do is setup your project to use either Gulp or Grunt, depending on your preference. If you don’t already have a preference for one or the other I recommend using Gulp as you’ll need less code to achieve the same ends.

You can read about how to setup Gulp or Grunt projects for PostCSS in the previous tutorials

respectively.

If you don't want to manually setup your project from scratch though, you can download the source files attached to this tutorial, and extract either the provided Gulp or Grunt starter project into an empty project folder.

Then, with a terminal or command prompt pointed at the folder, run the command npm install.

## Note on Plugin Installation

This tutorial will assume you‘ve followed the previous entries in the series and are now familiar with how to install a plugin into your project and load it via your Gulpfile or Gruntfile.

Important! As we go through, be sure to load the plugins into your Gulpfile/Gruntfile in the order you see in this tutorial; load order is important in PostCSS to keep everything running smoothly.

## Add Imports for Partials

The very first place we’re going to start with putting together our custom preprocessor is imports. You have already seen PostCSS inlining of @import stylesheets in the previous tutorials For Minification and Optimization and Preprocessing with PreCSS. The way imports will be used in this preprocessor is no different.

We just touched above on the fact that load order is important in PostCSS, and here we find the first example of this. We want to ensure all @import files are inlined as the very first step, so that we have all the code of our project in one place for the rest of our plugins to run against.

For example, we might store all our variables in a partial file, and use @import to bring that partial into our main stylesheet. If we didn’t run the plugin that inlines @import files first, our variables wouldn’t be imported and hence wouldn’t available for the rest of our processing to work with.

### First, Change Gulpfile Source File to “style.css”

Because we’re going to start importing partials, we want to make a little tweak to our Gulpfile before we add our import functionality.

Note: if using Grunt, you won’t need to make any changes at this stage.

Right now we’re having any “.css” file found in the “src” folder compiled, but we don’t want to accidentally compile partial files. We’ll be importing everything into our “style.css” file so it’s the only one that needs to be compiled.

Find this line:

 1  return gulp.src('./src/*.css') 

...and change it to:

 1  return gulp.src('./src/style.css') 

### Import Plugin Used:

This is the same plugin we used in the “For Minification and Optimization” tutorial, and that is also used in PreCSS, so you’ll be somewhat familiar with it at this point.

Install the plugin into your project, then in your “src” folder create a file named “_vars.css” and add some basic test code to it. Note we haven’t added variables functionality yet, so just some straight CSS, for example:

 1 .test {  2  background: black;  3 } 

Now import your new variables file into your main “src/style.css” file by adding this code at the first line:

 1 @import "_vars"; 

Compile your code, then check your “dest/style.css” file and you should see it now contains the code from your “_vars.css” file.

### Mixins Plugin Used:

Note: this plugin must be executed before the postcss-nested and postcss-simple-vars plugins, both of which we’ll be using.

Go ahead and install postcss-mixins, then add the following code to your “src/style.css” file:

 1 @define-mixin icon $network,$color {  2  .button.$(network) {  3  background-image: url('img/$(network).png');  4  background-color: $color;  5  }  6 }  7 8 @mixin icon twitter, blue;  After compilation your “dest/style.css” should have the following compiled code added to it:  1 .button.twitter {  2  background-image: url('img/twitter.png');  3  background-color: blue;  4 }  The postcss-mixins plugin we’re using here is the same one as used in PreCSS. We went over how to use it in the tutorial on PreCSS, so for full details on its syntax check out the “Mixins” section of the previous tutorial. ### Other Mixin Plugin Options: If you’d prefer to use Sass syntax when creating mixins, check out Andy Jansson's postcss-sassy-mixins plugin, which works in the same way as postcss-mixins but with the syntax @mixin to define a mixin, and @include to use one. ## Add “for” Loops ### “for” Loops Plugin Used: Note: the postcss-for plugin is another that must be executed before postcss-nested and postcss-simple-vars. Install the postcss-for plugin, then test it’s working as expected by adding this code to your “src/style.css” file:  1 @for$i from 1 to 3 {  2  p:nth-of-type($i) {  3  margin-left: calc( 100% /$i );  4  }  5 } 

It should compile to give you:

 1 p:nth-of-type(1) {  2  margin-left: calc( 100% / 1 );  3 }  4 5 p:nth-of-type(2) {  6  margin-left: calc( 100% / 2 );  7 }  8 9 p:nth-of-type(3) {  10  margin-left: calc( 100% / 3 );  11 } 

Once again, the plugin we’re using to add @for loops is the same as is used in PreCSS, so for extra information on its syntax check out the “Loops” section in the previous tutorial.

### Other “for” Loop Plugin Options:

The postcss-for plugin has to be run before postcss-simple-vars, which means there’s no way to use variables to set the range you want your @for loop to iterate through.

If this is a problem, you can instead use this fork of the postcss-for plugin that should instead be loaded after the postcss-simple-vars plugins.

Because it runs after variables are evaluated, you are free to use variables to set the range you want your @for loop to iterate through, like this:

 1 @from: 1;  2 @count: 3;  3 4 @for $i from @from to @count {  5  p:nth-of-type($i) {  6  margin-left: calc( 100% / $i );  7  }  8 }  ## Add Variables We’re going to be adding two kinds of variables to our preprocessor, both of which can be very handy. The first kind uses Sass-like syntax, and the second uses the syntax of CSS custom properties, otherwise known as CSS variables. ### Variables Plugins Used: Install these two plugins, then we’ll test each one at a time. First, we’ll test the Sass-like syntax of postcss-simple-vars. Open up the “_vars.css” file you made earlier, delete its contents and add the following code:  1 $default_padding: 1rem; 

Add the following to your “src/style.css” file and recompile:

 1 .post {  2  padding: $default_padding;  3 }  It should compile to give you:  1 .post {  2  padding: 1rem;  3 }  Now we’ll test the CSS custom properties like syntax of postcss-css-variables. Add the following code to your “src/style.css” file:  1 :root {  2  --h1_font_size: 3rem;  3 }  4 5 h1 {  6  font-size: var(--h1_font_size);  7 }  8 9 @media ( max-width: 75rem ){  10  h1 {  11  --h1_font_size: 4vw;  12  }  13 }  It should compile into:  1 h1 {  2  font-size: 3rem;  3 }  4 5 @media ( max-width: 75rem ) {  6 7  h1 {  8  font-size: 4vw;  9  }  10 }  Notice that when using CSS variables, we only had to change the value of the --h1_font_size variable inside the media query and it automatically output the associated font-size property. This is particularly useful functionality. ### Why Use Both Kinds of Variables? Before I go on I’ll just briefly mention again, that the approach taken in this tutorial is not the approach you have to take. If you want to use one kind of variable and not the other, that’s completely fine. From my perspective, the reason I like to use both kinds of variables is I use them in two different ways. I will typically use the CSS custom properties syntax in my main stylesheet, while I use Sass-like variables in my partial files. This lets me set out CSS custom properties for the type of variables I might actually use in a live project if/when they become well supported across browsers. As you saw in the above example, they also have certain functionality that Sass-like variables do not. Meanwhile, I can use Sass-like variables for things that don’t belong in a live stylesheet, especially those that exist purely to be processed through through things like each loops, conditionals and other transformations. ### Other Variables Plugin Options: As an alternative to using postcss-simple-vars you might like to consider using postcss-advanced-variables, the plugin used as part of the PreCSS pack. This is also an excellent option, with the primary difference being it handles conditionals, loops and variables all in the same plugin. For me, the reason I currently choose postcss-simple-vars is I prefer to have conditionals coming from a separate plugin; postcss-conditionals which we’ll cover shortly. Instead of using postcss-css-variables, you might prefer postcss-custom-properties The essential difference between the two is postcss-custom-properties conforms strictly to the W3C spec for custom properties so you can be confident that you are writing only correct future CSS. On the other hand postcss-css-variables offers extra functionality, but in doing so it does not claim to have complete parity with spec. I personally choose postcss-css-variables because I am using it in the context of preprocessing where I write a lot of non-spec code anyway. As such I’d rather have the added functionality over 100% spec compliance. However, if you’re using variables in the context of writing future CSS, you might find postcss-custom-properties is a better fit for you. ## Add “each” Loops ### “each” Loop Plugin Used: Install the postcss-each plugin then add this variable code to your “_vars.css” file:  1 $social: twitter, facebook, youtube; 

This code defines a list, stored in the $social variable. Now we’re going to create an @each loop to iterate through the values stored in our $social variable. Add this code to your “src/style.css” file:

 1 @each $icon in ($social){  2  .icon-$(icon) {  3  background: url('img/$(icon).png');  4  }  5 } 

Our @each loop is now ready, but before we can compile it we need to make a little configuration change to the options of postcss-simple-vars.

You’ll notice that in the code above we’re using $icon to represent the current value we’re iterating through. Some difficulty can arise from this because the postcss-simple-vars plugin looks for the $ sign in order to identify variables.

This means it will see $icon, think it’s a variable, try to process it, then see it doesn’t have a value. That will make it stop compiling and log an error to the console that it’s discovered an undefined variable. To resolve this, we want to add the option silent: true to our options for the plugin. This means that if it discovers an undefined variable it won’t stop compiling to log an error, it will just carry on. Hence it won’t be bothered by the presence $icon in our @each loop and we’ll be able to compile successfully.

In the processors array of your Gulpfile or Gruntfile, set the option:

 1 /* Gulpfile */  2 simple_vars({silent: true})  3 4 /* Gruntfile */  5 require('postcss-simple-vars')({silent: true}) 

Now compile your CSS and you should get:

 1 .icon-twitter {  2  background: url('img/twitter.png');  3 }  4 5 .icon-facebook {  6  background: url('img/facebook.png');  7 }  8 9 .icon-youtube {  10  background: url('img/youtube.png');  11 } 

### Other “each” Loop Plugin Options:

As mentioned earlier, postcss-advanced-variables is another excellent plugin option that handles variables, loops and conditionals all in one.

### Conditionals Plugin Used:

I mentioned previously that this plugin is my preference for handling conditionals. This is because I have found it is able to handle more complex conditional checks. It includes support for @else if syntax, meaning you can test against more conditions in a single piece of code.

After installing the postcss-conditionals plugin, test it out by adding this code to your “src/style.css” file:

 1 $column_count: 3;  2 3 .column {  4  @if$column_count == 3 {  5  width: 33%;  6  float: left;  7  } @else if $column_count == 2 {  8  width: 50%;  9  float: left;  10  } @else {  11  width: 100%;  12  }  13 }  This code will check on the value we’ve set in the variable @column_count and will output different width and float values depending on what it finds. It works in the same way as the code we used in the previous preprocessing tutorial, but now that we have the ability to use @else if lines we’ve been able to increase the number of conditions we’re testing from two to three. After recompiling this should give you:  1 .column {  2  width: 33%;  3  float: left  4 }  Try changing $column_count to 2 or 1 and compiling again to see how it changes the CSS output.

We can also use these types of conditionals well inside mixins, for which we added support earlier. For example, we can create a mixin to generate column layout code like so:

 1 @define-mixin columns $count {  2  @if$count == 3 {  3  width: 33%;  4  float: left;  5  } @else if $count == 2 {  6  width: 50%;  7  float: left;  8  } @else {  9  width: 100%;  10  }  11 }  12 13 .another_column {  14  @mixin columns 2;  15 }  This will give you the output:  1 .another_column {  2  width: 50%;  3  float: left;  4 }  ### Other Conditionals Options: As mentioned earlier, postcss-advanced-variables is another excellent plugin option that handles variables, loops and conditionals all in one. ## Add Calc() for Math ### Calc() Plugin Used: In a previous tutorial we used postcss-calc, via cssnano, to help make instances of calc() use more efficient. In the context of preprocessing, however, it can be very useful wherever we might want to use math in our stylesheets. Go ahead and install postcss-calc, then we’re going to test it out by making the column generation mixin we added above more efficient. Right now we’re using conditionals to check if the mixin’s $count argument is set to either 1, 2 or 3 then outputting a corresponding pre-calculated width. Instead, we’ll use calc() to automatically output the right width for our column code, no matter what number is passed through the mixin.

 1 @define-mixin columns_calc $count {  2  width: calc( 100% /$count );  3  @if $count > 1 {  4  float: left;  5  }  6 }  7 8 .column_calculated {  9  @mixin columns_calc 2;  10 }  Instead of hard coding the percentage widths we’d need for certain numbers of columns, we’re now calculating it on the fly. The postcss-calc plugin will convert width: calc( 100% /$count ); into a static amount depending on the value passed when we call the mixin, in this case 2.

Recompile your code and you should see this output:

 1 .column_calculated {  2  width: 50%;  3  float: left;  4 } 

Note: Wherever postcss-calc can resolve calc() to a static value it will output it into your code. If it can’t, it will change nothing, so you can still use calc() for values that need to be handled by the browser at runtime.

### Nesting Plugin Used:

For nesting we’re using the same plugin as is used in the PreCSS pack, so you can refer back to the previous tutorial for full information on syntax.

Install postcss-nested then test that everything is working properly by compiling this code:

 1 .menu {  2  width: 100%;  3  a {  4  text-decoration: none;  5  }  6 } 

Your resulting CSS should be:

 1 .menu {  2  width: 100%;  3 }  4 5 .menu a {  6  text-decoration: none;  7 } 

### Extends Plugin Used:

For extends we’ll be using the postcss-sass-extend plugin. It will give us different syntax to use than that we covered in our previous tutorial on working with PreCSS. Instead of extends being defined with @define-extend extend_name {...} they are defined with %extend_name {...}.

They are still used with the essentially the same syntax of @extend %extend_name;.

Note that the postcss-sass-extend plugin does actually ship with PreCSS, however I assume it doesn’t load by default as when I attempted to use the required syntax it did not compile.

After installing postcss-sass-extend into your project, test it out with the following code:

 1 %rounded_button {  2  border-radius: 0.5rem;  3  padding: 1em;  4  border-width: 0.0625rem;  5  border-style: solid;  6 }  7 8 .blue_button {  9  @extend %rounded_button;  10  border-color: #2F74D1;  11  background-color: #3B8EFF;  12 }  13 14 .red_button {  15  @extend %rounded_button;  16  border-color: #C41A1E;  17  background-color: #FF2025;  18 } 

It should compile into:

 1 .blue_button, .red_button {  2  border-radius: 0.5rem;  3  padding: 1em;  4  border-width: 0.0625rem;  5  border-style: solid;  6 }  7 8 .blue_button {  9  border-color: #2F74D1;  10  background-color: #3B8EFF;  11 }  12 13 .red_button {  14  border-color: #C41A1E;  15  background-color: #FF2025;  16 } 

## Extras

So far we’ve covered what could be considered the core features common to most preprocessors. However, there are still even more plugins available to offer extra features; some of these features are found in other preprocessors, and some you have to go to PostCSS to find. We’ll go over these extra options briefly now.

### Color Manipulation

#### Plugins:

The ability to tweak colors can be one of the most useful features found in preprocessors. There are actually several color plugins for PostCSS, but these are three that find themselves particularly at home in a preprocessing setup. They allow for various color transformations including lightening, darkening, saturating, adding alpha values and more.

### Property Definitions

#### Plugin:

The functionality offered by this plugin could be compared to the seamless mixins of Stylus, whereby, rather than using a syntax like @mixin, you define chunks of code in such a way that they can subsequently be used in code in the same way as a native property, e.g.

 1 /* Define a property */  2 size: $size {  3  height:$size;  4  width: \$size;  5 }  6 7 /* Use it like a native property */  8 .square {  9  size: 50px;  10 } 

The plugin can also be used to redefine native properties to suit your needs.

### Property Lookup

#### Plugin:

Property lookup is a feature found in Stylus that can be very handy. It allows you to lookup the value of a property from within the same style. For example, you might set a right margin to match the left with: margin-left: 20px; margin-right: @margin-left;

### Nested Properties

#### Plugin:

While the regular nesting we covered above unwraps selectors, the postcss-nested-props plugin unwraps nested properties, for example:

 1 /* Origincal code */  2 .element {  3  border: {  4  width: 1px;  5  style: solid;  6  color: #ccc;  7  }  8 }  9 10 /* After processing */  11 .element {  12  border-width: 1px;  13  border-style: solid;  14  border-color: #ccc;  15 } 

### Matching

#### Plugin:

Matching gives you another way to perform conditional checks, this time using Rust like pattern matching, something similar to switch statements in JavaScript or PHP. This can give you a more efficient way to check multiple conditions than writing many @if else checks.

### CSS Sprite Generation

#### Plugin:

CSS sprite generation, a popular feature in Compass, can also be done through the postcss-sprites plugin. The plugin will scan your CSS for images, combine those images into a sprite sheet, and update your code as required in order to display from the new sprite sheet correctly.

### Lots More Choices

There is currently a really robust list of language extension plugins to choose from, more than we can cover here, so check out the full list at: https://github.com/postcss/postcss#language-extensions

## Coming Soon: Alternative Syntaxes

For many people, the ability to write in terse, efficient syntax (typically sans semicolons and curly braces) is one of the big appeals of preprocessors like Stylus or Sass. The newly released version 5.0 of PostCSS now supports custom parsers which will enable new syntaxes to be supported. SugarSS is to be the terse syntax parser, and discussions are currently open on how this syntax will be structured.

## You Can Always Add Your Own

PostCSS is still relatively new and you may find there is something you want to achieve with your custom preprocessor for which there’s currently no plugin. The beauty of this modular ecosystem is you have the option to solve that problem yourself by creating your own plugin. Anyone can do it, and the barrier to entry is far lower than were you to try and add your own functionality to Stylus, Sass or LESS. We’ll learn how in a later tutorial.

## In the Next Tutorial

You don’t have to choose between PreCSS and rolling your own preprocessor if you want to use PostCSS. You can actually opt out of any PostCSS-based preprocessing entirely if you choose, instead using it side by side with your favorite preprocessor.

In the next tutorial we’ll learn how to use PostCSS in conjunction with Stylus, Sass or LESS. See you there!