How to Use Gulp.js to Automate Your CSS Tasks

Share this article

Using Gulp for 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 repetitive tasks that are essential for a modern website and fast performance, such as:

  • converting or transpiling
  • 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
  • outputing sourcemaps for use in browser devtools
  • live-reloading in a browser when source files change.

Why Use Gulp?

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

Gulp is a few years old, but it’s 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 removing CSS sourcemaps when building the final files for live deployment.

Demonstration Code

This tutorial’s code is available from GitHub. Ensure Git and Node.js are installed, then enter the following commands in your terminal to install and run the demonstration:

git clone https://github.com/craigbuckler/gulp4-css
cd gulp4-css
npm i gulp-cli -g
npm i
gulp

In your browser, navigate to http://localhost:8000/ or the External URL shown.

Alternatively, you can create your own project following the steps below.

Example Project Overview

This tutorial uses Gulp 4.0. This is the most recent stable version and the current default on npm.

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

A CSS file is built using:

  1. the Sass preprocessor, which compiles .scss syntax and partials into a single main.css file, and
  2. the PostCSS postprocessor, which supplements main.css to provide asset management, vendor-prefixing, minification and more via its own plugins.

Using both a preprocessor and a postprocessor provides coding flexibility.

Sass may not be as essential as it once was, but it remains a practical option for file splitting, organization, (static) variables, mixins and nesting. Alternative preprocessors include gulp-less for Less and gulp-stylus for Stylus.

The gulp-sass plugin is used in the example code below. This uses node-sass to call the LibSass C/C++ engine and is currently the fastest option. However, you could consider Dart Sass, which has become the primary implementation and receives language updates first. To use Dart Sass, change all npm installation and require references from gulp-sass to gulp-dart-sass accordingly.

Finally, you could forego a preprocessor and use PostCSS for all CSS transformations. It offers a range of plugins including those which replicate many (but not all) Sass syntax options.

Getting Started with Gulp

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

  1. Ensure a recent edition of 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 subfolder for source files: mkdir src.

The example project uses the following subfolders:

  • 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 built stylesheet. For example:

<!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

For this tutorial, most Node.js modules are installed as project dependencies so the CSS can be built on local development or live production servers. Alternatively, you could install them as development dependencies using the npm --save-dev option so they must be built locally prior to deployment.

To install Gulp and all plugins, run the following npm command in your terminal from the project folder:

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

All modules will be installed and listed in the "dependencies" section of package.json.

The browser-sync test server can now be installed — as a development dependency, since it should never be required on a live production device:

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 in your project root. Create it, then open the file in your editor (VS Code is a great option). Add the following code:

