Tailwind is rad but have you ever wanted a customized, slim version of Bootstrap?
In this tutorial, we'll be adding PurgeCSS to our Eleventy project and have it automatically remove unused css while working on our project. It will scan through our nunjucks, javascript, markdown, and whatever other file types you want to use and make sure that we're only using the slimmest version of the Bootstrap library possible.
This tutorial assumes you already have the bootstrap css library added to your project. If not, go to https://getbootstrap.com/ and download the CSS. You can add it anywhere in your project. I keep mine in a source
folder.
For this specific tutorial, you'll need two separate files. One for bootstrap.css
, and another for bootstrap-reboot.css
. This is because PurgeCSS will remove the reboot styles, potentially causing cross-browser issues in your CSS. We will remedy this later in the tutorial.
TLDR on what PurgeCSS is
PurgeCSS can scan through your content using a regex and find all the class names that are used in your project. You then feed it a css file, and it will remove any css that was not found in your project. The minified bootstrap 5 css library is around 150kb. Using PurgeCSS, you can easily drop that down to 15kb.
Step 1 - Install PurgeCSS
Install PostCSS & PurgeCSS, so they can work together.
npm i -D @fullhuman/postcss-purgecss postcss
PostCSS is an additional tool that lets you string together multiple plugins. This will be useful later if you decide you want to use something like Autoprefixer, as you can quickly add in more plugins. I'll go over that more in detail later in the tutorial.
We're also going to install Clean-CSS to help us minify the end result.
npm install clean-css
Step 2 - Create Module File
Create a file in your eleventy _data
folder called compileCSSFramework.js
(or whatever you want to name it. The name doesn't actually matter).
It is important to add it to this folder because the eleventy watch system can sometimes finish its updates before asynchronous requests (like PurgeCSS) are completed. By putting this in the _data
folder, the eleventy watch system will wait until Bootstrap is completed before building the rest of the site.
Step 3 - Setup Require Statements
Add in your dependencies to the top of our new JS file. For this, we'll need the NodeJS file system, a css minifying plugin, our PostCSS and PurgeCSS libraries.
const fs = require("fs");
const MinifyCSS = require("clean-css");
const postCSS = require('postcss');
const purgeCSS = require('@fullhuman/postcss-purgecss');
Step 4 - Add In Modules Export Function
The next step is adding our module.exports. Below our require statements, create your module exports function.
module.exports = async function() {
}
There's a few things we'll need to do here:
1) Safety checks for the folder structure
2) Set up the paths to our files
3) Purge the Bootstrap library
Step 5 - Build The Safety Checks
First is to check if the eleventy _site
folder is built (which doesn't exist when this function runs). This is because the NodeJS WriteFile function is unable to create files inside of folders that don't exist. We're going to make sure these folders are there so that we can successfully save our new bootstrap css.
module.exports = async function() {
// You must create the folder structure first.
// WriteFile does not create files if parent folders are missing
if (!fs.existsSync('_site')){fs.mkdirSync('_site');}
if (!fs.existsSync('_site/css')){fs.mkdirSync('_site/css');}
}
Step 6 - Setup File Paths
Now we are going to define our file paths in variables so that we can easily modify them in the future. There are three that you need:
1) The main bootstrap css source file
2) The target destination
3) The list of content that PurgeCSS needs to scan through.
Inside the module.exports
function:
// Create a custom, purged, version of Bootstrap
const sourceCSS = "source/css/_bootstrap.css";
const destinationCSS = "_site/css/bootstrap.css";
// Add in your file types here
const sourceContent = [
'source/**/*.njk',
'source/**/*.html',
'source/**/*.md',
'source/**/*.liquid',
'source/**/*.js'
];
You'll need to modify the sourceCSS path to match where your bootstrap files are located.
The destinationCSS shouldn't change much since the default eleventy project outputs the static files to the _site
folder. You'll need to modify this if you've changed the defaults.
Inside the source array, the /**/ is a wildcard folder search. In this example, it will find every .njk
, .html
, .md
, .liquid
, and .js
file and scan those for class names. Feel free to add or remove file names for your specific project.
Step 7 - Set Up PostCSS
Setting up PostCSS this way will allow us to add in future plugins like Autoprefixer.
Inside the module.exports
function:
fs.readFile(sourceCSS, (err, css) => {
postCSS([/* Plugins Go Here */])
.process(css, {
from: sourceCSS,
to: destinationCSS
})
.then(result => {
})
.catch(error => {
console.log(error)
});
})
This tells NodeJS to go find our source bootstrap file, and pass the contents to the PostCSS plugin. It takes it, processes it, then returns the finished result. Inside the .then() promise, we'll tell PostCSS what to do with the new, trimmed code.
First, we need to tell it to use PurgeCSS.
Step 8 - Set Up PurgeCSS
Inside the /* Plugins Go Here */ comment, we can add PurgeCSS with our specific option parameters.
Inside the module.exports
function:
postCSS([
purgeCSS({
content: sourceContent,
variables: true,
keyframes: true
})
])
Step 9 - Adding Reboot Back Into The CSS
Since most of the Reboot code is stripped out, we'll need to add it back in when the processing is completed.
Inside our .then
promise, lets grab the result and prepend Reboot.
.then(result => {
let newCSS = result.css;
let rebootCSS = fs.readFileSync('source/css/_reboot.css');
let allCSS = rebootCSS + newCSS;
})
Now we won't have any issues with cross browser CSS.
Step 10 - Minifying The Output
This step is optional, but useful for keeping a small file size. Below our combined reboot + css variable, add in this easy one-liner to minify the result.
let compiledCSS = new MinifyCSS().minify(allCSS)['styles'];
Step 11 - Save The New Bootstrap Code
This will output the new CSS to your destination file.
fs.writeFileSync(destinationCSS, compiledCSS, {encoding:"utf8"})
The Final Result
const fs = require("fs");
const MinifyCSS = require("clean-css");
const postCSS = require('postcss');
const purgeCSS = require('@fullhuman/postcss-purgecss');
module.exports = async function() {
// You must create the folder structure first.
// WriteFile does not create files if parent folders are missing
if (!fs.existsSync('_site')){fs.mkdirSync('_site');}
if (!fs.existsSync('_site/css')){fs.mkdirSync('_site/css');}
// Create a custom, purged, version of Bootstrap
const sourceCSS = "source/css/_bootstrap.css";
const destinationCSS = "_site/css/bootstrap.css";
// Add in your file types here
const sourceContent = [
'source/**/*.njk',
'source/**/*.html',
'source/**/*.md',
'source/**/*.liquid',
'source/**/*.js'
];
fs.readFile(sourceCSS, (err, css) => {
postCSS([
// Purge CSS will scan through and remove the styles
// that aren't in your project
purgeCSS({
content: sourceContent,
variables: true,
keyframes: true
})
])
.process(css, {
from: sourceCSS,
to: destinationCSS
})
.then(result => {
// Combine with Reboot
let newCSS = result.css;
let rebootCSS = fs.readFileSync('source/css/_reboot.css');
let allCSS = rebootCSS + newCSS;
// Minify
let compiledCSS = new MinifyCSS().minify(allCSS)['styles'];
// Save
fs.writeFileSync(destinationCSS, compiledCSS, {encoding:"utf8"})
})
.catch(error => {
console.log(error)
});
})
};
Now when you spin up your local server, npx eleventy --serve
, PurgeCSS will be running in the background, cleaning up any unused code from Bootstrap.
If you want to add a different library like Material UI, or Foundation, you should be able to swap out your path variables with the new CSS. If you do use something besides Bootstrap, make sure to change the lines pertaining to Reboot CSS.
If you'd like to use a ready-to-use eleventy template, try out the Celestial template on github. It's got all these features and more.
Top comments (3)
You read my mind, I was just thinking about this. I will give it a whirl this week! Thanks for the write up :)
Good luck!! Let me know if you have any questions and I'll do my best to help.