12 min read

In this article I will show you how to use Gulp to setup a smooth WordPress themes development workflow, and create a boilerplate to be used as reference for your new themes development.

Gulp

Gulp is a task runner tool to automate and enhance your development workflow, in general. It is used to implement tasks such as optimizing images, compressing scripts, compiling Sass to CSS, and watch files for changes.

TLTR; You could skip all the details and go directly to the Summing up section at the end.

Workflow Requirements

  • The theme source files can be managed in a single folder and repository without polluting the build or WordPress folders.
  • The final built theme files are created in a separate folder, and consist only in the files WordPress requires to run the theme on the webserver (placed typically in a /wp-content/themes/my-new-theme folder).
  • Gulp, its plug-ins and other applications are not contained in the built theme folder. They cannot be accidentally copied to a production server which is unnecessary and could have security implications.

Automated tasks

This boilerplate will implement the following tasks:

  • Copying all newer theme files.
  • Optimizing images.
  • Javascript files processing and concatenation.
  • Sass to PostCSS to native CSS conversion.
  • Watch files for changes.

PostCSS will automatically:

  • Add asset references.
  • Apply vendor prefixes.
  • Group together all matching Media Queries.
  • Minify the resulting CSS code.

Watch files for changes, purpose:

  • Keep all the files updated and in sync with the built theme files in the destination folder.
  • Inject CSS changes without reload the whole page (using Browsersync).
  • Automatically refresh page each time a .php/.js file, an image, or any file of the theme changes (again using BrowserSync).

Getting Started

Let’s go on step by step, installing Node.js and Gulp, and add the configuration to execute the tasks we want.

Install Node.js

Gulp requires you to download and install first Node.js, which will also install NPM, which is its package manager, and it is used to install modules.

To verify that you have installed Node.js and NPM correctly, open the terminal and check their current version entering the commands: node -v and npm -v.

Create a Node.js Project

  1. Open a terminal.
  2. Create a directory for your project with npx mkdirp my-project. Rename my-project as you wish, this will be your project source folder root.
  3. Navigate into it with cd my-project.
  4. Initialize a project with npm init.

This will guide you through giving your project a name, version, description, etc., and finally will create a package.json file, which is used to keep track of various information about the project such as its dependencies.

Install Gulp

  1. If you’ve previously installed gulp globally, run npm rm --global gulp before following these instructions.
  2. Install the gulp command line utility, run: npm install --global gulp-cli.

  3. After having created a package.json file, navigate into the Node.js project folder, if not already there (e.g. with cd my-project).
  4. Install the gulp package in your devDependencies with npm install --save-dev [email protected].
  5. Verify your gulp versions with gulp --version. You should have a CLI and a Local version as well.

A node_modules folder will be created which contains the module code. That should be omitted from your source control system, therefore add node_modules to your .gitignore file.

Note: If you are using npm version >5 package-lock.json is intended to be checked into source control.

Create a gulpfile.js

Tasks in Gulp are added and configured through a gulpfile.js file in your project source folder.

Each Gulp task is an asynchronous JavaScript function, and can be considered public or private.

  • Public tasks are exported from your gulpfile, which allows them to be run by the gulp command.
  • Private tasks are made to be used internally, usually used as part of series() or parallel() composition.

A private task looks and acts like any other task, but an end-user can’t ever execute it independently. To register a task publicly, export it from your gulpfile.js with export.taskname = taskfunction;.

Let’s create a test configuration file:

  1. Using your text editor, create a file named gulpfile.js in your project root with these contents:

     function defaultTask(cb) {
     // place code for your default task here
     cb();
     }
    
     exports.default = defaultTask
    
  2. Run the gulp command in your project directory: gulp.
  3. The default task will run and do nothing. It’s a good sign, you can go ahead.
  4. You can empty the gulpfile.js, this was just a test.

Project Files

I organize all my files under a project source folder, containing the Node.js files (node_modules folder, package.json, and package-lock.json), Gulp configuration files (gulpfile.js), and the theme source files which I keep under a ./src folder.

The theme source files are the source files containing your CSS, Javascript files, and images, to be processed, plus all the normal theme files which usually are just copied as they are. Gulp will process, manipulate, minify all, and copy all of the built theme files to a specific destination folder (e.g. /wp-content/themes/my-new-theme).

I use Vagrant for my WordPress development, therefore, the destination folder of the build is in my case, for a test WordPress website named wp-my-new-theme-test, for a new theme named my-new-theme:

vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/

I add a further four sub-folders to my project source folder (the ./src folder):

  1. theme – The WordPress theme files (all the PHPs, screenshot.png, and the language files), these are just copied as they are, and kept in sync with the destination folder.
  2. images – Images used by your theme.
  3. scss – Sass SCSS source files.
  4. js – Any number of separate client-side JavaScript source files.

My working tree looks like this one:

my-new-theme (project source folder)
│ 
├── node_modules (add it to your .gitignore)
│ 
├── src (theme source files)
│   ├── images
│   ├── js
│   ├── scss
│   └── theme (the WordPress theme files)
│ 
├── gulpfile.js
├── package.json
├── package-lock.json (since npm >5 to be checked in)
└── ... other configuration files