(() => {

  '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 — set true when NODE_ENV is blank or set to development
  • dir.src — the src/ source file folder
  • dir.build — the build/ build folder
  • Gulp and all plugin modules

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

Gulp Image Task

Create a src/images folder and copy some image files into that or any of its subfolders.

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

/**************** images task ****************/
const imgConfig = {
  src           : dir.src + 'images/**/*',
  build         : dir.build + 'images/',
  minOpts: {
    optimizationLevel: 5
  }
};

function images() {

  return gulp.src(imgConfig.src)
    .pipe(newer(imgConfig.build))
    .pipe(imagemin(imgConfig.minOpts))
    .pipe(size({ showFiles:true }))
    .pipe(gulp.dest(imgConfig.build));

}
exports.images = images;

Configuration parameters are defined in imgConfig, which sets:

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

An images function returns a Gulp stream, which pipes data through a series of plugins:

  1. gulp.src is passed a source folder glob to examine
  2. the gulp-newer plugin removes any newer images already present in the build folder
  3. the gulp-imagemin plugin optimizes the remaining files
  4. the gulp-size plugin reports the resulting size of all processed files
  5. the files are saved to the gulp.dest build folder.

Finally, a public Gulp images task is exported that calls the images function.

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

gulp images

The terminal will show a log such as:

Gulp development build
[18:03:38] Using gulpfile /gulp4-css/gulpfile.js
[18:03:38] Starting 'images'...
[18:03:38] cave-0600.jpg 48.6 kB
[18:03:38] icons/alert.svg 308 B
[18:03:38] icons/fast.svg 240 B
[18:03:38] icons/reload.svg 303 B
[18:03:38] cave-1200.jpg 116 kB
[18:03:38] cave-1800.jpg 162 kB
[18:03:38] gulp-imagemin: Minified 3 images (saved 203 B - 19.3%)
[18:03:38] all files 328 kB
[18:03:38] Finished 'images' after 507 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 are 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:

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

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

// 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 define a css processing task:

/**************** CSS task ****************/
const cssConfig = {

  src         : dir.src + 'scss/main.scss',
  watch       : dir.src + 'scss/**/*',
  build       : dir.build + 'css/',
  sassOpts: {
    sourceMap       : devBuild,
    imagePath       : '/images/',
    precision       : 3,
    errLogToConsole : true
  },

  postCSS: [
    require('usedcss')({
      html: ['index.html']
    }),
    require('postcss-assets')({
      loadPaths: ['images/'],
      basePath: dir.build
    }),
    require('autoprefixer')({
      browsers: ['> 1%']
    }),
    require('cssnano')
  ]

};

function css() {

  return 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());

}
exports.css = gulp.series(images, css);

Configuration parameters are defined in cssConfig which sets:

  • the .src file to src/scss/main.scss
  • a .watch folder to any file within src/scss or its subfolders
  • the .build folder to build/css, and
  • node-sass options passed by gulp-sass in .sassOpts.

cssConfig.postCSS defines an array of PostCSS plugins and configuration options. The first is usedcss, which removes unused selectors by examining the example index.html file.

This is followed by postcss-assets, which can resolve image URL paths and information in the CSS files. For example, if myimage.png is a 400×300 PNG bitmap, the following code:

.myimage {
  background-image: resolve('myimage.jpg');
  width: width('myimage.png');
  height: height('myimage.png');
  background-size: size('myimage.png');
}

is translated to:

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

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

.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:

user-select: none;

becomes:

-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

Finally, cssnano minifies the resulting CSS file by rearranging properties, removing unnecessary units, deleting whitespace, and so on. Several alternatives are available, such as post-clean, but cssnano generates a slightly smaller file in the demonstration code.

A css function returns a Gulp stream which pipes data through a series of plugins:

  1. gulp.src is passed a source src/scss/main.scss file to examine.
  2. If devBuild is true, the gulp-sourcemaps plugin is initialized. Otherwise, the gulp-noop does nothing.
  3. The gulp-sass plugin preprocesses 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 the sourcemap is enabled, it’s appended as data to the end of the CSS file.
  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. If browsersync is enabled (devBuild must be true), an instruction is sent to browser-sync to refresh the CSS in all connected browsers (see below).

Finally, a public css task is exported which calls the images function followed by the css function in turn using gulp.series(). This is necessary because the CSS relies on images being available in the build/images folder.

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

gulp css

The terminal will show a log such as:

Gulp development build
[14:16:25] Using gulpfile /gulp4-css/gulpfile.js
[14:16:25] Starting 'css'...
[14:16:25] Starting 'images'...
[14:16:25] gulp-imagemin: Minified 0 images
[14:16:25] Finished 'images' after 61 ms
[14:16:25] Starting 'css'...
[14:16:26] main.css 9.78 kB
[14:16:26] Finished 'css' after 900 ms
[14:16:26] Finished 'css' after 967 ms

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

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 all browsers is no fun. Fortunately, Browsersync provides a seemingly magical solution:

  • It implements a development web server or proxies 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 a server function to launch Browsersync and a watch function to monitor file changes:

/**************** server task (private) ****************/
const syncConfig = {
  server: {
    baseDir   : './',
    index     : 'index.html'
  },
  port        : 8000,
  open        : false
};

// browser-sync
function server(done) {
  if (browsersync) browsersync.init(syncConfig);
  done();
}

/**************** watch task ****************/
function watch(done) {

  // image changes
  gulp.watch(imgConfig.src, images);

  // CSS changes
  gulp.watch(cssConfig.watch, css);

  done();

}

/**************** default task ****************/
exports.default = gulp.series(exports.css, watch, server);

The browser-sync configuration parameters are defined in syncConfig, which sets options such as the port and default file.

The server function initiates Browsersync and a done() callback is executed so Gulp knows it has completed.

Browsersync is able to watch for file changes itself but, in this case, we want to control it via Gulp to ensure refreshes only occur when a Sass change causes main.css to be rebuilt.

The watch function uses gulp.watch() to monitor files and trigger the appropriate function (optionally within gulp.series() or gulp.parallel() methods). Again, a done() callback is executed once the function has completed.

Finally, a default task is created which can be executed by running gulp without arguments. It calls the css task (which also runs images()) to build all files, runs the watch() function to monitor updates, and launches the Browsersync server.

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

gulp

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

Gulp development build
[14:32:05] Using gulpfile /gulp4-css/gulpfile.js
[14:32:05] Starting 'default'...
[14:32:05] Starting 'images'...
[14:32:05] gulp-imagemin: Minified 0 images
[14:32:05] Finished 'images' after 64 ms
[14:32:05] Starting 'css'...
[14:32:05] main.css 9.78 kB
[14:32:05] Finished 'css' after 843 ms
[14:32:05] Starting 'watch'...
[14:32:05] Finished 'watch' after 33 ms
[14:32:05] Starting 'server'...
[14:32:06] Finished 'server' after 19 ms
[14:32:06] Finished 'default' after 967 ms
[Browsersync] Access URLs:
 -------------------------------------
       Local: http://localhost:8000
    External: http://192.168.1.234:8000
 -------------------------------------
          UI: http://localhost:3001
 UI External: http://localhost:3001
 -------------------------------------
[Browsersync] Serving files from: ./

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 and disable sourcemap generation. On Linux and macOS terminals:

NODE_ENV=production

Windows Powershell:

$env:NODE_ENV="production"

Windows legacy command line:

set NODE_ENV=production

You can either:

  1. Install Gulp and run tasks directly on the live server. Ideally, NODE_ENV should be permanently set on production machines by modifying the startup script. For example, add export NODE_ENV=production to the end of a Linux ~/.bashrc file.
  2. Create production code locally, then upload to live servers.

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,700 Gulp plugins. Many help with CSS, but you’ll 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!

The code above is available from GitHub and you can view the whole gulpfile.js configuration. Please use it as you wish.

Frequently Asked Questions (FAQs) about Automating CSS Tasks with Gulp

What is Gulp and why is it important in automating CSS tasks?

Gulp is a popular task runner in JavaScript that automates repetitive tasks such as minification, compilation, unit testing, and linting. It uses a code-over-configuration approach, making it simpler and more straightforward to use. Gulp is important in automating CSS tasks because it helps to streamline the development process, reduce errors, and increase productivity. It allows developers to write and test code more efficiently by automating tasks like compiling Sass or Less to CSS, auto-prefixing CSS properties, and concatenating and minifying CSS files.

How do I install Gulp?

To install Gulp, you first need to have Node.js and npm (Node Package Manager) installed on your computer. Once you have these, you can install Gulp globally by running the command npm install --global gulp-cli in your terminal. This will allow you to use the gulp command anywhere on your system.

How do I create a Gulp task?

Creating a Gulp task involves defining a function and exporting it. For instance, to create a task that minifies CSS files, you would first require the necessary plugins, then define a function that sources the CSS files, pipes them through the minification plugin, and outputs the minified files to a destination folder. Finally, you would export the function as a task using exports.default = minifyCss.

How can I automate CSS tasks with Gulp?

Automating CSS tasks with Gulp involves creating tasks for each specific CSS task you want to automate, such as compiling Sass files to CSS, auto-prefixing CSS properties, and minifying CSS files. You can then use the gulp.series() and gulp.parallel() methods to run these tasks in series or parallel, respectively.

What are Gulp plugins and how do I use them?

Gulp plugins are reusable pieces of code that add extra functionality to Gulp. They can be used to perform a wide range of tasks, from compiling Sass to CSS, to linting JavaScript files, to refreshing your browser automatically whenever a file is saved. To use a Gulp plugin, you first need to install it using npm, then require it in your Gulpfile, and finally use it in a Gulp task.

How do I handle errors in Gulp?

Handling errors in Gulp can be done using the .on('error', function) method. This method listens for the ‘error’ event and executes a function whenever an error occurs. The function can be used to log the error message and prevent Gulp from crashing.

How do I watch files for changes with Gulp?

Watching files for changes with Gulp can be done using the gulp.watch() method. This method takes two arguments: the path of the files to watch, and the tasks to run whenever a file changes. For instance, gulp.watch('src/css/*.css', cssTask) would watch all CSS files in the ‘src/css’ directory and run the ‘cssTask’ whenever a file changes.

How do I run multiple tasks in Gulp?

Running multiple tasks in Gulp can be done using the gulp.series() and gulp.parallel() methods. The gulp.series() method runs tasks one after the other, while the gulp.parallel() method runs tasks simultaneously. For instance, gulp.series(clean, gulp.parallel(css, javascript)) would first run the ‘clean’ task, then run the ‘css’ and ‘javascript’ tasks simultaneously.

How do I optimize images with Gulp?

Optimizing images with Gulp can be done using the gulp-imagemin plugin. This plugin minifies PNG, JPEG, GIF and SVG images. To use it, you first need to install it using npm, then require it in your Gulpfile, and finally use it in a Gulp task that sources the images, pipes them through the plugin, and outputs the optimized images to a destination folder.

How do I use Gulp with a live server?

Using Gulp with a live server can be done using the browser-sync plugin. This plugin creates a live server that refreshes your browser automatically whenever a file is saved. To use it, you first need to install it using npm, then require it in your Gulpfile, and finally use it in a Gulp task that initializes the server and watches files for changes.

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

AdvancedCSScss preprocessorsGulpsass
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week