How to build bootstrap css with jekyll?

Hi,

tl;dr: How to build a custom bootstrap with jekyll?

I utilize bootstrap 5 for most of my websites. (https://getbootstrap.com/)
Bootstrap is a CSS framework that makes it easier to make a pretty, responsive website.

In a nutshell, it works by including a CSS file on my website that then provides a lot of pre-defined CSS classes that I can utilize.

This works very well except for one problem: the included CSS file is quite big - almost 30 kb.
I would like to slim that down to less than 15 kb.

The reason bootstrap.css is 30kb is that it contains a lot of classes, variables, etc. for all kinds of things which I do not need.
Fortunately, the bootstrap people thought about this and made it possible to build a custom CSS file using Sass (Sass · Bootstrap v5.3)
This way, I can build my own custom bootstrap.css that only contains the classes that I utilize and skip the rest.

Since Jekyll comes with a Sass compiler I thought it would be easy to just put the .scss files from bootstrap into my _sass folder in my jekyll site and have jekyll compile it.
However, this proved more difficult than I imagined.

I hope to hear from someone who has managed to build a custom bootstrap with jekyll.
How did you do it?

I don’t think it is that easy to do - I think you need to be able to have a build tool run some commands to build out the css with just the things needed - but it is probably using the full css when doing dev locally?

I’ve not seen any posts about how to do this type of thing with BS - though Tailwind is similar and I have seen posts about that - and there was a fair amount of setup to do it, looked like a bit of a pain.

If you google remove unused css jekyll I think you might find some easier solutions where you could just use the full BS css and then run a command to strip it down to what you need when you build for production, probably similar to the way BS wants you to do it but sort of manually triggered?

Another idea might be to separate out the css/assets from your project and have jekyll just do the layouts/includes/markdown stuff. I used to do this using gulp and then npm and it worked well but was a little complicated.

Hi there, I feel like I am only answering 1/2 of your question, but since I only know one method, I thought this could at least get you started.

There are two ways to access Bootstrap for any site:

  1. Connect to the CDN, which means your website will connect to an external source to connect to the Bootstrap components. This is the easiest method; you can jump into it immediately with Jekyll.
  2. Download the Bootstrap components and use them on your website. This may include a one-time download, modifications to the default Minima site, and more.

I know how to do #1, and it is easy. I will assume you are starting with Jekyll’s default minima theme. I suggest you do as well to verify you get the right results. Once you know what you are doing, go ahead and continue modifying the Minima site, create a site from scratch, or play around with other Jekyll themes.

  1. Override your theme
  2. Among other things, you will now have two new folders: _includes and _layouts
  3. Edit the _includes/head.html file, follow this link. Copy the <!-- Required meta tags --> and <!-- Bootstrap CSS --> elements and paste them into the <head> section of your head.html file.

Note: You must delete the existing utf-8 and viewport lines; more on that viewport line in a moment!

  1. Save head.html
  2. Open the _layouts/default.html and paste the JavaScript you want to use just above the </body> tag. Nothing else should be between the Bootstrap code and that </body> tag. Use the same Bootstrap link from #3 above. I usually copy the line under <!-- Option 1: Bootstrap Bundle with Popper --> and paste that into the default.html file.
  3. Save the default.html file
  4. To test that Bootstrap works, edit the /index.md file and add the sample code to build a navigation bar.
  5. Save index.html
  6. You are done adding Bootstrap, and you can run bundle exec jekyll serve --livereload to see the results, but please read on for more details…

Bootstrap and Minima

Jekyll’s default Minima has custom CSS or SASS/SCSS, like many themes. It is much easier to start from scratch using Bootstrap components.

However, if you want to use the Minima theme and play around with a pre-built site, please see the attached screenshots. Part of setting up Bootstrap was to change the meta viewport, which includes settings incompatible with the Minima theme.

It is easy to fix because you will probably replace the website header with Bootstrap navbar and nav- components. The footer will look a little broken with the columnar layout, but you can easily replace that with the grid systen using <section> and the row and col classes.


Next steps

After you are comfortable using Bootstrap with Jekyll, you can move on to adding the files directly to your site, but you probably want to manage the installation of those files for version control. If so, you will probably want to switch to Docker. While I create a video that shows you how to use Docker with GitHub Pages, I have not gone through the process of

Actually what you want to achieve is not done by sass compilers. It must be done by another tool, like UnCSS or PurgeCSS. These tools analyze your stylesheet and discard from it any classes that are not being used.

I used PurgeCSS in a template site that I am currently a maintainer. This was done in 2 steps:

  1. Created purgecss.config.js with all the rules that will be used to clean the stylesheet, which in our case is:
module.exports = {
  content: ["_site/**/*.html", "_site/**/*.js"],
  css: ["_site/assets/css/*.css"],
  output: "_site/assets/css/",
  skippedContentGlobs: ["_site/assets/**/*.html"],
};
  1. Added a step in our GitHub action responsible for building and deploying the site to install and call PurgeCSS:
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # steps to download and build the site first
      - name: Purge unused CSS 🧹
        run: |
          npm install -g purgecss
          purgecss -c purgecss.config.js
      # steps to deploy site

At the time I implemented this I got the following reduction is css sizes:

current minify PurgeCSS PurgeCSS + minify
1,1M 988K 456K 420K

I think this might be a good approach: to discard things from bootstrap.css that are not utilized.

However, I am a bit hesitant to proceed with anything in which the instructions start with “npm install …”
I currently do not use node.js in any projects and to be honest, I don’t quite understand what it is… But I am a bit afraid of introducing some huge dependency.

Are there no standalone tools that will do something similar to UnCSS or PurgeCSS that are just a static compiled binary or perhaps a python script?

Or are my concerns about node.js/npm unwarranted?

Are there no standalone tools that will do something similar to UnCSS or PurgeCSS that are just a static compiled binary or perhaps a python script?

I am not aware of any.

Or are my concerns about node.js/npm unwarranted?

It depends. You are working with some web technology, and sometimes they can get intertwined. And you can benefit from both.

Also npm is something like pip for python, used to install packages and libraries, and sometimes these packages can run as a standalone. For example, if you install tqdm in python, it can be run as a standalone, even though it was installed via pip. The same is true for npm.

If you look at the GitHub action above, after installing PurgeCSS with npm I simply run it with purgecss -c purgecss.config.js. So, I understand your concern about adding too much dependencies, but since this is just another step in a build process that can run in a remote isolated machine, I think it is not much trouble.

I am still trying to make this work in a good way.

However, I have another issue that might require me to have to build bootstrap after all: I need to be able to override the default bootstrap color theme (Color · Bootstrap v5.0)

Now, this should be possible using Sass. And I thought I could just go ahead and define the color names in my _sass/main.scss. Alas, this does not seem to work.

So will I have to actually build bootstrap in its entirety for this?

If I do have to do that, it sounds like it might be better to have a separate project for building and minifying bootstrap and then just putting the resulting bootstrap.min.css into my jekyll project. Agree?

are you saying that you can’t get a sass variable to work in general?

Have you downloaded all the BS sass files somehow? if so I would think you should be able to override the variables.

You may want to try doing this with your own sass files just to get a handle on it.

Its been a while since I used sass in jekyll much, if I remember correctly there were some things that worked in the sass folder that didn’t work in the scss main file that imported them?

You probably can combine jekyll and BS and just have node scripts or gulp scripts monitor different things and build either jekyll or BS as needed. It can be a little complicated but I did it once upon a time and learned a lot. Here is a blog on what I did, mademistakes did something similar around the same time: Speed Up Jekyll By Using Gulp For Sass And Other Assets while it is not about BS I think you could do something similar with it.

I believe you are over complicating things. Check this answer on stackoverflow.

I certainly hope you are right and that there is an easier solution.

I checked your link and the accepted solution is this:

// required to get $orange variable
@import "functions"; 
@import "variables";

$primary: $orange; // set the $primary variable

// merge with existing $theme-colors map
$theme-colors: map-merge($theme-colors, (
  "primary": $primary
));

// set changes
@import "bootstrap";

As I understand, this only works if I am building bootstrap from source. Is this not the case?

And this is really what this thread was originally about.
I set out to build bootstrap with jekyll specifically because I wanted to minimize the size of the resulting CSS by not including the components I do not use.

I came to understand that this is not possible with jekyll as it only comes with a Sass compiler and I would need all kinds of other stuff in addition to a Sass compiler. Maybe I misunderstood this?

Sorry, you are right. You need to build from source (I usually think about building from source when it is a compiled language that is not web technology). Jekyll will not remove the unused css, you’ll need to use another tool for this. There might be some plugins (like this one), but this can’t be achieved with jekyll alone.

Thanks for your quick responses!
So I guess I need to build from source in order to remove unused CSS from bootstrap. Is it also correct that I need to build from source in order to use the proposed solution on your stackoverflow link?

If this is the case I am thinking of creating a separate build server/build user for building bootstrap. I can then import the resulting bootstrap.min.css into my jekyll site.

This means that every time I want to update bootstrap, change color variables or utilize new components in bootstrap I will have to rebuild from source and import the new bootstrap.

Since I am using jekyll on multiple sites, I think this might be a workable solution.

The reason I want the external build on a separate account is that how I have deployed my jekyll sites are in a way where the production server will check out the jekyll site from version control and then run a build script (Makefile) to update the site. And I dont want to complicate the prod server by introducing node.js and all sorts of dependencies. But having them on a separate user on my laptop or a VM is not a problem.

It could work, except for the cleaning the css part. Unless all your sites use the exact same subset of classes.

PurgeCSS at least requires the final version of the site to be able to check what css classes are not being used.

Hi There,

I think I solved all your worries and problems for you (I hope!). Buckle in!

Before you begin

  1. Please test using the Jekyll Minima theme and override the theme. Once you understand the process, add it to whatever theme you like
  2. Connect your code to GitHub with GitHub Pages active. I assume you know how to do all this

Manually copy Bootstrap to your site (we will automate updates later)

We are going to do this step manually so you understand the process. Later, you will know what happens when I provide you with an automated way of getting the latest version.

  1. Go to the Bootstrap download page and download the files
  2. Extract that zip file; you will have a css and js folder.
  3. In your Jekyll site, copy those two folders into your assets folder. Your new assets folder will look something like this:
/assets
├── /css
│   └── [all the Bootstrap stuff you copied]
├── /js
│   └── [all the Bootstrap stuff you copied]
├── main.scss
└── minima-social-icons.svg

Add Bootstrap capabilities to your site

In this step, you will ensure Bootstrap is available to your entire site by updating the theme (remember earlier, I asked you to override the theme? This is why).

  1. Open _includes/head.html. The code will look something like this:
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  {%- seo -%}
  <link rel="stylesheet" href="{{ "/assets/main.css" | relative_url }}">
  {%- feed_meta -%}
  {%- if jekyll.environment == 'production' and site.google_analytics -%}
    {%- include google-analytics.html -%}
  {%- endif -%}
</head>
  1. This is very important since you want to override Bootstrap colors later. Locate where the code links main.css and add your Bootstrap code above that. Here is the new code with that one new single line (and some comments):
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  {%- seo -%}
  <!-- Add bootstrap -->
  <link rel="stylesheet" href="{{ "/assets/css/bootstrap.min.css" | relative_url }}">
  <!-- Jekyll theme comes after bootstrap so you can do overrides -->
  <link rel="stylesheet" href="{{ "/assets/main.css" | relative_url }}">
  {%- feed_meta -%}
  {%- if jekyll.environment == 'production' and site.google_analytics -%}
    {%- include google-analytics.html -%}
  {%- endif -%}
</head>
  1. Since you likely want to use Bootstrap with JavaScript, open the _layouts/default.html file. It should look something like this:
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: "en" }}">

  {%- include head.html -%}

  <body>

    {%- include header.html -%}

    <main class="page-content" aria-label="Content">
      <div class="wrapper">
        {{ content }}
      </div>
    </main>

    {%- include footer.html -%}

  </body>

