Inline Critical CSS with Wordpress

by: Joe Watkins

  • 05 April 2015

The importance of web performance is at an all-time high now due to the proliferation of mobile devices. Google is now promoting “Mobile Friendly” websites giving them higher rank for meeting a few key guidelines. One piece of that #webperf puzzle is the practice of inlining Critical CSS (only styles needed to render the page ‘above-the-fold’) into the document’s <head> alongside calling CSS and JavaScript asynchronously. This “progressive loading” will make your website hit quicker and render faster for the user. There are a few awesome tools out there to assist with capturing these styles.

Here’s a great roundup of critical-path tools: Critical Path Above-the-fold Tools

This tutorial assumes you have a basic understanding of GruntJS and modern web development workflow tooling. Oh.. and you understand the ins and outs of a Wordpress template setup.

Ok.. how do you handle Critical CSS in Wordpress?

Wordpress is an awesome CMS/Blogging platform! BUT… it currently doesn’t play well with the webperf-minded. Wordpress, out-of-the-box, can have excessive calls to CSS and JavaScript files in the <head> of the document. When you start adding plugins the list will get even longer. The less http file requests, the quicker the page will load.

An important step to pull off inline Critical CSS successfully in Wordpress is to limit all of those extra http requests so our Critical CSS efforts are not lost on other files slowing down the render due to load blocking.

Step One: Grab Critical CSS

The first thing you will want to do is grab the actual Above-the-fold styles using your preferred automated task. I use grunt-criticalcss

Here’s a look at a Grunt config:


var wpDefaults = {
	fullThemePath : "../wp-content/themes/theme-name-here",
}

criticalcss: {
	home : {
		options: {
			url: "http://localhost:3000",
			width: 1200,
			height: 900,
			output file: wpDefaults.fullThemePath+"/includes/critical-home.css",
			filename: "styles/main.min.css",
			buffer: 800*1024
		}
	},
	inner : {
		options: {
			url: "http://localhost:3000/inner-page",
			width: 1200,
			height: 900,
			output file: wpDefaults.fullThemePath+"/includes/critical-inner.css",
			filename: "styles/main.min.css",
			buffer: 800*1024
		}
	}
}

This Grunt task will use a headless web browser to visit a url you specify and grab all the styles needed for above-the-fold and then creates a CSS file that I will then include as a PHP include.

You will notice that there are two separate tasks to cover Home page styles as well as Inner page styles as those may differ from project to project. From the command line I can then invoke them like this:

$grunt criticalcss:home or $grunt criticalcss:inner

These tasks create two separate Critical CSS stylesheets that I can then include like this:

<?php include('/includes/critical-home.css'); ?> inside the <head> of the header.php template file.

It is also a good idea to minify these critical CSS files once you have them.

You can elect to setup a separate Grunt task for this or have be part of your Watch tasks. It can be a bit slow so I separate them.

grunt.registerTask('cssBuild',['criticalcss','cssmin']);

Step Two: Install Async JS and CSS Plugin

A great plugin for Critical CSS loading in Wordpress is called Async JS and CSS. It is important to note that the plugin can affect only those files loaded using Worpdress’s queue, which most do.

This plugin will take all the site’s required CSS and Javascript files and load them asynchronously. This means the plugin removes all of the <style> and <script> tags from the <head> and then calls those files with JavaScript in the foot of the document. For Progressive Enhancement reasons you can offer fallback <noscript> solutions along side this solution.

Here is how you install the plugin:

  1. Upload asyncJSandCSS folder to the /wp-content/plugins/ directory

  2. Activate the plugin through the ‘Plugins’ menu in WordPress

  3. Configure the plugin if needed (Settings/Async Settings)

Plugin settings

Step Four (Optional): Setup Dev Mode in your template

I like to setup a Dev state so that while I’m developing I’m not wrestling the inline styles as they will take precedence over files called via HTTP and make debugging tougher. While in Dev mode I actually call files the old fashioned way using hardcoded <style> tags and then turn this mode off prior to building and pushing out to staging or production.

<?php
	$dev_mode = false;
?>

Step Five: Setup your with loadCSS and PHP Includes

Now we are going to make sure that we are calling our main site styles asynchronously by using LoadCSS.

This is super simple to setup you just include the loadCSS() function in the <head> and point it to your main styles file.

header.php file

<?php
	if(!$dev_mode){
?>
<script>
	// load CSS async
	function loadCSS(e,t,n){"use strict";function o(){var t;for(var i=0;i<s.length;i++){if(s[i].href&&s[i].href.indexOf(e)>-1){t=true}}if(t){r.media=n||"all"}else{setTimeout(o)}}var r=window.document.createElement("link");var i=t||window.document.getElementsByTagName("script")[0];var s=window.document.styleSheets;r.rel="stylesheet";r.href=e;r.media="only x";i.parentNode.insertBefore(r,i);o();return r}

	loadCSS( "<?php bloginfo('template_url'); ?>/styles/main.min.css" );
</script>

<!-- no js support -->
<noscript>
	<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/styles/main.min.css">
</noscript>

<!-- Critical CSS includes -->
<style>
<?php
	if(is_front_page()){
		include (TEMPLATEPATH . '/includes/critical-home.min.css' );
	}else{
		include (TEMPLATEPATH . '/includes/critical-inner.min.css' );
	}
?>
</style>

<?php }else{ ?>

	<!-- dev mode call standard styles -->
	<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/styles/main.min.css">

<?php } ?>

With this setup if $dev_mode == false then styles will be loaded asynchronously otherwise styles will be loaded using <link> tag. I find this to be a better for debugging so that I’m not fighting the inlined styles when working.

When I’m ready to push my changes I do the following:

  • Switch $dev_mode from true back to false
  • Build my new Critical CSS
  • Then push changes

Old IE Support

I noticed some odd behavior with IE8-9 with this method of loading Critical CSS. I found that creatively using conditionals for those browsers did the trick to simply have those browsers load the CSS from the <link> tags. Something like this:

Modern browsers and IE10+

<!--[if !IE]> -->
	function loadCSS(e,t,n){..}
	loadCSS( "<?php bloginfo('template_url'); ?>/styles/main.min.css" );
<!-- <![endif]-->

IE9

<!--[if IE 9]>
	<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/styles/main.min.css">
<![endif]-->

IE8 and below

<!--[if lt IE 9]>
	<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/styles/main.min.css">
	<script src="<?php bloginfo('template_url'); ?>/js/vendor/html5shiv.js"></script>
	<script src="<?php bloginfo('template_url'); ?>/js/vendor/respond.js"></script>
<![endif]-->

Here is a website we (Emerge Interactive) shipped using this exact technique. Visit We Build Green Cities to see it in action.