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:
return gulp.src('./src/*.css')
...and change it to:
return gulp.src('./src/style.css')
Import Plugin Used:
- postcss-import by Maxime Thirouin: https://github.com/postcss/postcss-import
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:
.test { background: black; }
Now import your new variables file into your main “src/style.css” file by adding this code at the first line:
@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.
Add Mixins
Mixins Plugin Used:
- postcss-mixins by Andrey Sitnik: https://github.com/postcss/postcss-mixins
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:
@define-mixin icon $network, $color { .button.$(network) { background-image: url('img/$(network).png'); background-color: $color; } } @mixin icon twitter, blue;
After compilation your “dest/style.css” should have the following compiled code added to it:
.button.twitter { background-image: url('img/twitter.png'); background-color: blue; }
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:
- postcss-sassy-mixins by Andy Jansson: https://github.com/andyjansson/postcss-sassy-mixins
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:
- postcss-for by Anton Yakushev: https://github.com/antyakushev/postcss-for
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:
@for $i from 1 to 3 { p:nth-of-type($i) { margin-left: calc( 100% / $i ); } }
It should compile to give you:
p:nth-of-type(1) { margin-left: calc( 100% / 1 ); } p:nth-of-type(2) { margin-left: calc( 100% / 2 ); } p:nth-of-type(3) { margin-left: calc( 100% / 3 ); }
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:
- Fork of postcss-for by Anton Yakushev: https://github.com/xori/postcss-for
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:
@from: 1; @count: 3; @for $i from @from to @count { p:nth-of-type($i) { margin-left: calc( 100% / $i ); } }
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:
- postcss-simple-vars by Andrey Sitnik: https://github.com/postcss/postcss-simple-vars
- postcss-css-variables by Eric Eastwood: https://github.com/MadLittleMods/postcss-css-variables
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:
$default_padding: 1rem;
Add the following to your “src/style.css” file and recompile:
.post { padding: $default_padding; }
It should compile to give you:
.post { padding: 1rem; }
Now we’ll test the CSS custom properties like syntax of postcss-css-variables. Add the following code to your “src/style.css” file:
:root { --h1_font_size: 3rem; } h1 { font-size: var(--h1_font_size); } @media ( max-width: 75rem ){ h1 { --h1_font_size: 4vw; } }
It should compile into:
h1 { font-size: 3rem; } @media ( max-width: 75rem ) { h1 { font-size: 4vw; } }
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:
- postcss-advanced-variables by Jonathan Neal: https://github.com/jonathantneal/postcss-advanced-variables
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.
- postcss-custom-properties by Maxime Thirouin: https://github.com/postcss/postcss-custom-properties
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:
- postcss-each by Alexander Madyankin: https://github.com/outpunk/postcss-each
Install the postcss-each plugin then add this variable code to your “_vars.css” file:
$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:
@each $icon in ($social){ .icon-$(icon) { background: url('img/$(icon).png'); } }
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:
/* Gulpfile */ simple_vars({silent: true}) /* Gruntfile */ require('postcss-simple-vars')({silent: true})
Now compile your CSS and you should get:
.icon-twitter { background: url('img/twitter.png'); } .icon-facebook { background: url('img/facebook.png'); } .icon-youtube { background: url('img/youtube.png'); }
Other “each” Loop Plugin Options:
- postcss-advanced-variables by Jonathan Neal: https://github.com/jonathantneal/postcss-advanced-variables
As mentioned earlier, postcss-advanced-variables is another excellent plugin option that handles variables, loops and conditionals all in one.
Add Conditionals
Conditionals Plugin Used:
- postcss-conditionals by Andy Jansson: https://github.com/andyjansson/postcss-conditionals
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:
$column_count: 3; .column { @if $column_count == 3 { width: 33%; float: left; } @else if $column_count == 2 { width: 50%; float: left; } @else { width: 100%; } }
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:
.column { width: 33%; float: left }
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:
@define-mixin columns $count { @if $count == 3 { width: 33%; float: left; } @else if $count == 2 { width: 50%; float: left; } @else { width: 100%; } } .another_column { @mixin columns 2; }
This will give you the output:
.another_column { width: 50%; float: left; }
Other Conditionals Options:
- postcss-advanced-variables by Jonathan Neal: https://github.com/jonathantneal/postcss-advanced-variables
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:
- postcss-calc by Maxime Thirouin: https://github.com/postcss/postcss-calc
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.
Add to your “src/style.css” file:
@define-mixin columns_calc $count { width: calc( 100% / $count ); @if $count > 1 { float: left; } } .column_calculated { @mixin columns_calc 2; }
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:
.column_calculated { width: 50%; float: left; }
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.
Add Nesting
Nesting Plugin Used:
- postcss-nested by Andrey Sitnik: https://github.com/postcss/postcss-nested
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:
.menu { width: 100%; a { text-decoration: none; } }
Your resulting CSS should be:
.menu { width: 100%; } .menu a { text-decoration: none; }
Add Extends
Extends Plugin Used:
- postcss-sass-extend by Jonathan Neal: https://github.com/jonathantneal/postcss-sass-extend
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:
%rounded_button { border-radius: 0.5rem; padding: 1em; border-width: 0.0625rem; border-style: solid; } .blue_button { @extend %rounded_button; border-color: #2F74D1; background-color: #3B8EFF; } .red_button { @extend %rounded_button; border-color: #C41A1E; background-color: #FF2025; }
It should compile into:
.blue_button, .red_button { border-radius: 0.5rem; padding: 1em; border-width: 0.0625rem; border-style: solid; } .blue_button { border-color: #2F74D1; background-color: #3B8EFF; } .red_button { border-color: #C41A1E; background-color: #FF2025; }
Other Extend Plugin Options:
- postcss-extend: https://github.com/travco/postcss-extend
- postcss-simple-extend: https://github.com/davidtheclark/postcss-simple-extend
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:
- postcss-color-function by Maxime Thirouin: https://github.com/postcss/postcss-color-function
- postcss-color-alpha by Ivan Vlasenko: https://github.com/avanes/postcss-color-alpha
- postcss-color-scale by Kristofer Joseph: https://github.com/kristoferjoseph/postcss-color-scale
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:
- postcss-define-property by Dale Eidd: https://github.com/daleeidd/postcss-define-property
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.
/* Define a property */ size: $size { height: $size; width: $size; } /* Use it like a native property */ .square { size: 50px; }
The plugin can also be used to redefine native properties to suit your needs.
Property Lookup
Plugin:
- postcss-property-lookup by Simon Smith: https://github.com/simonsmith/postcss-property-lookup
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:
- postcss-nested-props by Jed Mao: https://github.com/jedmao/postcss-nested-props
While the regular nesting we covered above unwraps selectors, the postcss-nested-props plugin unwraps nested properties, for example:
/* Origincal code */ .element { border: { width: 1px; style: solid; color: #ccc; } } /* After processing */ .element { border-width: 1px; border-style: solid; border-color: #ccc; }
Matching
Plugin:
- postcss-match by Ryan Tsao: https://github.com/rtsao/postcss-match
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:
- postcss-sprites by Viktor Vasilev: https://github.com/2createStudio/postcss-sprites
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!
Comments