Setup an asset pipeline for WordPress theme development

Karel September 3, 2015

#

This article is part of a series about how to make A slightly less shitty WordPress developer workflow. Check out the other parts if you haven’t already. You can also find the project on Github.

A WordPress theme development assets pipeline

Web development techniques have evolved a lot in the past few years. We have great superset languages like SASS that make our developer life a bit more pleasant, we have tools to optimize the size of our assets (images, css, js, fonts) and make sure we are making the web a slighty faster (better) place.

But the sad truth is that WordPress hasn’t incorporated or even tried to promote these practices among theme developers. And most WordPress website are not well optimized or fast, actually it is the total opposite and that’s a shame! Let’s fix this!

At the end of the article, we will have:

  1. Setup Gulp
  2. Structure the theme for the asset pipeline
  3. Use SASS for better CSS
  4. Concatenate your Javascript
  5. Copy your images and fonts
  6. Work with templates

1. Setup Gulp

An asset pipeline is a fancy name to say that we will transform and optimize every asset we use in our theme: css, js, images, fonts… If you need inspiration for what to optimize, Ezequiel Bruni recently published The quick guide to speeding up your websites. It covers a lot of important techniques!

We will use Gulp to run the transformations in the background. Gulp is a build tool, a way to automate the transformations we need on our assets. There are couple other options like Grunt and Broccoli but Gulp is cleaner and simpler.

To get started, install Node, install Gulp and then run npm install inside your project. Then we will need to tell Gulp where is our theme located, and to do that you create a configuration file:

cp gulp.json.example gulp.json

Now open the gulp.json in your favourite editor, and simply add the name of your theme. Don’t worry about the other options, we will review in the next article of the series:

{
  "theme": "the-theme",
  "productionAssetURL": "",
  "aws": {
    "params": {
      "Bucket": ""
    },
    "accessKeyId": "",
    "secretAccessKey": ""
  }
}

Then simply run gulp. It will run the default task which is intended for development. The gulp task is in gulfile.js if you are curious and we will talk about it later. But for now, let’s structure our theme and assets.

2. Structure the theme for the asset pipeline

Typically, you will put the raw assets in an assets/ folder that you will version control. And the asset pipeline will transform these assets into a dist/ folder that you will NOT version control. The reason is that there is no point to version twice the same file and that you will never manually modify files in dist/. Go ahead and create the assets/ folder with the following subfolders:

wp-content/themes/the-theme/assets
wp-content/themes/the-theme/assets/scss
wp-content/themes/the-theme/assets/js
wp-content/themes/the-theme/assets/images
wp-content/themes/the-theme/assets/fonts
wp-content/themes/the-theme/assets/templates

And add this to your .gitignore:

wp-content/themes/the-theme/dist

All our assets will go in their respective folder based on their type. So web fonts will be in fonts/, our styles in scss/ and so on.

3. Use SASS for better CSS

Let’s create some files:

wp-content/themes/the-theme/assets/scss/style.scss
wp-content/themes/the-theme/assets/scss/pages/_home.scss
wp-content/themes/the-theme/assets/scss/pages/_about.scss

The files starting with an underscore like _home.scss are partials. It means that they won’t directly be compiled into CSS but can be included into another file. In style.scss, simply import our partial files:

@import "pages/home";
@import "pages/about";

The goal here is to have multiple SCSS files to better organize our CSS, knowing that we will end up with a single style.css at the end. So in you template you can simply include the style.css file:

<link rel="stylesheet" href="/wp-content/themes/the-theme/dist/css/style.css">

Note that we are grabbing the file from the dist/ folder, this is where the SCSS is compiled.

4. Concatenate your Javascript

The same way it is nice to have multiple files for CSS, it is nice to have the same organization for Javascript. But we also want a single file to include in our HTML. Since we are not using a preprocessor, or something like Coffeescript, the process is a little more manual.

First, you can create couple js files like so for instance:

wp-content/themes/the-theme/assets/js/script.js
wp-content/themes/the-theme/assets/js/vendors/jquery.js

