DEV Community

Brewhouse Digital
Brewhouse Digital

Posted on

Using PurgeCSS with Bootstrap and Eleventy

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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() {

}
Enter fullscreen mode Exit fullscreen mode

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');}
}
Enter fullscreen mode Exit fullscreen mode

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'
];
Enter fullscreen mode Exit fullscreen mode

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)
  });
})
Enter fullscreen mode Exit fullscreen mode

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
  })
])
Enter fullscreen mode Exit fullscreen mode

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;
})
Enter fullscreen mode Exit fullscreen mode

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'];
Enter fullscreen mode Exit fullscreen mode

Step 11 - Save The New Bootstrap Code

This will output the new CSS to your destination file.

fs.writeFileSync(destinationCSS, compiledCSS, {encoding:"utf8"})
Enter fullscreen mode Exit fullscreen mode

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)
    });
  })
};

Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
rickthehat profile image
Rick G.

You read my mind, I was just thinking about this. I will give it a whirl this week! Thanks for the write up :)

Collapse
 
brewhousedigital profile image
Brewhouse Digital • Edited

Good luck!! Let me know if you have any questions and I'll do my best to help.

Collapse
 
Sloan, the sloth mascot
Comment deleted