Adding Tasks to Gulp

Some tasks in this tutorial introduce the need of packages dependency which need to be installed. These are the tasks Gulp will be in charge of:

  1. Synching Theme Files
  2. Optimizing Images
  3. Saas Compilation
  4. JavaScript Processing
  5. Run All Tasks at once
  6. Enable File Watching and Browsersync
  7. Add a Default Task

Installing and Including Dependencies

From the project source folder, run the following npm command to install all the Gulp plug-ins as development dependencies:

npm install --save-dev autoprefixer browser-sync css-mqpacker cssnano gulp-concat gulp-deporder gulp-imagemin gulp-newer gulp-postcss gulp-sass gulp-strip-debug gulp-uglify gulp-noop postcss-assets

Note: gulp-util has been deprecated. It is used by the gulp-deporder package. I already created a pull request to solve this issue, it was accepted. We are waiting for a release.

Using your text editor, edit the file gulpfile.js in your project source folder, replace its content, if any, with these contents:

// Gulp.js configuration
'use strict';

const

  // ** change these two to yours **
  wordpress_project_name = 'your-site',
  theme_name = 'your-site',
  browserSyncProxy = 'http://local.your-site.test/',

  // source and build folders, ** change this to yours **
  dir = {
    src         : 'src/',
    build       : `../../vagrant-local/www/${wordpress_project_name}/public_html/wp-content/themes/${theme_name}/`
  },

  // Gulp and plugins
  { src, dest, series, parallel, watch } = require('gulp'),
  noop          = require("gulp-noop"),
  newer         = require('gulp-newer'),
  imagemin      = require('gulp-imagemin'),
  sass          = require('gulp-sass'),
  postcss       = require('gulp-postcss'),
  deporder      = require('gulp-deporder'),
  concat        = require('gulp-concat'),
  stripdebug    = require('gulp-strip-debug'),
  uglify        = require('gulp-uglify'),
  browserSync   = require('browser-sync').create(),
  log           = require('fancy-log')
;

// For BrowserSync
const reload = (cb) => { browserSync.reload(); cb(); };

Note: It’s your task to set correctly the values of wordpress_project_name, theme_name, dir.src, dir.build (basically source and build destination which probably will differ from mine, and you can take it as an example).

Synching Theme Files

The files in the theme folder of the project source folder should be simply copied to the destination folder, no processing needed. These are all the theme PHP files, the screenshot.png, and all the language files in the folder language.

Append the following configuration task to your file gulpfile.js:

// theme files settings
const themefiles_settings = {
  src           : dir.src + 'theme/**/*',
  build         : dir.build
};

// copy theme files
const themefiles = function() {
  return src(themefiles_settings.src)
    .pipe(newer(themefiles_settings.build))
    .pipe(dest(themefiles_settings.build));
};

exports.themefiles = themefiles;
// ---

Save, and create any file or folder in the source template folder. Then enter the following command:

gulp themefiles

All the files under theme folder will be recursively copied to the destination folder (specified in dir.dest, in my case that would be vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/).

Optimizing Images

Image files can often be compressed further using tools such as imagemin. Add the following code to gulpfile.js:

// image settings
const images_path = 'assets/images/';
const images_settings = {
  src         : dir.src + 'images/**/*',
  build       : dir.build + images_path
};

// image processing
const images = function() {
  return src(images_settings.src)
    .pipe(newer(images_settings.build))
    .pipe(imagemin())    
    .pipe(dest(images_settings.build));
};

exports.images = images;
// ---

Save then run gulp images. Compressed versions of any new or updated images in the source images folder are copied to the destination folder.

Saas Compilation

// CSS settings
var scss_settings = {
  src         : dir.src + 'scss/style.scss',
  watch       : dir.src + 'scss/**/*',
  build       : dir.build,
  sassOpts: {
    outputStyle     : 'nested',
    imagePath       : images_settings.build,
    precision       : 3,
    errLogToConsole : true
  },
  processors: [
    require('postcss-assets')({
      loadPaths: [images_path],
      basePath: dir.build,
      baseUrl: `/wp-content/themes/${theme_name}/`
    }),
    require('autoprefixer')({
      browsers: ['last 2 versions', '> 2%']
    }),
    require('css-mqpacker'),
    require('cssnano')
  ]
};

// SCSS processing
const scss = function() {
  return src(scss_settings.src)
    .pipe(sass(scss_settings.sassOpts))
    .pipe(postcss(scss_settings.processors))
    .pipe(dest(scss_settings.build))
    .pipe(browserSync.active ? browserSync.reload({ stream: true }) : noop());
};
const css = series(images, scss);

exports.css = css;
// ---

Launch this new task with gulp css to:

  • run the Gulp images task first (images may be required in your CSS)
  • compile the Sass code in the source scss/style.scss file using the fast LibSass compiler
  • use PostCSS to automatically add asset references, apply vendor prefixes, pack media queries together, and minify the resulting CSS code
  • output the stylesheet to the destination folder (specified in dir.dest, in my case that would be vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/).
  • force a Browsersync CSS reload (more about that later).

