SitePoint
  • Premium
  • Library
  • Community
  • Jobs
  • Blog
LoginStart Free Trial
CSS: Tools & Skills
CSS: Tools & Skills
Notice of Rights
Notice of Liability
Trademark Notice
About SitePoint
Conventions Used
Why Use Gulp?
Example Project Overview
Getting Started with Gulp
Test HTML Page
Module Installation
Create a Gulp Task File
Gulp Image Task
Gulp CSS Task
Automating Your Workflow
Live Production Code
Next Steps
The Demo PWA to Audit
Auditing with Google’s Lighthouse
Critical Rendering Path
CSS Optimization Using Lighthouse Opportunities
Checking the Optimizations
Similar Tools
Conclusion
stylelint
UnCSS
Consider a Task Runner or Build Tool
Using the Styles Panel
Debugging Responsive Layouts
Installing CSSO
Minification with CSSO
Print Stylesheets
Testing Printer Output
Remove Unnecessary Sections
Linearize the Layout
Printer Styling
Add Supplementary Content
Page Breaks
Printing Pains

How to Use Gulp.js to Automate Your CSS Tasks

In this article, we look at how you can use Gulp.js to automate a range of repetitive CSS development tasks to speed up your workflow.

Web development requires little more than a text editor. However, you’ ll quickly become frustrated with the repetitive tasks that are essential for a modern website and fast performance, such as:

  • converting or transpiling
  • minimizing file sizes
  • concatenating files
  • minifying production code
  • deploying updates to development, staging and live production servers.

Some tasks must be repeated every time you make a change. The most infallible developer will forget to optimize an image or two and pre-production tasks become increasingly arduous.

Fortunately, computers never complain about mind-numbing work. This article demonstrates how use Gulp.js to automate CSS tasks, including:

  • optimizing images
  • compiling Sass .scss files
  • handling and inlining assets
  • automatically appending vendor prefixes
  • removing unused CSS selectors
  • minifying CSS
  • reporting file sizes
  • outputting sourcemaps for use in browser DevTools
  • live-reloading CSS in a browser when source files change.

All the code is available from GitHub, and it works on Windows, macOS or Linux.

Why Use Gulp?

A variety of task runners are available for web projects including Gulp, Grunt, webpack and even npm scripts. Ultimately, the choice is yours and it doesn’t matter what you use, as your site/app visitors will never know or care.

Gulp is a few years old but stable, fast, supports many plugins, and is configured using JavaScript code. Writing tasks in code has several advantages, and you can modify output according to conditions — such as only minifying CSS when building the final files for live deployment.

Example Project Overview

The task code assumes Gulp 3.x will be used. This is the most recent stable version and, while Gulp 4 is available, it’s not the default on npm. If you’re using Gulp 4, refer to How do I update to Gulp 4? and tweak the gulpfile.js code accordingly.

Image file sizes will be minimized with gulp-imagemin which optimizes JPG, GIF and PNG bitmaps as well as SVG vector graphics.

Sass .scss files will be pre-processed into a browser-compatible main.css file. Sass isn’t considered as essential as it once was, because:

  • standard CSS now offers features such as variables (Custom Properties)
  • HTTP/2 reduces the need for file concatenation.

That said, Sass remains a practical option for file splitting, organization, (static) variables, mixins and nesting (presuming you don’t go too deep).

The resulting Sass-compiled CSS file will then be processed using PostCSS to provide further enhancements such as asset management and vendor-prefixing.

Getting Started with Gulp

If you’ ve never used Gulp before, please read An Introduction to Gulp.js. These are the basic steps for getting started from your terminal:

  1. Ensure Node.js is installed.
  2. Install the Gulp command-line interface globally with npm i gulp-cli -g.
  3. Create a new project folder — for example, mkdir gulpcss — and enter it (cd gulpcss).
  4. Run npm init and answer each question (the defaults are fine). This will create a package.json project configuration file.
  5. Create a src sub-folder for source files: mkdir src.

The example project uses the following sub-folders:

  • src/images — image files
  • src/scss — source Sass files
  • build — the folder where compiled files are generated

Test HTML Page

This tutorial concentrates on CSS-related tasks, but an index.html file in the root folder is useful for testing. Add your own page code with a <link> to the final stylesheet. For example:

Code snippet

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width,initial-scale=1.0">  <title>Using Gulp.js for CSS tasks</title>  <link rel="stylesheet" media="all" href="build/css/main.css"></head><body>
  <h1>My example page</h1>
