Gulp For Sass

  • Unsurprisingly, Sass compilation with LibSass
  • Generating sourcemaps for easier debugging
  • Prefixing CSS with Autoprefixer
  • Generating Sass documentation with SassDoc

Compiling Sass

The first thing to do is to install the dependencies and to create a Gulpfile.js. We will need Gulp (no shit, Sherlock), but also gulp-sass to compile our stylesheets:

$ npm install gulp gulp-sass --save-dev

This line tells npm to install both gulp and gulp-sass packages as development dependencies. You can now find them in the devDependencies object of your package.json. And the Gulpfile.js:

var gulp = require('gulp');
var sass = require('gulp-sass');

Wow, that was short. What we need now is a task to run Sass (actually gulp-sass) on our stylesheets folder.
var input = './stylesheets/**/*.scss';
var output = './public/css';

gulp.task('sass', function () {
return gulp
// Find all `.scss` files from the `stylesheets/` folder
.src(input)
// Run Sass on those files
.pipe(sass())
// Write the resulting CSS in the output folder
.pipe(gulp.dest(output));
});

That’s it! We can now compile our stylesheets using LibSass thanks to a very minimal Gulp task. What about that? We can pass options to gulp-sass to compile stylesheets in expanded mode and to print errors in console:

var sassOptions = {
errLogToConsole: true,
outputStyle: 'expanded'
};

gulp.task('sass', function () {
return gulp
.src(input)
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(gulp.dest(output));
});

Bringing Autoprefixer to the party

I won’t go into much detail about why using Autoprefixer is better than writing vendor by hand (or with a mixin which is basically the same thing), but roughly Autoprefixer is a post-processing step meaning it actually updates already compiled stylesheets to add relevant prefixes based on an up-to-date database and a given configuration. In other words, you tell Autoprefixer which browsers you want to support, and it adds only relevant prefixes to the stylesheets. Zero effort, perfect support (please remind me to patent this catch phrase).

To include Autoprefixer in our Gulp’y workflow, we only need it to pipe it after Sass has done its thing. Then Autoprefixer updates the stylesheets to add prefixes.

First, let’s install it (you get the gist by now):

$ npm install gulp-autoprefixer --save-dev

Then we add it to our task:

var gulp = require('gulp');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');

// ... variables

gulp.task('sass', function () {
return gulp
.src(input)
.pipe(sourcemaps.init())
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(autoprefixer())
.pipe(gulp.dest(output));
});

Right now, we run with the default configuration from Autoprefixer which is

  • Browsers with over 1% market share,
  • Last 2 versions of all browsers,
  • Firefox ESR,
  • Opera 12.1

We can use our own configuration like so:

var gulp = require('gulp');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');

// ... variables
var autoprefixerOptions = {
browsers: ['last 2 versions', '> 5%', 'Firefox ESR']
};

gulp.task('sass', function () {
return gulp
.src(input)
.pipe(sourcemaps.init())
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(autoprefixer(autoprefixerOptions))
.pipe(gulp.dest(output));
});

Release the docs!

The last, but not least, tool to add to our workflow, Sass documentation generation with SassDoc. SassDoc is to Sass what JSDoc is to JavaScript: a documentation tool. It parses your stylesheets looking for comment blocks documenting variables, mixins, functions and placeholders.

If your project uses SassDoc (it should!), you can add the automatic documentation generation in your Gulp workflow.

The cool thing with SassDoc is that it can be piped directly in Gulp because its API is Gulp compatible. So you don’t actually have a gulp-sassdoc plugin.

npm install sassdoc --save-dev

var gulp = require('gulp');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');
var sassdoc = require('sassdoc');

// ... variables

gulp.task('sass', function () {
return gulp
.src(input)
.pipe(sourcemaps.init())
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(autoprefixer(autoprefixerOptions))
.pipe(gulp.dest(output))
.pipe(sassdoc())
// Release the pressure back and trigger flowing mode (drain)
// See: http://sassdoc.com/gulp/#drain-event
.resume();
});

Note that depending on the size of your project and the number of documented items, SassDoc can take up to a few of seconds to run (rarely above 3 as far as I’ve noticed), so you might want to have a separate task for this.

gulp.task('sassdoc', function () {
return gulp
.src(input)
.pipe(sassdoc())
.resume();
});

Again, we use the default configuration but we can use our own if we want to.

var sassdocOptions = {
dest: './public/sassdoc'
};

gulp.task('sassdoc', function () {
return gulp
.src(input)
.pipe(sassdoc(sassdocOptions))
.resume();
});

I’m watching you

There is still something we can do before leaving: creating a watch task. The point of this task would be to watch for changes in stylesheets to recompile them again. It is very convenient when working on the Sass side of the project so you don’t have to run the sass task by hand every time you save a file.

gulp.task('watch', function() {
return gulp
// Watch the input folder for change,
// and run `sass` task when something happens
.watch(input, ['sass'])
// When there is a change,
// log a message in the console
.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
});

Here is another reason why I recommend not including SassDoc in the sass task: you probably don’t want to regenerate the docs every time you touch a stylesheet. This is likely something you want to do on build or push, maybe with a pre-commit hook.

Adding the final touch

A last, yet important, thing to think about: running sass in the default task.

gulp.task('default', ['sass', 'watch' /*, possible other tasks... */]);

The array passed as second argument of the task(..) function is a list of dependency tasks. Basically, it tells Gulp to run those tasks before running the one specified as a third argument (if any).

Also, we could probably create a prod task that could be run right before deploying to production (maybe with a git hook). This task should:

  • Compile Sass in compressed mode
  • Prefix CSS with Autoprefixer
  • Regenerate SassDoc documentation
  • Avoid any sourcemaps
gulp.task('prod', ['sassdoc'], function () {
return gulp
.src(input)
.pipe(sass({ outputStyle: 'compressed' }))
.pipe(autoprefixer(autoprefixerOptions))
.pipe(gulp.dest(output));
});
`