Knock out your unused CSS

yashints

Yaser Adel Mehraban

Posted on May 7, 2019

Knock out your unused CSS

Unused CSS is a one of the issues most web applications suffer when it comes to performance and page load time.

Disclaimer

Apart from option one in this post, most of the time you will need a tool and some manual intervention to be able to safely eliminate unused CSS. These tools are great in a sense that they will let you know what you don't know; which classes in your template files are not used.

These tools can't work with complex scenarios like when you have JavaScript adding a DOM element in the template.

They will have problems with dynamic templates in Single Page Applications (SPA) or when your template changes based on a state on your server side code.

Problem

As I said, Unused CSS is a one of the issues most web applications suffer when it comes to performance and page load time.

This even gets worst when you use a CSS library like Tailwind, or an older versions of CSS frameworks like Bootstrap or Material Design.

Note: most CSS frameworks have moved to modular structure where you could import only the part you need without having to include the whole bundle.

Best and safest approach

To my opinion, the best and safest approach is to be careful and get rid of any CSS file or part you remove from your HTML or template files. Do not be lazy or ignorance when it comes to tech debts like this. If you're involved in a green field project, make sure you do not copy paste large chunks of CSS from somewhere you're looking into without realising which parts are actually used.

You can manually find your unused CSS using the DevTools in Google Chrome with following these steps:

  1. Open the Chrome DevTools using F12 or Ctrl Shift J on Windows or Command Option J on Mac.
  2. Open the command menu by pressing Ctrl Shift P on Windows or Command Shift J on Mac.
  3. Type in “Coverage” and click on the “Show Coverage” option
  4. Click on reload button at the coverage tab.
  5. Select a CSS file from the Coverage tab which will open the file up in the Sources tab.

Any CSS rule which has a solid green line on the left side is used. Those with a red line are not:

Unused CSS

Warning: Just because a rule isn't used on this page doesn't mean it's not used elsewhere. You would ideally check the coverage on all pages and combine the result for a better overview.

Tools

Some times you cannot adhere to ☝🏼 point because of various reasons. Such as you got involved in a brown field project or the code base is to large to be able to refactor and fix the issue in a timely fashion.

In this case you might be looking at some tool to automate the process and do the clean-up systematically during build time. Fortunately there are many tools available which help you with this. I will cover some famous ones and mention a short list at the end for good measure 😉.

1. PurifyCSS

PurifyCSS is tool that can analyse your files, go through code, and figure out what classes are not used. Most of the time when you have static HTML files this tool can eliminate nearly all your unused CSS.

Apart from that, it can also work to a degree with Single Page Applications (SPA).

Setup

Standalone

You can install this package via npm:

npm i -D purify-css
Enter fullscreen mode Exit fullscreen mode

And some basic usage:

import purify from 'purify-css'
const purify = require('purify-css')

let content = ''
let css = ''
let options = {
  output: 'filepath/output.css',
}
purify(content, css, options)
Enter fullscreen mode Exit fullscreen mode

If you're wondering well, this is not gonna help me, you might be right. But this is the simplest form. In fact in the purify command, content and css parameters can be an Array of glob file patterns. Now you see the bigger picture and how this can help.

Now let's make it a bit more complex.

via Grunt

First you need to install the grunt package:

npm install grunt-purifycss --save-dev
Enter fullscreen mode Exit fullscreen mode

And then use it:

grunt.initConfig({
  purifycss: {
    options: {},
    target: {
      src: ['path/to/*.html', 'path/to/*.js'],
      css: ['path/to/*.css'],
      dest: 'tmp/purestyles.css',
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

This will handle even scenarios when you have a class added using JavaScript 😍. So this will be picked up:

<!-- html -->
<!-- class directly on element -->
<div class="button-active">click</div>
Enter fullscreen mode Exit fullscreen mode

Or in JavaScript:

// javascript
// Anytime your class name is together in your files, it will find it.
$(button).addClass('button-active')
Enter fullscreen mode Exit fullscreen mode

Or even a bit more complex scenario:

// Can detect if class is split.
var half = 'button-';
$(button).addClass(half + 'active');

// Can detect if class is joined.
var dynamicClass = ['button', 'active'].join('-');
$(button).addClass(dynamicClass);

// Can detect various more ways, including all Javascript frameworks.
// A React example.
var classes = classNames({
  'button-active': this.state.buttonActive
});

return (
  <button className={classes}>Submit</button>;
);
Enter fullscreen mode Exit fullscreen mode

Warning: The Webpack plugin for purifycss is deprecated. You will need to use Purgecss which I will go through later on.

CLI

Install the CLI:

npm install -g purify-css
Enter fullscreen mode Exit fullscreen mode

And you can see the help using -h param:

purifycss -h

purifycss <css> <content> [option]

Options:
  -m, --min        Minify CSS                                   [boolean] [default: false]
  -o, --out        Filepath to write purified css to                              [string]
  -i, --info       Logs info on how much css was removed        [boolean] [default: false]
  -r, --rejected   Logs the CSS rules that were removed         [boolean] [default: false]
  -w, --whitelist  List of classes that should not be removed        [array] [default: []]
  -h, --help       Show help                                                     [boolean]
  -v, --version    Show version number                                           [boolean]
Enter fullscreen mode Exit fullscreen mode

Stats

This library can help you big time. It's pretty effective and works on complex scenarios. But as I mentioned before you will need to have good tests to able to find out if anything is messed up after clean-up.

In terms of Boostrap, PurifyCSS can reduce up to ~ 33.8% of unused CSS.

2. Purgecss

Purgecss is another powerful tool to remove unused CSS. It can be used as part of your development workflow. It comes with a JavaScript API, a CLI, and plugins for popular build tools.

CLI

You can install Purgecss globally or using npx.

npm i -g Purgecss
Enter fullscreen mode Exit fullscreen mode

And use it:

Purgecss --css <css> --content <content> [option]
Enter fullscreen mode Exit fullscreen mode

As you can see, the API is very similar to PurifyCSS, but of course options would be different.

Via Webpack

You should install the plugin first:

npm i -D Purgecss-webpack-plugin
Enter fullscreen mode Exit fullscreen mode

And the add it to your Webpack config:

const path = require('path')
const glob = require('glob')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const PurgecssPlugin = require('Purgecss-webpack-plugin')

const PATHS = {
  src: path.join(__dirname, 'src'),
}

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader?sourceMap',
        }),
      },
    ],
  },
  plugins: [
    new ExtractTextPlugin('[name].css?[hash]'),
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`, {
        nodir: true,
      }),
    }),
  ],
}
Enter fullscreen mode Exit fullscreen mode

For more information please refer to their documents.

3. UnCSS

UnCSS is another tool to help you remove your unused CSS. But this tool is a bit different in sense that it will load your files in jsdom, then will parse all the stylesheets with PostCSS.

Once finished, document.querySelector will filter out selectors that are not found in the HTML files. And finally, the remaining rules are converted back to CSS.

Note: The best thing about this library which I am in love with, is their unofficial server where you can paste your HTML and CSS it will show you the shortened CSS online.

Install

Again you can use npm to install it or use npx:

npm i -g uncss
Enter fullscreen mode Exit fullscreen mode

You can use UnCSS with node:

var uncss = require('uncss')

var files = [
    'my',
    'array',
    'of',
    'HTML',
    'files',
    'or',
    'http://urls.com',
  ],
  options = {
    ignore: ['#added_at_runtime', /test\-[0-9]+/],
    media: [
      '(min-width: 700px) handheld and (orientation: landscape)',
    ],
    csspath: '../public/css/',
    raw: 'h1 { color: green }',
    stylesheets: [
      'lib/bootstrap/dist/css/bootstrap.css',
      'src/public/css/main.css',
    ],
    ignoreSheets: [/fonts.googleapis/],
    timeout: 1000,
    htmlroot: 'public',
    report: false,
    banner: false,
    uncssrc: '.uncssrc',
    userAgent:
      'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X)',
    inject: function(window) {
      window.document
        .querySelector('html')
        .classList.add('no-csscalc', 'csscalc')
    },
  }

uncss(files, options, function(error, output) {
  console.log(output)
})

/* Look Ma, no options! */
uncss(files, function(error, output) {
  console.log(output)
})

/* Specifying raw HTML */
var rawHtml = '...'

uncss(rawHtml, options, function(error, output) {
  console.log(output)
})
Enter fullscreen mode Exit fullscreen mode

Or use their build flows which supports Gulp, Grunt and Broccoli (unfortunately no Webpack 😔). For more information about how to setup those tools refer to their documentation.

Comparison

Now let's compare these tools and see their pros and cons.

PurifyCSS

Some people believe the biggest problem with PurifyCSS is lack of modularity. Some think it's also its biggest benefit 🤷🏽‍♂️. It can work with any file type not just HTML. It can also find selectors which are added using JavaScript.

Unfortunately since every word is considered a selector, this can end up in a lot of false positives and make the resulting CSS a bit larger than it should be.

Purgecss

Purgecss fixes the above issue by providing the possibility to create an extractor. This is simply a function which takes content of a file and extracts the list of CSS selectors used in it.

The extractor can be used as a parser that returns an AST (abstract syntax tree) and looks through it to find any CSS selectors. This is the way purge-from-html works. You can specify which selectors you want to use for each file type, allowing you to get the most accurate results. Additionally, you can use the default or legacy extractor, which will mimic PurifyCSS's behaviour.

That said, Purgecss has some drawbacks too. FIrst and foremost you will need to write a custom extractor for frameworks like Tailwind. Another problem is when you use a syntax highlighter like primjs, in which case you will have to whitelist your token classes using a property called whitelistPatternsChildren.

Another point to consider is that Purgecss doesn't have an extractor for JavaScript files. But because of its modularity, developers can create custom extractors for frameworks like Angular, React or Vue.

UnCSS

Because of its HTML emulation and JavaScript execution, UnCSS is effective at removing unused selectors from web applications. However, its emulation can have a cost in terms of performance and practicality.

At this point in time, UnCSS is probably the most accurate tool to remove unused CSS if you do not use server-side rendering.

Other tools

Here is of other tools you can consider:

  • GitBit: an online tool which crawls your root page and stylesheets linked in it.
  • UnusedCSS: another online tool which easily find and removes unused CSS rules

Summary

Although these tools are really helpful in terms of finding unused CSS, each has its own drawbacks and you will need to be careful to not end up with a broken UI.

Hope you've gained just a tiny bit insight on how to find your unused CSS and deploy them to space 😁👇.

💖 💪 🙅 🚩
yashints
Yaser Adel Mehraban

Posted on May 7, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Knock out your unused CSS
webpack Knock out your unused CSS

May 7, 2019