676Days

676 days since this post was last revised. Specific details are likely out of date.

If you come from a Sass background you will be used to creating and referencing mixins and variables in your style sheets. However, if you find yourself in a situation where you need process a raft of unrelated CSS files with a single set of variables and mixins this can be a pain. PostCSS offers an alternative.

The problem

With multiple CSS files all being processed seperately (rather than being concatenated into a single file), even with pre-processors this presents an issue. It would mean writing something like:

@include "dir/variables";
@include "dir/mixins";

At the top of every file.

However, with the ever flexible JS-based PostCSS we can do better.

David Khourshid pointed out via Twitter that he has a project that aims to bring just this sort of functionality to Sass. If you are processing your style sheets with Sass, check out Sassport https://github.com/davidkpiano/sassport

With PostCSS it is possible to define the variables and the mixins as JavaScript. In this post I’m going to use Gulp to demonstrate how this can be done. Basically I have a raft of CSS files in one folder that I want to process with PostCSS (using variables and mixins) and output as a raft of separate CSS files in another folder.

The readme for postcss-simple-vars and postcss-mixins both stipulate that it’s possible to use objects and functions externally. The idea therefore was simple. Create two JS files, one containing the variables and one containing the mixins. PostCSS could then look to these before processing the many CSS files.

Assume a file structure like this:

preCSS/
    css/ [source CSS files here]
    variables.js
    mixins.js
[output CSS files here]
gulpfile.js
package.json

First our Gulpfile for the PostCSS job:

var gulp = require("gulp");
var changed = require("gulp-changed");

// As we need to reference a single set of mixins across multiple independently processed CSS files, we have created JS version of the mixins
var cssMixins = require("./preCSS/mixins.js");

//As we need to reference a single set of vars across multiple independently processed CSS files, we have created a JS for the CSS vars
var cssVariables = require("./preCSS/variables.js");

var sourceCSS = ["preCSS/**/*.css"];
var outputCSS = "./";

gulp.task("styles", function () {

    //PostCSS related
    var postcss = require("gulp-postcss");
    var autoprefixer = require("autoprefixer");
    var mixins = require("postcss-mixins")({
        mixins: cssMixins
    });
    var simpleVars = require("postcss-simple-vars")({
        variables: cssVariables
    });
    var nested = require("postcss-nested");
    var gutil = require("gulp-util");

    var processors = [
    mixins, simpleVars, nested, autoprefixer({ browsers: ["last 2 version", "safari 5", "ie > 9", "opera 12.1", "ios 6", "android 2.3"] })];
    
    return gulp.src(sourceCSS)

    // Only files that have changed are piped through
    .pipe(changed(outputCSS))
    
    // We always want PostCSS to run
    .pipe(postcss(processors).on("error", gutil.log))
    
    // Set the destination for the CSS file
    .pipe(gulp.dest(outputCSS));
});

gulp.task("watch", function () {
    gulp.watch(sourceCSS, ["styles"]);
});

// define the default task and add the watch task to it
gulp.task("default", ["watch"]);

OK, first of all notice the require for the CSS mixins. This is the format that mixins need to take in CSS land:

module.exports = {
    FontHeadline: function () {
        return {
            "font-family": '$font-verdana',
            "font-weight": '400',

            '@media (max-device-width: 1024px)': {
                'font-family': '$font-hv-bold',
                'font-weight': '700'
            },
            '@media (max-device-width: 2048px) and (min-resolution: 2dppx)': {
                'font-family': '$font-hv-bold',
                'font-weight': '700'
            }
        }
    }
};

As you can probably gather, each mixin is defined as a function with the return of the function being an object with key value pairs (the CSS property/value equivalents).

It’s important to remember that as we are in JS land, hyphens in variables are very naughty, so it’s necessary to quote them.

OK, so let’s have a look at the format of the variables.js:

module.exports = {
    // Breakpoints
    "$S": "300px", 
    "$SSM": "380px",
    "$SM": "550px", 
    "$toM": "767px",
    "$M": "768px", 
    "$XM": "950px", 
    "$L": "1200px", 
    "$XL": "1328px"
};

Same deal here. Key/value pairs in JS object format as opposed to CSS property/value format. And that’s pretty much it. A few minutes in a text editor and t’s fairly trivial to convert any existing CSS file full or variables into JS object format.

I’d like to draw your attention to these two parts of the gulpfile:

var mixins = require("postcss-mixins")({
    mixins: cssMixins
});
var simpleVars = require("postcss-simple-vars")({
    variables: cssVariables
});

This is how you communicate to PostCSS where the mixins and variables for each plugin are stored. cssVariables and cssMixins both being variables referenced at the top of the file.

You still write references to variables and mixins the same way in the CSS:

.hp-Header {
    @mixin FontHeadline;
    min-height: $size-quadruple;
}

I like this approach a lot. Refencing the variables and mixins in this manner allows files to be processed exactly as if you had referenced the variables and mixins with an @import statement the ‘normal’ way.