Most web sites use a CSS library like Bootstrap. It’s great for getting started quickly and works well as an all-purpose style sheet.

But what about all the selectors in Bootstrap you aren’t using in your app? What if you also add a Bootstrap theme and your own web app style overrides on top of that? All the used and unused styles go down to your users’ browsers with every response.

If you can trim that down to only the styles your web app actually uses, you’ll send down a much smaller style sheet, your site will run faster, and you’ll save on bandwidth costs.

Here’s how I did just that with PurifyCSS and webpack.

Install webpack and PurifyCSS

Once you’ve got Node and NPM up and running, install webpack 4.

npm install webpack webpack-cli --save-dev

Then install PurifyCSS and the MiniCssExtractPlugin, if you don’t already have it set up.

npm install purify-css purifycss-webpack glob-all mini-css-extract-plugin --save-dev

Set up PurifyCSS in webpack.config.js

In your webpack.config.js file, add a reference to the MiniCssExtractPlugin, the PurifyCSS plugin, and a way to glob files.

const glob = require("glob-all");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const PurifyCSSPlugin = require("purifycss-webpack");

Then set up your paths in PurifyCSS. These are the locations of the files that will contain your styles. PurifyCSS will statically scan these files for styles and reference them in the style sheet. Styles found in the markup or scripts are left in the final style sheet. Styles not found are removed. The algorithm for finding used styles is pretty sophisticated.

        plugins: [
            new MiniCssExtractPlugin({
                filename: "[name].css"
            }),
            new PurifyCSSPlugin({
                paths: glob.sync([
                    path.join(__dirname, "Views/**/*.cshtml"),
                    path.join(__dirname, "Scripts/**/*.html"),
                    path.join(__dirname, "Scripts/**/*.js")
                ])
            })
        ],

Here, I’m referencing my ASP.NET MVC Razor pages in Views/**/*.cshtml, my component templates in Scripts/**/*.html, and my project *.js files. Also note this plugin entry goes after the MiniCssExtractPlugin.

Add any whitelist exceptions

Even with it’s fancy algorithm, PurifyCSS can still miss styles it should leave alone. To prevent PurifyCSS from removing them from the final CSS, use the whitelist option.

Here, I’ve got a project style sheet with two styles – one that should be removed because it is not referenced, and one that should be left in even though it isn’t referenced.

.some-unused-class {
    color: red;
}

.some-dynamic-class {
    color: red;
}

To do this, I’ve added "*some-dynamic-class*" to the whitelist array so PurifyCSS won’t remove it.

        plugins: [
            new MiniCssExtractPlugin({
                filename: "[name].css"
            }),
            new PurifyCSSPlugin({
                paths: glob.sync([
                    path.join(__dirname, "Views/**/*.cshtml"),
                    path.join(__dirname, "Scripts/**/*.html"),
                    path.join(__dirname, "Scripts/**/*.js")
                ]),
                purifyOptions: {
                    whitelist: ["*some-dynamic-class*"]
                }
            })
        ],

Results

In the sample project, I’m using Bootstrap and a very small app style sheet. Here are the result before PurifyCSS.

before PurifyCSS

And here are the result after PurifyCSS.

after PurifyCSS

The sample site is a one-page site with a carousel component that references Bootstrap CSS, and as you would expect, the KB trimmed from removing unused CSS selectors is significant.

Here is what happened with that class .some-dynamic-class I put in the PurifyCSS whitelist.  The .some-unused-class selector is removed as we’d expect, and the .some-dynamic-class is still in the final CSS file at the bottom.

whitelist style left in

All this code is on GitHub if you want to see the package.json or full webpack.config.js or mess around with this yourself.