And modify the gulpfile.js accordingly under the compile-scripts task:

gulp.task('compile-scripts', function () {
  return (
    gulp.src([
      assetPath + '/js/vendors/jquery.js'
      assetPath + '/js/script.js'
    ])
    .pipe(concat('script.js'))
    .pipe(gulp.dest(distPath + '/js'))
  );
});

The main reason you have to list your JS files manually in the gulpfile is to control the order in which to concatenate them. Here it is likely that our script.js will depend on jquery.js so we load it first. And we end up with a single script.js file that you can include in your theme:

<script src="/wp-content/themes/the-theme/dist/js/script.js"></script>

5. Copy your images and fonts

Like for sass and js, you put images and fonts in their respective folder under assets/. In development we don’t do anything to images and fonts, we simply copy them into the dist/ folder so you can link to them. When you add an image to assets/images/logo.svg and you need to use it in your theme, you should use the dist/ path:

# Good - link the dist folder
<img src="/wp-content/themes/the-theme/dist/images/logo.svg" alt="" />

# Bad - do not EVER link the assets folder
<img src="/wp-content/themes/the-theme/assets/images/logo.svg" alt="" />

And the same goes for CSS paths. If you want to include an image and font path into your CSS, link to the dist/ path:

.logo {
  background: url("/wp-content/themes/visible/dist/images/logo.svg");
}

@font-face {
  font-family: "avenir-next-regular";
  src: url("/wp-content/themes/the-theme/dist/fonts/font.woff") format("woff"),
  /* ... */
}

6. Work with templates

This is with no doubt going to be the weirdest part if you have ever worked on a WordPress theme. We will actually store all our PHP templates inside of assets/templates/. Any theme template should be stored there, for instance you could have:

wp-content/themes/the-theme/assets/templates/index.php
wp-content/themes/the-theme/assets/templates/single.php

But you can keep non template files where you normally put them, like wp-content/themes/the-theme/functions.php for instance. When you run gulp, it actually copies the template to the normal path:

wp-content/themes/the-theme/index.php
wp-content/themes/the-theme/single.php

And you actually want to version contol both. The one in assets/ because it is the file that you are developing on and the one at the root of your theme folder because WordPress needs it. This might be really confusing and seem unecessary but there is a good reason behind it. When you are developping, you will point to your local assets:

<?php get_header(); ?>

<img src="/wp-content/themes/the-theme/dist/images/logo.svg" />

<?php get_footer(); ?>

So if you update your logo to a new version, it will update right away. But when deploying, we will want to show the logo stored in our CDN. So we will want the template to become:

<?php get_header(); ?>

<img src="https://some.cloudfront.net/images/logo-1234.svg" />

<?php get_footer(); ?>

It means that a template has multiple versions possible, one for development, one for production. Because of that, we treat template like all other assets. We store them in the assets/ folder and modify them according to our environment.

Pro Tip: You could argue that a theme option could handle where the file is actually stored. That’s right but it also has its limits. Our style.css and script.js are different in development (non minified) from production (minified). It means that they will have a different checksum and when we version assets (more on that in Part 3), the fingerprint will be different. We need to replace both the fingerprint and the path. The only better solution could to write a custom loader for assets in the theme, but that’s outside of the scope of this series.

To be honest, this is the one part that sucks the most about this entire workflow, it sucks to have to version control twice the same file but there is no way around it if you want to make sure that your theme “just work” when you install it in WordPress. If you have a better suggestion, let us know!

Conclusion

It might sounds weird but there is a very good reason why we store assets in assets/ but use the dist/ path in our templates and SASS files. When comes the time to deploy our theme, we will want to store our assets in a CDN. But all our paths in our theme are currently local to the server. When we will deploy, we will be able to do a search and replace in our templates and CSS to make sure all the urls are updated to our CDN. In the next article, we will see how Gulp helps us to further optimize the assets before deploying them.

Subscribe to the Visible Newsletter

Every week we share the best resources we've come across & what's new with Visible!