</body></html>

Module Installation

Most Node.js modules will be installed as project dependencies so the CSS can be built on development or production servers. To install Gulp and all plugins locally, do the following:

Code snippet

npm i gulp gulp-imagemin gulp-newer gulp-noop gulp-postcss gulp-sass gulp-size ➥gulp-sourcemaps postcss-assets autoprefixer cssnano usedcss

Assuming you’re using a recent version of npm, all modules will be listed in the "dependencies" section of package.json.

browser-sync is installed as a development dependency since, it’s not required on live production servers:

Code snippet

npm i browser-sync --save-dev

The module will be listed in the "devDependencies" section of package.json.

Create a Gulp Task File

Gulp tasks are defined in a JavaScript file named gulpfile.js. Create it, then open the file in your editor (VS Code is the current favorite). Add the following code:

Code snippet

(() => {
  'use strict';
  /**************** gulpfile.js configuration ****************/
  const
    // development or production    devBuild  = ((process.env.NODE_ENV || 'development').trim().toLowerCase() ===     ➥'development'),
    // directory locations    dir = {      src         : 'src/',      build       : 'build/'    },
    // modules    gulp          = require('gulp'),    noop          = require('gulp-noop'),    newer         = require('gulp-newer'),    size          = require('gulp-size'),    imagemin      = require('gulp-imagemin'),    sass          = require('gulp-sass'),    postcss       = require('gulp-postcss'),    sourcemaps    = devBuild ? require('gulp-sourcemaps') : null,    browsersync   = devBuild ? require('browser-sync').create() : null;
  console.log('Gulp', devBuild ? 'development' : 'production', 'build');
})();

This defines a self-executing function and constants for:

  • devBuild — true if NODE_ENV is blank or set to development
  • dir.src — the source file folder
  • dir.build — the build folder
  • Gulp and all plugin modules

Note that sourcemaps and browsersync are only enabled for development builds.

Gulp Image Task

Create the src/images folder then copy some image files into it or any subfolder.

Insert the following code below the console.log in gulpfile.js to create an images processing task:

Code snippet

/**************** images task ****************/
  const imgConfig = {    src           : dir.src + 'images/**/*',    build         : dir.build + 'images/',
    minOpts: {      optimizationLevel: 5    }  };
  gulp.task('images', () =>
    gulp.src(imgConfig.src)      .pipe(newer(imgConfig.build))      .pipe(imagemin(imgConfig.minOpts))      .pipe(size({ showFiles:true }))      .pipe(gulp.dest(imgConfig.build))
  );

Configuration parameters are defined in imgConfig, which sets:

  • a src folder to any image inside src/images or a subfolder
  • a build folder to build/images, and
  • gulp-imagemin optimization options.

A gulp.task named images passes data through a series of pipes:

  1. The source folder is examined.
  2. The gulp-newer plugin removes any newer image already present in the build folder.
  3. The gulp-imagemin plugin optimizes the remaining files.
  4. The gulp-size plugin shows the resulting size of all processed files.
  5. The files are saved to the gulp.dest build folder.

Save gulpfile.js, then run the images task from the command line:

Code snippet

gulp images

The terminal will show a log something like this:

Code snippet

Gulp development build[16:55:12] Using gulpfile gulpfile.js[16:55:12] Starting 'images'...[16:55:12] icons/alert.svg 306 B[16:55:12] cave-0600.jpg 47.8 kB[16:55:12] icons/fast.svg 240 B[16:55:12] cave-1200.jpg 112 kB[16:55:12] cave-1800.jpg 157 kB[16:55:12] icons/reload.svg 303 B[16:55:12] gulp-imagemin: Minified 3 images (saved 205 B - 19.4%)[16:55:12] all files 318 kB[16:55:12] Finished 'images' after 640 ms

Examine the created build/images folder to find optimized versions of your images. If you run gulp images again, nothing will occur because only newer files will be processed.

Gulp CSS Task

Create a src/scss folder with a file named main.scss. This is the root Sass file which imports other partials. You can organize these files as you like but, to get started, add:

Code snippet

// main.scss@import 'base/_base';

Create a src/scss/base folder and add a _base.scss file with the following code:

Code snippet

