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) { critical.generate({ 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 infosrc
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 CSSminify
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.
Making the web a better place to teach, learn, and advocate starts here...
When you subscribe to our newsletter!