The source scss/style.scss file must include the WordPress theme meta data at the top. It is important to use /*! as the first line. This ensures the cssnano minifier does not remove the comment and render your theme unusable.

/*!
Theme Name: Gutenberg
Theme URI: https://github.com/WordPress/gutenberg-theme/
Author: wordpressdotorg
Author URI: https://wordpress.org
Description: Showcasing themeing with Gutenberg.
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: gutenbergtheme
Tags: translation-ready

This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned with others.

gutenbergtheme is based on Underscores http://underscores.me/, (C) 2012-2016 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.

Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal http://necolas.github.io/normalize.css/
*/
// etc...

The postcss-assets plugin allows you to refer to image assets using code such as:

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

You can also inline images with automatic Base64 encoding:

.widget2 {
  background-image: inline('myimage.jpg');
}

JavaScript Processing

Add the following code to gulpfile.js:

// JavaScript settings
const js_settings = {
  src         : dir.src + 'js/**/*',
  build       : dir.build + 'js/',
  filename    : 'scripts.js'
};

// JavaScript processing
const js = function() {

  return src(js_settings.src)
    .pipe(deporder())
    .pipe(concat(js_settings.filename))
    .pipe(stripdebug())
    .pipe(uglify())
    .pipe(dest(js_settings.build))
    .pipe(browserSync.active ? browserSync.reload({ stream: true }) : noop());
};

exports.js = js;
// ---

Run this new task with gulp js to:

  • process all JavaScript files in the source js folder
  • order the files appropriately. Add comments at the top of your JavaScript files to declare dependencies, e.g. // requires: lib1.js or // requires: config.js lib1.js.
  • concatenate into a single file
  • strip all debugging and console logging statements
  • minify the code
  • output the resulting code to the destination folder (specified in dir.dest + '/js/scripts.js', in my case that would be vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/js/scripts.js).
  • force a Browsersync CSS reload (more about that later).

Run All Tasks

Rather than calling each task separately, we can add the following code to gulpfile.js:

// run all tasks
const build = parallel(themefiles, css, js);
exports.build = build;
// ---

You can now use gulp build to run the themefiles, js, css and images tasks in parallel. (Note images is a dependency of the css task so we need not call it directly.)

Enable File Watching and Browsersync

Your workflow can be radically improved by:

  1. Letting Gulp watch for file changes before launching the appropriate task.
  2. Automatically reloading CSS and JavaScript files when they change (without a page refresh).
  3. Automatically refreshing the page when a template file changes.

First, we need to define a browsersync task in gulpfile.js. This will create a proxy server to your web server running WordPress on localhost (change this domain or use an IP address as necessary):

// Browsersync options
const syncOpts = {
  proxy       : browserSyncProxy,
  open        : false,
  notify      : false,
  ghostMode   : false,
  watch       : false, // do not refresh on changes automatically
  injectChanges: true, // Inject CSS changes
  ui: {
    port: 8001
  }
};

Now add a watch task to run Browsersync, watch for file changes and run the appropriate task:

// watch for file changes
const watch_all = function() {

  browserSync.init(syncOpts);

  // page changes
  watch(themefiles_settings.src, series(themefiles, reload));

  // image changes
  watch(images_settings.src, series(images, reload));

  // CSS changes
  watch(scss_settings.watch, css);

  // JavaScript main changes
  watch(js_settings.src, js);
};

exports.watch_all = watch_all;

Add a Default Tasks

Finally, add a default Gulp task which runs an initial build and starts the watch task:

// default task
exports.default = series(build, watch_all);
// ---

Now run gulp from the command line. The console will display output which includes lines similar to:

Rather than loading your development site from http://localhost/, enter the address http://localhost:3000/ or the External URL if you are viewing from another device. Your WordPress site will load as before but Gulp will watch for changes and apply the updates immediately. You’ll need never switch to your browser and click refresh again!

Hit Ctrl/Cmd + C when you want to stop Gulp processing.

TLTR - Summing up

To summarize in a few steps:

  1. Download and install first Node.js.
  2. Fork or clone the boilerplatw: wp-theme-gulp-basic
  3. Go into the project folder cd wp-theme-gulp-basic.
  4. Initialize the project with npm install.
  5. Create the project source folder with mkdir src src/images src/js src/scss src/theme.
  6. Copy your theme’s files into ./src/theme.
  7. Place your style.scss into src/scss or create an empty one with touch src/scss/style.scss (this file is required).
  8. Place into the src/images, src/js, and src/scss folders everything that must be processed by Gulp.
  9. Run gulp (Ctrl/Cmd + C when you want to stop watching).

References

The following articles were of big help in my investigation:



If this article was useful to you, please share it using your favorite way for doing it! If you have suggestions of improvement or you’ve found something wrong, please let me know!


You are not being tracked on this website, no analytics, no cookies taken. I am still looking for a good plugin for comments which respects people's privacy. This website is hosted by Gitlab Pages which as far as stated by their general GitLab's privacy policy terms they collects potentially personally-identifying information like Internet Protocol (IP) addresses as mentioned in this thread.