// base/_base.scss partial$font-main: sans-serif;$font-size: 100%;
body {  font-family: $font-main;  font-size: $font-size;  color: #444;  background-color: #fff;}

Insert the following code below the images task in gulpfile.js to create a css processing task:

Code snippet

/**************** CSS task ****************/
  const cssConfig = {
    src         : dir.src + 'scss/main.scss',    watch       : dir.src + 'scss/**/*',    build       : dir.build + 'css/',    sassOpts: {      sourceMap       : devBuild,      outputStyle     : 'nested',      imagePath       : '/images/',      precision       : 3,      errLogToConsole : true    },
    postCSS: [      require('postcss-assets')({        loadPaths: ['images/'],        basePath: dir.build      }),      require('autoprefixer')({        browsers: ['> 1%']      })    ]
  };
  // remove unused selectors and minify production CSS  if (!devBuild) {
    cssConfig.postCSS.push(      require('usedcss')({ html: ['index.html'] }),      require('cssnano')    );
  }
  gulp.task('css', ['images'], () =>
    gulp.src(cssConfig.src)      .pipe(sourcemaps ? sourcemaps.init() : noop())      .pipe(sass(cssConfig.sassOpts).on('error', sass.logError))      .pipe(postcss(cssConfig.postCSS))      .pipe(sourcemaps ? sourcemaps.write() : noop())      .pipe(size({ showFiles:true }))      .pipe(gulp.dest(cssConfig.build))      .pipe(browsersync ? browsersync.reload({ stream: true }) : noop())
  );

Configuration parameters are defined in cssConfig, which sets:

  • the src file src/scss/main.scss
  • a watch folder to any file within src/scss or its subfolders
  • the build folder to build/css
  • gulp-sass options in sassOpts: these are passed to node-sass, which ultimately calls LibSass.

cssConfig.postCSS defines an array of PostCSS plugins and configuration options. The first is postcss-assets, which can resolve image URL paths and pass information to CSS files. For example, if myimage.png is a 400 x 300 bitmap:

Code snippet

.myimage {  width: width('myimage.png'); /* 400px */  height: height('myimage.png'); /* 300px */  background-size: size('myimage.png'); /* 400px 300px */}

It’s also possible to inline bitmap or SVG images. For example:

Code snippet

.mysvg {  background-image: inline('mysvg.svg');  /* url('data:image/svg+xml;charset=utf-8,... */}

autoprefixer is the famous PostCSS plugin which adds vendor prefixes according to information from caniuse.com. In the configuration above, any browser with a global market share of 1% or more will have vendor prefixes added. For example:

Code snippet

filter: opacity(0.5);

becomes:

Code snippet

-webkit-filter: opacity(0.5);filter: opacity(0.5);

Two further PostCSS plugins are added when NODE_ENV is set to production:

  1. usedcss, which removes unused selectors by examining the example index.html file.
  2. cssnano, which minifies the resulting CSS file by removing all comments, whitespace, etc.

A gulp.task named css is then defined. It runs the images task first, since the CSS may depend on images. It then passes data through a series of pipes:

  1. The source folder is examined.
  2. If devBuild is true, the gulp-sourcemaps plugin is initialized. Otherwise, the gulp-noop does nothing.
  3. The gulp-sass plugin transpiles main.scss to CSS using the cssConfig.sassOpts configuration options. Note the on('error') event handler prevents Gulp terminating when a Sass syntax error is encountered.
  4. The resulting CSS is piped into gulp-postcss, which applies the plugins described above.
  5. If sourcemaps are enabled, they’re now appended as a data URI to the end of the CSS.
  6. The gulp-size plugin displays the final size of the CSS file.
  7. The files are saved to the gulp.dest build folder.
  8. Finally, if browsersync is set, a instruction is sent to browser-sync to refresh the CSS in all connected browsers (see below).

Save gulpfile.js, then run the task from the command line:

Code snippet

gulp css

The terminal will show a log similar to this:

Code snippet

Gulp development build[09:22:18] Using gulpfile gulpfile.js[09:22:18] Starting 'images'...[09:22:18] gulp-imagemin: Minified 0 images[09:22:18] Finished 'images' after 80 ms[09:22:18] Starting 'css'...[09:22:18] main.css 1.07 kB[09:22:18] Finished 'css' after 91 ms

Examine the created build/css folder to find a development version of the resulting main.css file containing a sourcemap data:

Code snippet