</html>
  1. It is a best practice to add your Bootstrap JS code just above the </body> element. Include that line just as I have done in the sample code below:
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: "en" }}">

  {%- include head.html -%}

  <body>

    {%- include header.html -%}

    <main class="page-content" aria-label="Content">
      <div class="wrapper">
        {{ content }}
      </div>
    </main>

    {%- include footer.html -%}
    <!-- Add bootstrap js -->
    <script src="{{ '/assets/js/bootstrap.bundle.min.js' | relative_url }}"></script>
  </body>

</html>

Test that Bootstrap functions properly

Now that you have successfully added Bootstrap to your site, test it to ensure that it functions properly.

  1. Open your /index.md file
  2. The code should look something like this:
---
# Feel free to add content and custom Front Matter to this file.
# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults

layout: home
---
  1. Update the index.md file to display a navbar and button. Here is some sample code:
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
      <ul class="navbar-nav">
        <li class="nav-item active">
          <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Features</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Pricing</a>
        </li>
      </ul>
    </div>
  </nav>

<div class="container">
    <h1 class="text-primary">Hello, Bootstrap!</h1>
    <button class="btn btn-primary">Primary Button</button>
</div>


**The rest of Jekyll Minima stuff will display below:**
  1. Run bundle exec jekyll serve --livereload and open the link. Your new site should look something like this:

