Four Kitchens

Use Gulp to automate your critical path CSS

4 Min. ReadDevelopment

Howdy, perfers! Last week I went over the surprisingly brief process of cutting the fat out of your CSS frameworks with gulp and UnCSS. This week let’s take CSS optimization a step further and inline some critical CSS into our HTML document.

Inline CSS for initial page loads

An organized project is one with separate files for HTML, CSS, and JavaScript. This separation of concerns has worked exceedingly well for the web, and there’s no good reason to depart from it. It allows browsers to do all sorts of convenient things, like caching to save bandwidth on future requests. However, it can sometimes create a bottleneck when trying to render a page for the first time.

Since this first page load can make or break the user’s impression of your site, it’s important to make it as fast as possible so they can see your content and possibly stick around for more.

Age-old advice: Reducing HTTP requests

Since separate files must be requested individually, browsers on slower networks have to endure latency for each file you wish to use. Once they’ve been downloaded, you likely won’t need to request them again until you leave the site and come back some other time. To get the best of both worlds, web folk settled on an interesting technique: inlining the most immediately-used CSS (hence the name critical rendering path), and allowing the rest to be downloaded using the normal process.

Once this idea caught on, several tools sprung up to allow your workflow to remain organized and simple, while providing a quick, hassle-free process for updating these critical styles. The tool we’re using to demonstrate is called Critical, but there are others too.

Read the npm docs for Critical

Note: If you’re using Grunt, you might want to check out grunt-critical or grunt-criticalcss.

Update 2015-08-12: I’ve written a separate article showing how to use grunt-criticalcss to inline CSS within a Drupal 7 theme. If you use a PHP-based CMS then this article may be of more use to you.

Gulp + Critical

Let’s take a look at the gulp-critical task within Four Kitchens’ frontend performance training kit. I’ll repost the code here for your convenience, but if you want, check out the full gulpfile.js which includes many other tasks related to frontend performance.

var critical = require('critical');

gulp.task('critical', function (cb) {
    base: '_site/',
    src: 'index.html',
    css: ['css/all.min.css'],
    dimensions: [{
      width: 320,
      height: 480
      width: 768,
      height: 1024
      width: 1280,
      height: 960
    dest: '../_includes/critical.css',
    minify: true,
    extract: false,
    ignore: ['font-face']

You see? There’s not much to it. I’ll walk you through the configuration:

  • base is just a base directory to keep the other paths we have to specify from containing redundant info
  • src is the HTML we’ll be using. You can point to a local HTML file or directly embed your markup. At this time the HTML source cannot be a remote URL; it must be within the filesystem where you run the task. More on this below.
  • css is an array of all CSS that should be considered as possible critical styles.
  • dimensions allows us to specify one or more viewport sizes to be considered within the critical area. Typically the amount of CSS we’re able to include means that optimizing for both smaller portrait viewports as well as wider landscape sizes is ok. If you have to stick with one, you probably want something closer to a mobile phone screen.
  • dest is simply the destination of our freshly-generated critical CSS
  • minify can remove one step of the process for you by automatically minifying the result.
  • extract is a tricky one. The tool can technically swap out any <link> tags within your HTML and replace them with an asynchronous CSS loader. However I tend to take care of this step myself, using the include system of my actual site (Jekyll, Drupal, etc). Our sample task drops the critical CSS into the Jekyll _includes directory.
  • ignore allows you to avoid including certain CSS rules within the generated CSS. See the second caveat below for more info.

Always check your styles

There are a few ground rules to follow. Breaking any of them can have negative side effects which remove any benefit you were attempting to achieve by inlining the styles. So be careful and always check on these!

First, inline styles should not exceed approximately 10KB. This is due to HTTP packet sizes, which are most frequently in the 14KB range, and you need to leave room for the HTML itself. Exceeding this limit means the page has to wait for additional data before rendering, and you do not achieve the benefits of a “first-packet render.” Often your generated CSS won’t even come close to this, but do keep an eye out. Remember: the site doesn’t have to look perfect on the first render, just presentable.

Second, avoid external requests that block rendering. I’m looking at you, webfonts. Previously I wrote an article about testing critical CSS to avoid including render-blocking styles and shortly afterward there was a new feature within Critical: the ability to filter out specific rules within the CSS. So if you find your @font-face declarations or other render-blocking styles showing up within the critical CSS, try using the ignore option. See the filter-css module for syntax examples.

Third, at this time there is no out-of-the-box approach to inlining styles on a dynamic site, such as a Drupal or WordPress site. You can work around it by using cURL or wget to grab a copy of your page(s), and then run the task against the HTML locally. See issues on GitHub: #19, #90, #94, but please respect the maintainers and don’t go “+1” these issues. They know that people would like to have this capability. If drop-in support for remote URLs are your priority, consider using Filament Group’s criticalcss.