body {  font-family: sans-serif;  font-size: 100%;  color: #444;  background-color: #fff; }
/*# sourceMappingURL=data:application/json;charset=utf8;base64,...

Automating Your Workflow

Running one task at a time and manually refreshing your browser is no fun. Fortunately, Browsersync provides a seemingly magical solution:

  • It implements a development web server or proxy to an existing server.
  • Code changes are dynamically applied and CSS can refresh without a full page reload.
  • Connected browsers can mirror scrolling and form input. For example, you complete a form on your desktop PC and see it happening on a mobile device.
  • It’s fully compatible with Gulp and other build tools.

Insert the following code below the css task in gulpfile.js to define:

  1. a browsersync processing task

  2. a default Gulp task for monitoring file changes:

Code snippet

/**************** browser-sync task ****************/
  const syncConfig = {    server: {      baseDir   : './',      index     : 'index.html'    },    port        : 8000,    files       : dir.build + '**/*',    open        : false  };
  // browser-sync  gulp.task('browsersync', () =>    browsersync ? browsersync.init(syncConfig) : null  );
  /**************** watch task ****************/
  gulp.task('default', ['css', 'browsersync'], () => {// image changesgulp.watch(imgConfig.src, ['images']);
// CSS changesgulp.watch(cssConfig.watch, ['css']);  });

The browser-sync configuration parameters are defined in syncConfig, which set options such as the port and default file. The browsersync task then initiates accordingly.

Browsersync is able to watch for file changes itself but, in this case, we want to control it via Gulp to ensure that refreshes only occur when CSS changes occur.

The default task is one that runs when gulp is called without a task name. It runs the css and browsersync tasks to build all files initially (the images task is a dependency of css). Then, gulp.watch is passed a folder to monitor and the associated tasks to run.

Save gulpfile.js and run the default task from the command line:

Code snippet

gulp

The terminal will show a log but, unlike before, it won’t terminate and remain running:

Code snippet

Gulp development build[09:43:17] Using gulpfile gulpfile.js[09:43:17] Starting 'images'...[09:43:17] Starting 'browsersync'...[09:43:17] Finished 'browsersync' after 23 ms[Browsersync] Access URLs: -------------------------------------       Local: http://localhost:8000    External: http://1.2.3.4:8000 -------------------------------------          UI: http://localhost:3001 UI External: http://1.2.3.4:3001 -------------------------------------[Browsersync] Serving files from: ./[09:43:17] gulp-imagemin: Minified 0 images[09:43:17] Finished 'images' after 237 ms[09:43:17] Starting 'css'...[09:43:17] main.css 11.3 kB[Browsersync] 1 file changed (main.css)[09:43:17] Finished 'css' after 173 ms[09:43:17] Starting 'default'...[09:43:17] Finished 'default' after 21 ms

Your PC is now running a web server from http://localhost:8000. Other devices on the network can connect to the External URL. Open the URL in a browser or two, then make changes to any .scss file. The results are immediately refreshed.

Examine any element in the DevTools and the Styles panel will show the location of the pre-compiled Sass code. You can click the filename to view the full source:

sourcemap support in DevTools

Finally, press Ctrl + C to stop the Gulp task running in your terminal.

Live Production Code

The NODE_ENV environment variable must be set to production so Gulp tasks know when to produce final code — that is, remove unused CSS, minify files, and disable sourcemap generation. On Linux and macOS terminals:

Code snippet

NODE_ENV=production

Windows Powershell:

Code snippet

$env:NODE_ENV="production"

Windows legacy command line:

Code snippet

set NODE_ENV=production

You can do either of the following:

  • Create production code locally then upload to live servers.
  • Run Gulp tasks directly on the live server. Ideally, NODE_ENV should be permanently set on production machines by modifying the start-up script — for example, add export NODE_ENV=production to the end of a Linux ~/.bashrc file.

Run gulp css to generate the final code.

To return to development mode, change NODE_ENV to development or an empty string.

Next Steps

This article demonstrates a possible Gulp CSS workflow, but it can be adapted for any project:

  • There are more than 3,500 Gulp plugins. Many help with CSS and pre-processing, but you can find others for HTML, templating, image handling, JavaScript, server-side languages, linting and more.
  • There are hundreds of PostCSS plugins and it’ s simple to write your own.

Whichever tools you choose, I recommend you:

  • Automate the most frustrating, time-consuming or performance-improving tasks first. For example, optimizing images could shave hundreds of kilobytes from your total page weight.
  • Don’t over-complicate your build process. A few hours should be adequate to get started.
  • Try other task runners, but don’t switch on a whim!

Github

The code above is available from github.com/craigbuckler/gulp-css. Please use it as you wish!

End of PreviewSign Up to unlock the rest of this title.

Community Questions