Accomplishment #1: Add Bootstrap to your site

You have included Bootstrap on your Jekyll site, so you could stop here and create whatever code you want to manage your site.

If you started with a blank Jekyll site, you now know the best way to add Bootstrap code so it appears everywhere.

Overriding Bootstrap CSS

You want to override colors in Bootstrap. Mind you, this is not easy, so know what you are getting into. You will want to ask on other forums about the best method, but I will show you a simple way to change the button text and the button from blue (default) to red.

  1. Locate the /_sass/minima folder in your Jekyll site. Add a new file and call it _override-bootstrap.scss
  2. In your new /_sass/minima/override-bootstrap.scss file, add the following code to change our components from blue to red:
/* custom.css */
:root {
    --bs-primary: #ff0000; /* Red color */
  }
  
  .btn-primary {
    background-color: var(--bs-primary) !important;
    border-color: var(--bs-primary) !important;
  }

  .text-primary {
    color: var(--bs-primary) !important;
  }
  1. Save the file
  2. Open the file /_sass/minima.scss. The code should look something like this:
@charset "utf-8";

// Define defaults for each variable.

$base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
$base-font-size:   16px !default;
$base-font-weight: 400 !default;
$small-font-size:  $base-font-size * 0.875 !default;
$base-line-height: 1.5 !default;

