Sun Jun 10 2018 15:17:54 GMT-0400 (EDT)

Drastically Reduce your CSS File Size with PurifyCSS and webpack

There is absolutely zero use of including CSS in your project if it's not being used across any of your site. Why include Bootstrap's hundred-something classes if you're only making use of its grid?

Doing so creates a bloated CSS file that slows your site down and in return affects your pagespeed scores and SEO ranking (Google now factors mobile sites' SEO ranking based on page speed). As a result, we'll want to ensure we're only adding the exact classes required for our site to function, and we can do this using no other than: PurifyCSS.

Before PurifyCSS:

152kb worth of unused classes. Y I K E S.

After PurifyCSS:

5.92kb, a 96% size reduction. 96 FREAKIN PERCENT.

PurifyCSS is a command-line tool "that takes content (HTML/JS/PHP/etc) and CSS, and returns only the used CSS."

Let's say we have two files: index.html and style.css. If we're only making use of Bootstrap's grid, we'll want to make sure style.css only contains Bootstrap's grid classes and nothing else. We could hand-pick the grid classes out manually, but that would be an extremely time consuming process, so instead,  we'll use PurifyCSS to hand-pick those classes automatically.

1.Project Setup

Now before we begin, I should mention PurifyCSS has its own set of command-line functions that'll purify your CSS files on an as-needed basis, however, I'd rather have my CSS purified automatically whenever a file is changed, so I'm going to use webpack to watch for any changes instead.

Let's start off by setting up a basic webpack template that is responsible for managing our HTML, CSS, and JavaScript.

First, clone the project template:

git clone https://github.com/chriscourses/webpack-html-boilerplate.git
Clone er' up.

Second, cd into the new directory and install any project dependencies with yarn or npm:

cd webpack-html-boilerplate;
yarn

or

cd webpack-html-boilerplate;
npm install
Activate download hyperdrive...

Third, run webpack (make sure you have webpack installed globally to do this). Our project should open up automatically in the browser:

Now that we have a boilerplate up and running, let's integrate PurifyCSS into our project.

2.Integrating PurifyCSS into Webpack

You may have noticed that we already have a basic SCSS file being used within our project, style.scss:

./src/scss/style.scss

This file is importing Bootstrap's CSS (152kb large), which in turn has it's classes being used in index.html:

./src/index.html

Let's utilize PurifyCSS to only add the classes being used within index.html into our outputted style.css file in /dist.

2.1 Install Dependencies

Before we're able to make use of PurifyCSS, we first need to install it along with some associated packages:

yarn add purifycss-webpack purify-css glob-all --dev

or

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

2.2 Update webpack.config.js

With the dependencies installed, we'll need to update our webpack config at webpack.config.js (this what connects PurifyCSS to our actual project):

const BrowserSyncPlugin = require('browser-sync-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const PurifyCSSPlugin = require('purifycss-webpack')
const path = require('path')
const glob = require('glob-all')

module.exports = {
    entry: './src/main.js',
    output: {
        path: __dirname + '/dist/',
        filename: 'js/main.bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.scss$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: 'css-loader!sass-loader'
                })
            },

            {
                test: /\.(png|jpg|gif|svg)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            outputPath: 'img'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin('css/style.css'),
        new BrowserSyncPlugin({
            host: 'localhost',
            port: 3000,
            server: { baseDir: ['dist'] },
            files: ['./dist/*', '**/*.html']
        }),
        new HtmlWebpackPlugin({
            title: 'My App',
            template: 'src/index.html',
            filename: 'index.html'
        }),
        new PurifyCSSPlugin({
            paths: glob.sync([
                path.join(__dirname, 'src/*.html'),
                path.join(__dirname, 'src/*.js')
            ]),
            minimize: true,
            purifyOptions: {
                whitelist: []
            }
        })
    ],
    watch: true,
    devtool: 'source-map'
}

We're pulling in the recently downloaded project dependencies on lines 5-7, then telling webpack to make use of PurifyCSS by adding its webpack loader code to our plugins property on lines 51-60.

