Liam Gladdy recently authored this post about how to remove unused CSS using the
grunt-uncss task and I wanted to implement it on the Roots theme (which comes pre-packaged with Grunt). When I succeeded, it managed to reduce my final stylesheet size from 159kb down to 20kb. That’s an 87% reduction!
I’m no expert when it comes to Grunt, but it’s not as scary as you might think. So let’s walkthrough how I did it, and if you spot something that can be done more efficiently then please leave a comment. Here we go!
I am using the Roots theme; any defaults I mention are exclusive to it.
By default, when you
grunt build your Roots project in preparation for deployment, this is what Grunt does:
- Compiles your LESS into CSS and compresses it into
- Parses your
main.min.cssand adds vendor-prefixed CSS properties using the Can I Use database (via the
- Builds and uglifies a Modernizr script on the fly according to the CSS and JS already created in steps 1-4
- Finally it uses
grunt-wp-assetsto create a hash which is then pulled into
lib/scripts.phpto prevent CSS and JS being cached in your browser
Here’s what we’re going to do:
- Disable CSS compression in step 2 above and intercept Grunt’s workflow from here (because I found that UnCSS works better when the input stylesheet is unminified)
- Generate a sitemap of all published WordPress pages/posts
- Use the sitemap to parse all WordPress pages/posts for only used CSS rule-sets
- Compress the result of step 3
grunt-autoprefixeron the compressed and now clean CSS
- Resume its previous workflow at step 4 in the list above
grunt-uncss essentially parses a list of URLs and stylesheets and compiles the CSS rule-sets which correspond to elements on those pages. Only CSS rule-sets that actually affect elements on your site will be included in your final CSS.
So, we need to give
grunt-uncss a list of all published WordPress pages. As Liam’s post mentions, this is pretty easy. Instead of his suggestion of making a plugin, I just added his function along with a few tweaks to my
lib/extras.php file. Here is what I added:
Now WordPress will output a list of all published pages or posts (even custom post types) when we go to
http://example.dev/?show_sitemap. I added the check for
WP_ENV in the if statement because I only want to display the sitemap if I’m in my development environment and I recommend you do the same. Now strap on your Grunt gloves because everything we do going forward is Grunt related.
Add and Install the Grunt Tasks We Need
We need to add two new Grunt tasks to our project:
grunt-execwhich we’ll use to fetch our sitemap and tell
grunt-uncssto use it; and
If you’re like me and up to this point you’ve only used Roots + Grunt with its default tasks, this can seem daunting but it’s actually quite simple. In the Roots theme root directory just open
package.json and add our two new tasks to the
devDependencies section. Here is my finished file:
If you’re wondering where I got the version numbers for the new tasks from, I just used the latest stable release from each task’s Github repository which you can easily find by appending
/releases to the repo’s URL.
Now to actually install these tasks, open a terminal window and in your theme directory do
$ npm install.
Register and Configure Our Grunt Tasks
Now we need to configure our Grunt tasks to “intercept” the current workflow at step 2 in the first list at the top of this post. I’m going to start in the order that it made the most sense to me. (NB, we’re not really intercepting them, we’re creating a new task that starts out much like the old with a few key differences.)
At the bottom of
Gruntfile.js, just after the
build task is registered, we need to register the two new tasks we installed:
Now let’s configure our new tasks. Our
grunt-exec task will use cURL to retrieve our sitemap so after the
jsFileList declaration I created a new variable containing my current localhost development domain as a string:
var domain = 'http://example.dev/';
Then I added the
grunt-exec task config:
Next we need to add the
We’re almost done, we just need to add a few arguments to the default LESS task that Roots ships with to prevent Grunt from compressing our CSS before it runs UnCSS.
Tweak the Default LESS Task
Inside of our LESS task we already have
less:build, but the new tasks we registered will not use those, they will use
less:compress, which we need to create now. Here is the complete LESS task after adding those new arguments:
This post is getting long but if you want to view my complete
Gruntfile.js you can check it out here.
Execute Our New Task
Whew, now we should be squared away. In your theme directory you can run
$ grunt build_uncss and Grunt will now remove unused styles from your WordPress site. This can take anywhere from 30 seconds to several minutes, depending on how many pages/posts you have on your site, but the results speak for themselves.
Not everyone needs to remove unused CSS from their WordPress site and not everyone should. In fact, this methodology has some serious shortcomings. For example, if you’ve added styles to any WordPress pages/posts that are drafts or to page elements not yet visible (like comments sections), then UnCSS will not “see” those areas and will remove the CSS for them; when they do become visible they could appear unstyled.
WooCommerce and other ecommerce plugins could also be problematic, especially with WooCommerce’s new endpoint structure. It’s probably trivial to add those endpoints to the sitemap but if your ecommerce shop is highly customized—say, with a dropdown shopping cart in the header/nav area—you might experience some unstyled elements unless you tell UnCSS to ignore specific selectors.
Finally, when I ran this on a site that used Disqus for its comments, PhantomJS crashed and the resulting stylesheet visually crippled my entire project.