$spacing-unit:     30px !default;

$text-color:       #111 !default;
$background-color: #fdfdfd !default;
$brand-color:      #2a7ae2 !default;

$grey-color:       #828282 !default;
$grey-color-light: lighten($grey-color, 40%) !default;
$grey-color-dark:  darken($grey-color, 25%) !default;

$table-text-align: left !default;

// Width of the content area
$content-width:    800px !default;

$on-palm:          600px !default;
$on-laptop:        800px !default;

// Use media queries like this:
// @include media-query($on-palm) {
//   .wrapper {
//     padding-right: $spacing-unit / 2;
//     padding-left: $spacing-unit / 2;
//   }
// }
@mixin media-query($device) {
  @media screen and (max-width: $device) {
    @content;
  }
}

@mixin relative-font-size($ratio) {
  font-size: $base-font-size * $ratio;
}

// Import partials.
@import
  "minima/base",
  "minima/layout",
  "minima/syntax-highlighting";
  1. Modify the /_sass/minima.scss file to include your override file like this:
@charset "utf-8";

// Define defaults for each variable.

$base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
$base-font-size:   16px !default;
$base-font-weight: 400 !default;
$small-font-size:  $base-font-size * 0.875 !default;
$base-line-height: 1.5 !default;

$spacing-unit:     30px !default;

$text-color:       #111 !default;
$background-color: #fdfdfd !default;
$brand-color:      #2a7ae2 !default;

$grey-color:       #828282 !default;
$grey-color-light: lighten($grey-color, 40%) !default;
$grey-color-dark:  darken($grey-color, 25%) !default;

$table-text-align: left !default;

// Width of the content area
$content-width:    800px !default;

$on-palm:          600px !default;
$on-laptop:        800px !default;

// Use media queries like this:
// @include media-query($on-palm) {
//   .wrapper {
//     padding-right: $spacing-unit / 2;
//     padding-left: $spacing-unit / 2;
//   }
// }
@mixin media-query($device) {
  @media screen and (max-width: $device) {
    @content;
  }
}

@mixin relative-font-size($ratio) {
  font-size: $base-font-size * $ratio;
}

// Import partials.
@import
  "minima/base",
  "minima/layout",
  "minima/syntax-highlighting", 
  "minima/override-bootstrap"
;
  1. Save the file and test. When you preview the site, you will notice the text and accompanying button are now red!

Accomplishment #2: Override Boostrap

You can now use that override-bootstrap.scss file to override bootstrap elements to your heart’s content. Again, look up best practices from here because I make no guarantees I followed best practices :-).