Run webpack, and voila, you should see that PurifyCSS has stripped out all of the unused CSS with our page styling remaining intact:

A truly amazing result to a truly amazing plugin.
Same styling, yet less code and a faster load.

2.3 Declaring Additional Paths

In order for this plugin to work, its required to specify what paths should be searched so that any CSS classes within them will be included in our final style.css file.

Here you can see that we're telling webpack to look through all files that end with a .html or .js extension within the src directory:

paths: glob.sync([
    path.join(__dirname, 'src/*.html'),
    path.join(__dirname, 'src/*.js')
])

glob-all is a node module that will allow us to provide directory patterns using the * symbol, while path is a module that'll help create a full path to whatever directories we'd like to search through.

Again, we're telling PurifyCSS to search for all CSS classes within .html and .js files—if you'd like to search through additional files, let's say PHP files, you'd add an additional path for PurifyCSS to search through:

paths: glob.sync([
    path.join(__dirname, 'src/*.html'),
    path.join(__dirname, 'src/*.js'),
    path.join(__dirname, '**/*.php')
    // searches all directories for all
    // files that end with .php
])

2.4 Whitelisting Classes

I'm going to skip over the minimize property (minimizes your CSS after purification) to focus on the whitelist property instead:

new PurifyCSSPlugin({
    paths: glob.sync([
        path.join(__dirname, 'src/*.html'),
        path.join(__dirname, 'src/*.js')
    ]),
    minimize: true,
    purifyOptions: {
        whitelist: []
    }
})

Adding values to the whitelist property array ensures that any classes that might not be caught by PurifyCSS, are indeed monitored and added to our end CSS file. For instance, if you're using WordPress, it's common you'll want to make use of some sort of plugin within your theme. Since these plugins are located in a completely different directory with an unpredictable directory structure, we have no way for PurifyCSS to accurately watch for changes within them. As a result, we can update our whitelist array to watch for any classes that PurifyCSS might've missed.

Here's an actual example of a PurifyCSS config within a WordPress project I've been working on:

new PurifyCSSPlugin({
    paths: glob.sync([
        path.join(__dirname, '*.php'),
        path.join(__dirname, 'templates/*.php'),
        path.join(__dirname, 'templates/partials/*.php'),
        path.join(__dirname, 'src/*.js')
    ]),
    minimize: true,
    purifyOptions: {
        whitelist: [
            '*gform_fields*',
            '*gform*',
            '*gfield*',
            '*svg*',
            '*alignright*',
            '*validation*',
            '*fa-facebook*',
            '*fa-twitter*',
            '*fa-linkedin*',
            '*fa-youtube*',
            '*fa-instagram*',
            '*fa-plus*',
            '*slick*',
            '*mw100*'
        ]
    }
})

Essentially, any CSS class that isn't taking effect probably isn't being monitored by PurifyCSS. As a result, you have two options:

  1. Ensure PurifyCSS is looking for classes within a particular file using the paths property
  2. Tell PurifyCSS to always look for a particular class using the whitelist property

Try to keep the amount of whitelisted classes to a minimum and only use if encountering an edge case such as pulling in HTML via WordPress plugins—doing otherwise will produce unnecessary bloat to your resultant CSS file.

Conclusion

To summarize, to utilize PurifyCSS's file shrinking wonders, you'll want perform the following:

  1. Install PurifyCSS dependencies
  2. Integrate PurifyCSS into webpack
  3. Declare what paths and files you want to monitor for CSS classes
  4. Add any classes not being tracked to your whitelist config

With that being said, you should now have a basic grasp on why you'd want to use PurifyCSS and how to integrate it into your project. For additional PurifyCSS information, check out the PurifyCSS and purifycss-webpack git repos.

Any questions or post edits? Leave a comment or reach out to me via one of the social media outlets on the site's footer.

Completed Project Files


Support the Cause

All Chris Courses tutorials are completely free—help support the production of more free blog posts and tutorials via PayPal or Patreon.