Eliminating Unused CSS

Uppy - Painless Uploads for JavaScript - Interview with Artur Paikin

Frameworks like Bootstrap tend to come with a lot of CSS. Often you use only a small part of it. Typically, you bundle even the unused CSS. It's possible, however, to eliminate the portions you aren't using.

PurifyCSS is a tool that can achieve this by analyzing files. It walks through your code and figures out which CSS classes are being used. Often there is enough information for it to strip unused CSS from your project. It also works with single page applications to an extent.

uncss is a good alternative to PurifyCSS. It operates through PhantomJS and performs its work differently. You can use uncss itself as a PostCSS plugin.

You have to be careful if you are using CSS Modules. You have to whitelist the related classes as discussed in purifycss-webpack readme.

Setting Up Pure.css#

To make the demo more realistic, let's install Pure.css, a small CSS framework, as well and refer to it from the project so that you can see PurifyCSS in action. These two projects aren't related in any way despite the naming.

npm install purecss --save

To make the project aware of Pure.css, import it:

src/index.js

import "purecss";
...
The import works because webpack will resolve against "browser": "build/pure-min.css", field in the package.json file of Pure.css due to resolve.mainFields. Webpack will try to resolve possible browser and module fields before looking into main.

You should also make the demo component use a Pure.css class, so there is something to work with:

src/component.js

export default (text = "Hello world") => {
  const element = document.createElement("div");

element.className = "pure-button";
element.innerHTML = text; return element; };

If you run the application (npm start), the "Hello world" should look like a button.

Styled hello
Styled hello

Building the application (npm run build) should yield output:

Hash: 36bff4e71a3f746d46fa
Version: webpack 4.1.1
Time: 739ms
Built at: 3/16/2018 4:26:49 PM
     Asset       Size  Chunks             Chunk Names
   main.js  747 bytes       0  [emitted]  main
  main.css   16.1 KiB       0  [emitted]  main
index.html  220 bytes          [emitted]
...

As you can see, the size of the CSS file grew, and this is something to fix with PurifyCSS.

Enabling PurifyCSS#

Using PurifyCSS can lead to significant savings. In the example of the project, they purify and minify Bootstrap (140 kB) in an application using ~40% of its selectors to mere ~35 kB. That's a big difference.

purifycss-webpack allows to achieve similar results. You should use the MiniCssExtractPlugin with it for the best results. Install it and a glob helper first:

npm install glob purifycss-webpack purify-css --save-dev

You also need PurifyCSS configuration as below:

webpack.parts.js

const PurifyCSSPlugin = require("purifycss-webpack");

exports.purifyCSS = ({ paths }) => ({
  plugins: [new PurifyCSSPlugin({ paths })],
});

Next, the part has to be connected with the configuration. It's essential the plugin is used after the MiniCssExtractPlugin; otherwise, it doesn't work:

webpack.config.js

...
const path = require("path"); const glob = require("glob");
const parts = require("./webpack.parts");
const PATHS = { app: path.join(__dirname, "src"), };
... const productionConfig = merge([ ...
parts.purifyCSS({ paths: glob.sync(`${PATHS.app}/**/*.js`, { nodir: true }), }),
]);
The order matters. CSS extraction has to happen before purifying.

If you execute npm run build now, you should see something:

Hash: 36bff4e71a3f746d46fa
Version: webpack 4.1.1
Time: 695ms
Built at: 3/16/2018 4:29:54 PM
     Asset       Size  Chunks             Chunk Names
   main.js  747 bytes       0  [emitted]  main
  main.css   2.07 KiB       0  [emitted]  main
index.html  220 bytes          [emitted]
...

The size of the style has decreased noticeably. Instead of 16k, you have roughly 2k now. The difference would be even more significant for more massive CSS frameworks.

PurifyCSS supports additional options including minify. You can enable these through the purifyOptions field when instantiating the plugin. Given PurifyCSS cannot pick all of the classes you are always using, you should use purifyOptions.whitelist array to define selectors which it should leave in the result no matter what.

Using PurifyCSS loses CSS source maps even if you have enabled them with loader specific configuration due to the way it works underneath.

Critical Path Rendering#

The idea of critical path rendering takes a look at CSS performance from a different angle. Instead of optimizing for size, it optimizes for render order and emphasizes above-the-fold CSS. The result is achieved by rendering the page and then figuring out which rules are required to obtain the shown result.

webpack-critical and html-critical-webpack-plugin implement the technique as a HtmlWebpackPlugin plugin. isomorphic-style-loader achieves the same using webpack and React.

critical-path-css-tools by Addy Osmani lists other related tools.

Conclusion#

Using PurifyCSS can lead to a significant decrease in file size. It's mainly valuable for static sites that rely on a massive CSS framework. The more dynamic a site or an application becomes, the harder it becomes to analyze reliably.

To recap:

  • Eliminating unused CSS is possible using PurifyCSS. It performs static analysis against the source.
  • The functionality can be enabled through purifycss-webpack, and the plugin should be applied after MiniCssExtractPlugin.
  • At best, PurifyCSS can eliminate most, if not all, unused CSS rules.
  • Critical path rendering is another CSS technique that emphasizes rendering the above-the-fold CSS first. The idea is to render something as fast as possible instead of waiting for all CSS to load.

In the next chapter, you'll learn to autoprefix. Enabling the feature makes it more convenient to develop complicated CSS setups that work with older browsers as well.

Previous chapter
Separating CSS
Next chapter
Autoprefixing

This book is available through Leanpub (digital), Amazon (paperback), and Kindle (digital). By purchasing the book you support the development of further content. A part of profit (~30%) goes to Tobias Koppers, the author of webpack.

Need help?