Automatically update Bootstrap

I do not recommend updating Bootstrap automatically since you could wake up one morning and find your site not working correctly. You should probably manually update those css and js folders when necessary.

That said, you can use a GitHub Action. As I write this, GitHub Actions are free but with limitations. Look those up before blindly following my instructions, but if this is your only GitHub workflow, running a weekly update should not impact your account.

  1. If it does not already exist, create a new folder in the root of your Jekyll site and call it /.github. Create a sub-folder and call it workflows. Finally, create a new file and name it update-bootstrap.yml. The folder structure should look like this:
/my-jekyll-site
├── /.github
├──── /.workflows
│   └──── update-bootstrap.yml
  1. Update the update-bootstrap.yml to include the following code:
name: Update Bootstrap

on:
  schedule:
    - cron: '0 0 * * 0' # Run weekly on Sunday at midnight
  workflow_dispatch:

jobs:
  update-bootstrap:
    runs-on: ubuntu-latest

    steps:
    # Step 1: Checkout the repository
    - name: Checkout repository
      uses: actions/checkout@v2

    # Step 2: Set up Node.js environment
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: 14

    # Step 3: Install necessary dependencies
    - name: Install dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y wget unzip jq

    # Step 4: Download and extract the latest Bootstrap version
    - name: Download and extract the latest Bootstrap version
      run: |
        # Fetch the latest release tag from the Bootstrap GitHub repository
        LATEST_RELEASE=$(curl --silent "https://api.github.com/repos/twbs/bootstrap/releases/latest" | jq -r .tag_name)
        LATEST_VERSION=${LATEST_RELEASE#v}
        echo "Latest Bootstrap version: $LATEST_VERSION"
        echo "version=$LATEST_VERSION" >> $GITHUB_ENV

        # Download the latest Bootstrap distribution zip file
        wget https://github.com/twbs/bootstrap/releases/download/v${LATEST_VERSION}/bootstrap-${LATEST_VERSION}-dist.zip -O bootstrap.zip
        # Extract the downloaded zip file
        unzip bootstrap.zip -d bootstrap

        # Copy the CSS and JS files to the appropriate directories
        mkdir -p assets/css assets/js
        cp -r bootstrap/bootstrap-${LATEST_VERSION}-dist/css/* assets/css/
        cp -r bootstrap/bootstrap-${LATEST_VERSION}-dist/js/* assets/js/
      env:
        version: ${{ env.version }}

    # Step 5: Commit and push the changes to the repository
    - name: Commit and push changes
      run: |
        # Configure Git with a dummy user for the commit
        git config --global user.name 'github-actions[bot]'
        git config --global user.email 'github-actions[bot]@users.noreply.github.com'
        # Add all changes, including untracked files
        git add .
        git status
        # Commit the changes with a message, or skip if there are no changes
        git commit -m "Update Bootstrap to version $LATEST_VERSION" || echo "No changes to commit"
        # Push the changes to the main branch
        git push origin main
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  1. Save, commit, and push your code up to GitHub

  2. In GitHub, navigate to the repository and:
    a) Make sure GitHub Pages are active on main/root
    b) Test that you can access the GitHub repository, and it looks like my previous screenshot (the one with the red text and button)
    c) Go to Repository->Settings->Actions->General->Workflow Permissions->Read and write permissions (it was probably read-only)->Save

  3. Go to your repository->Actions->Update Bootstrap->Run Workflow->Run Workflow
    a) You can select the running workflow and watch it update

If the workflow does not run successfully, paste the code and the error log and share that on a support board. I do not claim to want to maintain this code :slight_smile:

Accomplishment #3: Automatically update to the latest version of Bootstrap

You now have a Jekyll site that:

  1. Uses Bootstrap
  2. Allows you to override Bootstrap
  3. Updates Bootstrap to the latest version every Sunday at Midnight

Code availability

I created a GitHub repo with the code I used to write this, which you can access here:

And here is a link to the working GitHub Pages site:
https://billraymond.github.io/github-pages-with-bootstrap/

I happened to create this site using Visual Studio Dev Containers with Docker (hence the Dockerfile and the .devcontainer folder), but you can ignore those if you want.

Good luck!

Wow this is a very detailed process documented here. Does this solution minimizes the bootstrap file, removing unused classes @BillRaymond?

What’s the issue with my solution? The file sizes are tiny.