Using UnCSS and grunt-uncss with WordPress

Following on from my last blog post about modernising WordPress development with grunt, we’ve been using the process for a few months at Storm and have been slowly making it better.

This week, while working on pretty heavy project, we noticed that our compiled single CSS file was a huge 6,500 lines and ~180kb. This is still better than loading a bunch of different CSS files, but still less than ideal.

The problem is, we use Zurb Foundation, and not using a bootstrap/foundation/grid isn’t an option for us – You get way too much stuff for free, and I call BS on the usual argument that “all sites look the same” – that just means you’re not using it properly, or you’re throwing something together lazily.

Zurb is awesome – this site is built in it – but, by it’s nature, it contains probably 75% more stuff than you’re actually going to use, and there is really no point in making the browser download and parse the 75% of stuff you’re not actually using. This is where UnCSS comes in.

What is UnCSS?

UnCSS looks at all your html and figures out what selectors you actually use in the finished project, stripping out everything else that isn’t used. If you’re using any framework, or library that requires CSS, the chances are, this will save you a huge percentage of your minified CSS file.

In the case of dynamic or CMS powered websites, there is no complete html that contains all of the selectors that you’re using. UnCSS is aware of this and it supports reading URLs as well as local files – but that doesn’t really work for a CMS website – you could have hundreds of URLs, and they’re going to be changing regularly.

Applying UnCSS to WordPress

Originally, my idea to solve this was to use the W3 Total Cache files to load instead, but this would be messy and evil whilst relying on way too many other things to be in place locally, and the cache to be fully built at the time you run the UnCSS task. After 15 minutes or so it became clear this wasn’t going to work.

The answer to me was to create a WordPress plugin that gave me a list of every single published page on the site, somehow feed that into our grunt process, and then use Addy Osmani’s grunt-uncss.

WordPress’s WP_Query makes it crazy simple for us to get every published page on the site in one line, then we throw every URL into an array and echo it json_encoded. The plugin and instructions on how to make this work is available on my github as a gist, here. Once you’ve got that loaded, you can add the grunt-exec plugin to download the sitemap to your site root (I added sitemap.json to my .gitignore file here too, just to be clean)

Grunt Tasks

Here’s the grunt task which handles downloading the sitemap from our local build URL:

exec: {
  get_grunt_sitemap: {
     command: 'curl --silent --output sitemap.json http://gladdy.local/?show_sitemap'
  }
}

Next, we need to load the sitemap.json file into the Gruntfile in order to pass the URLs into the grunt-uncss task. I played a bunch with this, tried all kinds of config options, but as usual with this kind of stuff, it ended up being way simpler than I thought:

grunt.registerTask('load_sitemap_json', function() {
  var sitemap_urls = grunt.file.readJSON('./sitemap.json');
  grunt.config.set('uncss.dist.options.urls', sitemap_urls);
});

That task overwrites the uncss:dist task’s options.urls setting with our list JSON list of valid page paths, and here is what that UnCSS task looks like:

uncss: {
  dist: {
    options: {
      ignore       : [/expanded/,/js/,/wp-/,/align/,/admin-bar/],
      stylesheets  : ['css/app.css'],
      ignoreSheets : [/fonts.googleapis/],
      urls         : [], //Overwritten in load_sitemap_and_uncss task
    },
    files: {
      'css/app.clean.css': ['**/*.php']
    }
  }
}

We tell UnCSS to process our already compiled css file, css/app.css, and to ignore anything hosted on google fonts. We also get it to load all our .php files – just to give it a good starting point, though actually isn’t necessary. The UnCSS task unminifies any code you pass into it, so you need to run it back through your minifier after you’ve processed it.

Edit: I’ve updated this article here to include an ignore option which excludes a bunch of default styles that are only enabled when you login, (like the wordpress admin bar) or on javascript actions in Zurb, such as expanded for the menu.

Finally, we need a final task that will grab the sitemap, load the sitemap, and run the uncss task…

grunt.registerTask('deploy_build',
  ['exec:get_grunt_sitemap','load_sitemap_json','uncss:dist','sass:dist']);

Conclusions

For this site, containing about 20 pages at the moment, the deploy_build task takes about 3 minutes to run. It’s certainly not something you want as part of your watch task, but should be part of your pre-deploy system. I wouldn’t be surprised to see it take up to 30 minutes if you’ve got a massive WordPress site with hundreds of pages/URLs.

For this site, the UnCSS task reduces the CSS outputted by 81.13%, from 149.29 kB to 28.17 kB – once you pass your newly trimmed file through your minfier, it’ll cut even more off. In this case it goes down to 20.1 kB – that’s a total of 86.53% saved!

UnCSS with WordPress result

Edit: I published the full gruntfile I use to make this blog here