Using PurgeCSS with Bootstrap and Eleventy
Tags: eleventy, 11ty, bootstrap, purgecss
Published: 2021-05-09 15:51:39

*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/](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.
```text
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.

```text
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.

```javascript
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.

```javascript
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.

```javascript
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:*

```javascript
// 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:*

```javascript
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:*

```javascript
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.

```javascript
.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.

```javascript
let compiledCSS = new MinifyCSS().minify(allCSS)['styles'];
```


## Step 11 - Save The New Bootstrap Code
This will output the new CSS to your destination file.

```javascript
fs.writeFileSync(destinationCSS, compiledCSS, {encoding:"utf8"})
```


## The Final Result

```javascript
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](https://github.com/brewhousedigital/celestial-light). It's got all these features and more.