Gulpifying Jekyll

It’s important to use the right tools for the job. Investing in good infrastructure and setup reduces future fiddling and makes actual work happen faster.

This post is a walkthrough for setting up a Gulp-driven Jekyll site. Jekyll is a static site generator, which turns organized posts and pages written in Markdown into a bunch of static HTML files. It’s makes a site extremely fast for visitors because there’s no server-side scripting, database, etc. Gulp is an efficient JavaScript task runner.

I use Jekyll to power Beer Review. At some point I fired up generator-jekyllrb, a Yeoman generator, to compile Sass, minify code, and optimize images within a Grunt-based workflow. It served me well, but builds run slowly, and the setup has tasks that I don’t need. Plus, I’ve grown to prefer the code-over-configuration approach of Gulp.

Set Up

Let’s get ready. Here’s what you’ll need:

  • Ruby (already installed)
  • Jekyll (2.5.3 as of this writing): gem install jekyll
  • Libsass: brew install libsass
  • Node and NPM: brew install node
  • Gulp: npm install -g gulp

(Commands assume you’re on a Mac and have Homebrew installed.)

First, we need a Jekyll project to work on. Use an existing one or make a new one by running jekyll new [project_directory], replacing [project_directory] with your desired directory name.

Now, we need a starter gulpfile.js and package.json. I really enjoy the generator-gulp-webapp: it’s maintained by the Yeoman team, and it always has the latest and greatest stuff. We’ll borrow their files.

Here’s how retrieve this generator’s files (alternatively, download them here):

  1. Install Yeoman: npm install -g yo
  2. Install the generator: npm install -g generator-gulp-webapp
  3. Make a new temporary directory and cd into it
  4. Use Yeoman to generate a new project: yo gulp-webapp
  5. Follow the prompts
  6. Stop Yeoman from installing dependencies

We’re ready to set up the Jekyll project directory. Move all Jekyll-assocated files and directories into a new directory named app (to follow the Yeoman standard). A couple exceptions: keep the configuration file (_config.yml), any READMEs, Gemfile, etc. in the project root. It should look something like this:


Now, move gulpfile.js and package.json from the temporary gulp-webapp directory into your Jekyll project directory. You can optionally move the dotfiles (.jshintrc, .editorconfig, etc.) if you want their functionality. It directory structure should now look like this:


Once everything’s in place, run npm install in the Jekyll directory to install the project’s dependencies.


First up is the project’s stylesheet (assuming Sass):

  1. Go into your root Sass file (app/css/main.scss in a default Jekyll install) and remove the front matter (everthing between and including the ---s). This is necessary for running Sass through Jekyll, but it chokes up regular Sass.
  2. Ensure the styles task in the gulpfile.js points to the right file:

    -  return gulp.src('app/styles/main.scss')
    +  return gulp.src('app/css/main.scss')
  3. Add the app/sass_ directory to Sass's includes:

    -       includePaths: ['.'],
    +       includePaths: ['.', 'app/_sass'],

Building Jekyll

Jekyll needs to know about the new directory structure. In keeping with Yeoman standards, the site’s code lives in app and the built and bundled code lives in dest. Jekyll doesn’t need worry about images or styles, so let’s set the appropriate excludes. Add these to your _config.yml file:

source: "app"
destination: "dist"
exclude: ["img", "css", "_sass", "js"]
keep_files: ["img", "css", "js"]

We’ll need a way to fire up Jekyll through Gulp. gulp-shell works perfectly for this. Install it: npm install gulp-shell --save-dev. Then, add a jekyll task to the gulpfile.js:

