Unused CSS Removal Tools for SPAs + Webpack

While adding an audit to Lighthouse recently that checks for too many unused CSS rules (SPOILER ALERT: if you're using a framework like bootstrap, there are probably a lot!), I became really interested in automating their removal at build time. As it turns out there were already quite a few packages out there that already do this, so I gave a few most popular ones a try, purifycss and uncss before ultimately writing my own nukecss!

Specifically I was searching for a solution that is:

  1. Friendly for single page application use cases
  2. Correct, that is, the plugin can always be configured to keep used CSS
  3. Highly precise, that is, the plugin eliminates as much unused CSS as possible
  4. Meshes well with webpack setups, that is, the plugin does not disturb or demand changes to my existing transforms

uncss: great but not SPA-friendly

UnCSS loads your page using jsdom, executes JavaScript, parses all the stylesheets, and filters out all the selectors that aren't found in the HTML. This approach is super accurate, and if your site doesn't hide any element selectors behind interaction then UnCSS will work amazingly well for you. Unfortunately, if you're build an SPA where a lot of your elements are hidden behind interactions and intricate user flows, UnCSS won't work great out of the box. On to option 2.

purifycss: SPA-friendly, almost what I need

PurifyCSS attempts to solve these shortcomings by going straight to your source. By parsing all JavaScript source files, purifycss is able to determine that a selector will be used in the future even if it's not present in the DOM on initial load. Box 1, check. PurifyCSS also has impressive examples that even dynamically generated selectors will be detected and preserved along with a whitelist option to always keep selectors it can't find. Box 2, looking good. Unfortunately the wide net it casts leads to some overly conservative results when used with big CSS sets like font-awesome. Just because I have fa contained in my text and - and close doesn't mean I'm using the fa-close class. Box 3, half-n-half. Finally, PurifyCSS does indeed have a webpack plugin, but here's where things get sticky. No support for inline styles; no support for using the JavaScript emitted by Webpack; and a killer, no support for CSS modules. Failure on box 4 ultimately meant that the "correctness" of my purifycss result was back to zero without resorting to manually whitelisting everything, not much of an automatic tool. Oh well, now I get to write my own! :)

nukecss: SPA-friendly, webpack-first, and fun to write

NukeCSS was built from the ground up with these four priorities in mind. It has to plug seamlessly into my existing webpack setup, preserve source maps, work with modules, read from the Webpack emitted source, etc; it has to be tunable to be correct but with maximum precision; and of course, it has to be SPA-friendly. nukecss is all of these things and a joy to write. Together with nukecss-webpack and fontmin-webpack, I've seen drastic reductions to my payload sizes and can now include font-awesome for those 1 or 2 icons guilt-free! Ah, perfection at last.