Prioritizing the Critical Rendering Path CSS

This is part 2 in a series of posts that are intended to accompany my talk from WordCamp US 2015.

Google defines the critical rendering path as:

The code and resources required to render the initial view of a web page.

Delivering a website that loads quickly is not enough. While speed is important, delivering a fast visible response to the user results in a better user experience. Notice that the term used for the visible response contains the word critical. Is every line of code for the initial view that the browser renders? The answer is no. The CSS needed to render a footer, if it is not in the visible response from the browser, is not critical.

With this in mind, let’s look at how we can prioritize the critical rendering path CSS, while also preventing the non-critical CSS from being render blocking. We will also go through how to integrate the critical rendering path CSS into WordPress. And yes, CSS is render blocking.

Generating the Critical Rendering Path CSS

Generating the critical rendering path CSS, without the help of an automated task, would be a tedious and time-consuming task. CriticalCSS, a tool from the Filament Group, finds the above the fold CSS for a page, and outputs it to a file. In this tutorial, we will be using grunt-criticalcss, which is a Grunt wrapper for CriticalCSS. If you are not familiar with Grunt, now would be a good time to read through the Grunt documentation.

1. Add grunt-criticalcss to package.json

If you are already using Grunt for a project, then you should have a package.json file in the same directory as Gruntfile.js. If you don’t have a package.json file, don’t worry, the example below will include the basics of what you need to continue. Open, or create a new package.json file in the base directory of your project. Let’s add grunt-criticalcss to the list of dependencies:

Note: the versions of each dependency is the most current version at the time this article was published.

2. Setup the Grunt Task

After the package.json file has been updated to include grunt-criticalcss as a dependency, a new task can be added to Gruntfile.js. Open Gruntfile.js for your project, or create a new one. As with the package.json example above, the Gruntfile.js example below will include the basics needed to run the task. Let’s start by adding the task:

In the above example, we will be generating the critical CSS for the home page of a site. The newly defined object, in this casehome: {}, contains it’s own set of options. Descriptions for each of the above options can be found in the grunt-criticalcss readme.

While the example above will be effective, the code could be written a little cleaner. Here’s a cleaner example:

In the above example, variables for the url, width, height, path to the CSS folder, and buffer have been written. These variables will allow us to makes changes in one location in future steps.

Now that we have refactored Gruntfile.js, let’s add additional objects for another area on a site. Here’s an example:

In the above example, we’ve added an additional object that will run against a page on a site, and then generate a new file.

Now that we have setup Gruntfile.js to check multiple areas of a site, we’ll add in a minification task so that a minified version of the generated file will be created.

In the above example, a new task has been added calledcssmin, and it’s using grunt-contrib-cssmin. Descriptions for eachcssmin option can be found on the grunt-contrib-cssmin readme. Also, a new option has been added to the criticalgrunt.registerTask that will run thecssmin option. Take note of the addition of:critical to thecssmin option. This addition tellscssmin to only run thecritical object. This is an important addition when files other than the critical css files are minified, and will create separation between them.

While the above example will get the job done, there are still a couple of issues. A good development process consists of a git workflow, with multiple branches deploying to multiple environments. If multiple environments are a factor, then the generated files may not contain all that is needed for the critical rendering path CSS. Taking this into consideration, the Gruntfile.js should be updated so that the additional environments can be ran against.

In the example above, additional variables have been added that define the urls for a staging and production environment. We have also added additional objects to run against the staging and production environments, and then included those in two newly created tasks that can be run withgrunt criticalStage andgrunt criticalProd. You will also notice that the original critical task has been renamed and can be run withgrunt criticalLocal.

You may be asking, “Why three separate tasks?”. The reason that we have three separate tasks is to ensure that the prioritized CSS on each of these environment are 100% accurate. In many instances, the local and staging environments do not always line up with what is on production. Local and staging environments are being used to constantly check out new features or resolve bugs. The CSS for these two areas should not be available on production until they are ready.

Each of these commands can be included in a deploy script, if a deploy script is part of your deploy process. Adding these commands to deploy script removes the need for the developer to run the commands after each deploy.

One item of note, if you do include these commands in your deploy scripts, you will need to ensure that each environments has Node.js, npm, and Grunt installed. While Node.js includes npm, npm gets updated more frequently than Node.js, so you’ll want to make sure you have the latest version. To update npm, run the following command in terminal:

3. Add to Your WordPress Theme

The newly generated critical rendering path CSS will need to be added to your theme, but they cannot be enqueued like we normally would. To include the critical rendering path CSS in your WordPress theme, we will write a new function. This function can either be included in your theme’s functions.php file, or in a separate class as shown below.

The class above inlines the CSS generated by the grunt tasks. We are including some conditionals to ensure that the correct CSS is loaded on the front page of a site and on a page. After we have inlined the CSS, we are inlining a JavaScript function called LoadCSS. This function will create the link tag for the specified CSS file, which is the first parameter in the function, which will be injected into the DOM after it has formed. We have also specified the element that the link tag should be injected before, which is the second parameter in the function. In this case, we are injecting  the link tag before the LoadCSS function. By default, LoadCSS attempts to inject the link tag after all CSS and JS in the page, but I have seen this fail in live production environments. Specifying where the tag should be injected prevents the tag from being added to early or to late.

A question that I have been asked on more than one occasion is if the CSS file cached that is being injected into the DOM. The great news is, the answer is yes. I’ve been able to confirm that the browser does cache the CSS file as it would in any other instance.

While we could just add a require to our theme’s function.php file, I like to be able to leave the option open to test without the generated CSS. This is important when working on a new feature that will include new CSS. To do this, we will first add a conditional to the wp-config.php file of your WordPress installation. Here’s an example:

After this constant has been added, we will then add a conditional to your theme’s functions.php file.

In the example above, if the constant has been defined, the CriticalCSS class is called, while the theme styles are called if the constant is not defined (Note: in this example, the theme’s enqueued styles would be moved into a new class similar to the CriticalCSS class).

4. Test, Test, Test

The final step is test everything! While prioritizing the critical rendering path CSS is important, making sure that your site does not visually break is even more important. Prior to implementing a technique like this on a production environment, test locally, then on a staging environment.

If you have any comments or thoughts, include them in the comment section below.