gulp.task('jekyll', function () {
  return gulp.src('_config.yml')
      'jekyll build --config <%= file.path %>'
    .pipe(reload({stream: true}));

Wire up the new task in your gulpfile.js:

- gulp.task('html', ['styles'], function () {
+ gulp.task('html', ['styles', 'jekyll'], function () {
- gulp.task('html', ['styles'], function () {
+ gulp.task('serve', ['styles', 'jekyll'], function () {

You’ll also want to change your gulp watches:[
-     'app/*.html',
-     'app/scripts/**/*.js',
+     'app/js/**/*.js',
-     'app/images/**/*',
+     'app/img/**/*',
-     '.tmp/fonts/**/*'
    ]).on('change', reload);

+'app/**/*.{md,markdown,html}', ['jekyll']);

This will ensure BrowserSync reloads the server when your Jekyll files change.

Other Changes

Make sure your feed.xml isn’t overridden in the extras task:

  return gulp.src([
+   '!app/feed.xml',
  ], {

I also dropped the fonts task and any references to Bootstrap, which comes from the generator-gulp-webapp by default.

A note on Base URL

Beer Review operates from a base url (/beer-review/). Unfortunately, because of the new setup, Jekyll’s baseurl configuration can’t be relied upon. I found a workaround that involves string replacing. It isn’t the prettiest, but it works.

npm install gulp-replace --save-dev

Add this to the html task:

+   var baseurl = 'beer-review'
+   var htmlPattern = /(href|src)(=["|']?\/)([^\/])/gi;
+   var htmlReplacement = '$1$2' + baseurl + '/$3';
+   var cssPattern = /(url\(['|"]?\/)([^\/])/gi;
+   var cssReplacement = '$1' + baseurl + '/$2';

      .pipe($.if('*.html', $.minifyHtml({conditionals: true, loose: true})))
+     .pipe($.if('*.html', $.replace(htmlPattern, htmlReplacement)))
+     .pipe($.if('*.css', $.replace(cssPattern, cssReplacement)))

Replace baseurl with your desired path.

That’s that! See the complete gulpfile.js here. Happy building!

Moving To MRN

I’m very excited to announce that I’m joining the web team at the Mind Research Network as a software engineer. MRN is non-profit focused on academic research of mental illness and brain disorders. They’re based in Albuquerque, New Mexico, but I shall remain in Portland as a remote employee.

These transitions are always tough. I’m leaving Electric Pulp, one of the oldest web firms in the country. I recognize this last year’s projects to be some of my very best. I’m fortunate to have met and worked with such talented people.


Client work can be wearying. Constant context changing, rapid timescales, concerned clients…it’s a lot of things to juggle. There’s rarely a chance to revisit and improve on old work. Agency front-end developers build up a very particular set of skills: proficiency typically results in less challenging work. I’ve done it for four years, and I’m ready to try something new.

What Now

MRN’s web team works on a researcher-facing web app that’s fairly large and, frankly, showing its age. The team plans to restructure the app and update the design. I’ll help by fixing bugs, testing code, and developing front-end features (thus the recent JavaScript reads). I’m especially thrilled to put my design experience to use and assist with user interface design. There’s much to do, and I’m excited to get started.

Jeremy Keith on the Style Guide Podcast

The Style Guide Podcast, hosted by Anna Debenham and Brad Frost, is quickly becoming one of my favorites. They interview designers from organizations and teams that utilize, predictably, a style guide in their work.

The most recent episode features Jeremy Keith of Clearleft. It’s great episode because Jeremy is able to profoundly put into words the struggle of designing for web in the present day. While I highly suggest listening to the entire episode (41 minutes), here’s part of the conversation that I found particularly provoking (starting at 11:55):

Anna: Do you tend to work straight in code, or do you kind of come up with mockups first and then get them built?

Jeremy: There’s nearly always mockups first. I guess the question is how long you spend refining those mockups…

There’s three reasons a mockup could exist. One is that it’s for buy-in. It’s basically for sign-off. You’re mocking something up to present it to a decision maker who then says thumbs up or thumbs down…

Use number two is its a deliverable for a front-end developer, so in other words it’s something a visual designer gives to a front-end developer to get turned into code. That’s a separate use case. Now here’s problem number one is that mockups made for the first use case end up getting used for the second. So if you’re designing for sign-off everything’s perfect. Everything lines at the top and the bottom…everything looks beautiful…Well, everyone’s just going to get disappointed, right? The developer’s going to get frustrated because it’s not accurate, the designer’s frustrated because, “hey, that doesn’t look like my mockup,” and the client is frustrated because that doesn’t look like what they were presented with…

And then there’s a third use of a mockup which is for a visual designer to think…they have a tool that they’re comfortable with, like PhotoShop, Sketch, Illustrator, whatever, and its the fastest way for them to get ideas down is to create a mockup.

I think that’s extremely insightful. Everyone agrees that a mockup isn’t the website, but this artifact has different purposes in the process. Jeremy continues:

They’re three very different use cases…[A]nd yet what happens is a single mockup will end up kind of doing all three…

So this is the problem…we found in general is that there’s this mismatch, I guess, of expectations. And this is why I love Dan’s [Mall] idea of deciding in the browser…Yeah, you do stuff in PhotoShop or Sketch or whatever you’re comfortable with. It’s more about that third use case, use whatever tool is comfortable with you. But you don’t go to the client and say approval or not approval until you’ve got it in web browsers until you’ve got it in code. Because that’s just so much more accurate to reality.

Wow. Jeremy puts into words the things that we struggle with so well. While even he admits that they’re still refining their process, I think it’s a step in the right direction. The approach sounds more true to the medium.

On Human JavaScript

I just read Human JavaScript by Henrik Joreteg. It’s available as a $39 ebook, but you can also read it online for free.

Some context is important. Mr. Joreteg defines a web app:

  1. They are separate from the API.
  2. They don't work at all if someone has JavaScript turned off.
  3. A modern browser with a modern JavaScript engine is a minimum system requirement.

And so on. He separates this API – the data, business logic, authentication, etc. – from the client, which could be a native iOS or Android application. Or, of course, something that runs in the browser. He also remarks on the concept of “real time,” and its effect on users’ expectations:

Facebook with its chat, live comments, and push notifications is already conditioning an entire generation of users to expect realtime updates. I believe that knowing how to build realtime apps is a crucial skill for web developers who want to stay at the top of their field.

Unfortunately, a majority of the book delves into implementation examples with Ampersand.js, &yet’s modular fork of Backbone.js. The foreword was written in September 2013, so it’s understandable that the content is a bit dated. Nevertheless, here’s what I gathered:

  • A team should strive for consistent, readable code. precommit-hook is suggested to codify this practice.
  • Continual refactoring is essential:

    Entropy is inevitable in a codebase. If we don’t continually modify, simplify and unify the existing code along with the new code that’s being written, we can easily end up with a really big, messy app.

  • Views and state should be separated. Don’t tie state to class names:

    Your view (the DOM) should just be reacting to the model state of your application.

  • Investing in tooling is key. Mr. Joreteg suggests moonboots for running a development server, templatizer for shipping compiled templates to the client, and clientconfig for retrieving application configuration on the client.

  • A sane directory structure is important. He outlines his preferred folders in Chapter 4, but I think a team-wide agreement is the most important thing. It could change from application to application.

  • He hangs important functionality on a global app variable, and relies on CommonJS modules for nearly everything else. For example, navigation is an application-level concern, so he adds a app.navigate(url) method that defers to the router. Certain frameworks are more prescriptive than others, of course.

  • Event-based architecture makes for a good way to decouple modules, keeping code simple:

    Exporting objects and classes that inherit from some type of event emitter means that the code using your module can specify what they care about, rather than the module having to know.

  • Models are a good way to encapsulate data:

    The idea is simply that we create some data structures in the browser, separate from the DOM, that hold the data that we got from the server as well as any client specific data or state.

    He advocates putting view and session state information on the model itself, which seems questionable. But, MVVM.

As someone who’s interested in newer, better technologies (like React and Ember) this book doesn’t completely serve my needs. Surely Mr. Joreteg has knowledge that transcends a particular library; a book that focuses more on best practices and patterns would have been a stronger work.

But, it’s a quick read. I find the fact that the &yet team builds its own tools quite inspiring. And, if you’re looking for a primer on Ampersand.js, it’s the best.