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.
And here are the result 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.
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.