How to customize Bootstrap v4 using node packages

Gijs Boddeus
Afosto
Published in
11 min readDec 29, 2017

I recently got asked if i could help develop a WordPress theme for crypto currency websites. They were building it using Bootstrap v4 so i already had a lot of ideas on how to build out every page. As I joined the bitbucket repo I was stunned to see that they had no tooling setup. All styles were written in vanilla css… hadn’t done that in a while. Scss has been my go to for the last 3 years, so I decided to change how we worked.

What do I need

All these tools (Unbox Therapy)

For this project I needed to ensure code quality, ease of development by writing unprefixed css and making use of everything Sassy function, mixin and variable that Bootstrap includes. Compiling javascript was not a primary concern for now so that’s for another time.
Looking at the Bootstrap repository I decided on using these tools.

Exploring the possibilities

I started out looking at the Bootstrap Github repo and figure out how they build the distribution and doc files. After that I came across a video where someone used gulp to watch scss files and build on every change. I tried both ways and here is what I found out.

NPM / Yarn

The Bootstrap project uses npm scripts and packages to compile everything and setup a local server via jekyll. For this project I was only interested in compiling my scss to css. I also prefer Yarn over NPM but that is just personal preference, they work the same. It was pretty easy to setup (though npm-run-all didn’t do it for me) and could support everything i needed.

Gulp

Gulp is a really fast way of setting up build pipelines. It is completely javascript based, that means the that you can setup a pipeline with just javascript. I thought it was really cool and the video showed it was pretty fast. As I started building my pipeline in couldn’t figure out how to get stylelint integrated into the pipeline, so I chose to use NPM script for this project.
If you do know how, please tell me.

I use IntelliJ’s WebStorm, a really powerful IDE that I am really liking and is ramping up my development speed. You don’t need an IDE to follow along, you could cope with the command line and notepad, wouldn’t advice it though…

Let’s get started

So now we decided on which tools to use and how to run them we can get started with the project.
If you’ve never used node packages you can follow the setup guides for Yarn or NPM. You need one of these tools to follow along with this article. You’ll also need a basic understanding of the command line. Codecademy has a pretty good course for it that I also followed.
Now that you got all the required tools and knowledge let’s get started.

Start a project

Open up your command line tool and navigate to the directory you’d like to work in, create a new directory and navigate into that directory. It would look something like:

$ cd projects
$ mkdir bootstrap-setup
$ cd bootstrap-setup

If you would like to use git you can init with git init and use all the commands when necessary.
Now we can set our project up using the init command. Running yarn init or npm init the package manager will start setting up al the required files and will ask you a few questions:

name: project-name 
version: (0.0.0) 0.0.1
description: The Project Description
entry point: //a file like index.html or just '.'
test command: //leave empty
git repository: //the repositories url if you have any
keywords: //leave empty (NPM only)
author: // your name
license: N/A

These answers will be used to build package.json. This file is like you project configuration, it includes all key information and what package are used in what environment. The contents of package.json will look something like.

{
"name": "bootstrap-setup",
"version": "1.0.0",
"description": "tooling setup like bootstrap",
"main": ".",
"author": "Gijs Boddeus",
"license": "MIT"
}

Now we can start installing packages and running scripts.

Installing packages

This is the easiest part, it’s just installing all the packages. My project doesn’t need any package to be included in a release build, that means I only need dependencies for my development environment. To add packages as devDependencies you can use what’s called a flag: -D.
To install packages with NPM you run:

npm install <package name> -D

If you use yarn, like me, you run:

yarn add <package name> -D

I started out by installing/adding node-sass by running yarn add node-sass -D. This will install a butt load of node_modules and since it’s the first package it wil create a package.lock file, ignore it, not important.

If you don’t want to go through installing every package separately you can run these commands.

Yarn

yarn add node-sass clean-css-cli autoprefixer postcss-cli stylelint stylelint-scss stylelint-order stylelint-config-standard stylelint-config-recommended-scss watch -D && yarn add bootstrap@4.0.0-beta.3

NPM

npm install node-sass clean-css-cli autoprefixer postcss-cli stylelint stylelint-scss stylelint-order stylelint-config-standard stylelint-config-recommended-scss watch -D && npm install bootstrap@4.0.0-beta.3

Now that we have all the packages we need, we should configure a few things.

Configuring the tools

Photo by Oudom Pravat on Unsplash

To keep it well organized create a directory like build or configs to keep all config files in.

Stylelint

To ensure you and/or you co-developers write clean and consistent code we use a linter to filter out mistakes and display them ass errors. Stylelint is the package Bootstrap uses for their own configuration, as is every other package we just installed. In the build directory add a file called .stylelintrc and copy the contents from the equally named file on the Bootstrap repo.

All these rules defined what is allowed and what will throw an error. For example, "max-empty-lines": 2 will throw an error if you have 4 empty lines between two lines of code.

Postcss

Postcss is required to run autopefixer which will add vendor prefixes when necessary, depending on the configuration you define.

To specify which browsers you wan’t to support you can add an array to the package.json file specifying version like this:

"browserslist": [    
"last 1 major version",
">= 1%",
"Chrome >= 45",
"Firefox >= 38",
"Edge >= 12",
"Explorer >= 10",
"iOS >= 9",
"Safari >= 9",
"Android >= 4.4",
"Opera >= 30"
],

These are the default supported browsers by Bootstrap.

To make it possible to add prefixes when needed you need to create a postcss.config.js file and paste this into it:

'use strict' module.exports = (ctx) => ({
map: ctx.file.dirname.includes('examples') ? false : {
inline: false,
annotation: true,
sourcesContent: true
},
plugins: {
autoprefixer: { cascade: false }
}
})

Now the tools are configured and we can run some scripts… but first we need some scss files. Easiest way, simply add a directory for the scss files and create a main.scss file. In that file import the bootstrap source files from the node_modules:

@import "../../node_modules/bootstrap/scss/bootstrap";

Now we have something to compile and we can start writing scripts.

Defining scripts

Photo by Markus Spiske on Unsplash

Inside you package.json you can add a section in which you can define scripts to be run inside the command line. This is very convenient as you can group multiple scripts that you would normally run separately, into one simple command.
In my project I defined these scripts:

  • css:lint (to test our scss with the stylelint configuration)
  • css:compile (to compile our scss into css files)
  • css:prefix (to add prefixes via autoprefixer)
  • css:minify (to create minified files)
  • css:watch (to start watching for changes in the scss dir)
  • css (to run css:lint, css:compile, css:prefix and css:minify all together)

I actually copied most of the rules from Bootstrap’s package.json, but made some changes as i didn’t use the npm-run-all package (also available as yarn-run-all for yarn) to run multiple commands parallel or synchronous.

Open up your package.json and add an array to place your scripts in. You can scripts like this:

"scripts": {
"[script-name]": "[command line script to run]",
}

css:lint

This command will run stylelint with the configuration we defined earlier. If anything is not according to the rules it will return this in neatly in the command line. The script looks like this.

stylelint --config build/.stylelintrc --syntax scss \"scss/**/*.scss\" --cache --cache-location \"build/.stylelintcache/\"

Basically: run stylelint, use the rules from our config, the code we will lint is scss, go through every scss file in the scss dir and sub directories and cache the results to our build dir. I added the caching to speed up the watcher we will create in a minute.

css:compile

Now we have clean code, we need to transform it to usable code. Node-sass is a node.js sass/scss pre-processor that will compile your files into usable css.

node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/main.scss style.css

Basically: output css in expanded mode, generate source maps for easy debugging, allow 6 decimals, use scss/main.scss and output it as style.css. Easy right :).

css:prefix

We have usable css! well in the newest browsers, unfortunately we still need vendor prefixes. Autoprefixer to the rescue! We can run it as part of postcss as it actually is an addon for it. So the script:

postcss --config build/postcss.config.js --replace \"*.css\" \"!*.min.css\"

Basically: run postcss, use our config and use all .css files, but not the .min.css files and prefix it if needed based on our browserlist.

css:minify

We are all set with usable css and prefixes for all the browser we need to support. You could stop there but a good last step is to minify your files to minimize the file size. Here’s how:

cleancss --level 1 --source-map --source-map-inline-sources --output style.min.css style.css

Basically: run cleancss, level of cleanup to perform clean, create a source map fo the minified file, output style.min.css from style.css.

css

Sweet, everything is setup and we are able to run all of our scripts in our command line. But we don’t want to run all of these scripts separately everytime. We can combine defined scripts into one, single, easy command.

yarn css:lint && yarn css:compile && yarn css:prefix && yarn css:minify

To run these with NPM you need to replace yarn with npm run. Otherwise they will not run.

Basically: run all scripts above in order. If you use npm: replace yarn with npm run. (this is where npm-run-all would come in)

css:watch

Everything now works and can get executed with a single command, awesome!

Sorry, had to do it. (https://i.ytimg.com/vi/-Rt05oUmt5E/maxresdefault.jpg)

We can setup a watcher to run the css script every time an scss file is changed/saved. If run this script you can save your file, refresh your browser and you will see your changes. So without further ado:

watch 'yarn css' scss

After all this your scripts array should look something like:

"scripts": {
"css:lint": "stylelint --config build/.stylelintrc --syntax scss \"scss/**/*.scss\" --cache --cache-location \"build/.stylelintcache/\"",
"css:prefix": "postcss --config build/postcss.config.js --replace \"*.css\" \"!*.min.css\"",
"css:compile": "node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/main.scss style.css",
"css:minify": "cleancss --level 1 --source-map --source-map-inline-sources --output style.min.css style.css",
"css": "yarn css:lint && yarn css:compile && yarn css:prefix && yarn css:minify",
"css:watch": "watch 'yarn css' scss"
}

Running the scripts

Now that everything is working we can go ahead and start customizing bootstrap and creating our theme. Open the main.scss file and replace its content with:

//$primary: #fe39ed;

@import "../node_modules/bootstrap/scss/bootstrap";

Start up the watcher with yarn css:watch and keep an eye on the command line. You should get the following output:

yarn run v1.3.2
$ watch 'yarn css' scss
> Watching scss
$ yarn css:lint && yarn css:compile && yarn css:prefix && yarn css:minify
$ stylelint --config build/.stylelintrc --syntax scss "scss/**/*.scss" --cache --cache-location "build/.stylelintcache/"
scss/main.scss
3:51 ✖ Unexpected missing end-of-source newline no-missing-end-of-source-newline

Seems we have an error according to stylelint. Take a look at line 3 of main.scss. Our rules define we wan’t an empty line as the last line of every file. Go ahead and add an empty line to the file and save it. You should see some action in the command line, it shouldn’t throw any errors and lint + compile + prefix + minify without a problem.

Now to see it in action: create an index.html file in the root of your project. Open the file in a browser. Now add the following to the content of the file:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Bootstrap build setup</title>

<!-- include our style.min.css -->
<link rel="stylesheet" href="./style.min.css">
</head>
<body>
<div class="container mt-5">
<div class="card bg-primary text-light mb-5">
<div class="card-body text-center py-5">
<h1>
$primary
</h1>
</div>
</div>
</div>
</body>
</html>

Refresh your browser and you should see:

$primary will set the background color to #007bff

Now go back to main.scss and have a look at the first line. This line will override the default value of the primary color variable that Bootstrap uses. Delete the two slashes, //, at the beginning of the line and save the file. Node does its magic and tada!

$primary now sets the background #fe39ed

We have now customized bootstrap! You can use variables.scss from the Bootstrap repo as a reference for all the variabels you can use.

Make sure you read the documentation on theming to fully understand how to handle different scenarios and use colors in your custom css.

A good way to structure main.scss is to put all variables into a separate file, also don’t add actual css rules to this file but add them in their own files, and structure the imports like:

@import "variables";
@import "../../node_modules/bootstrap/scss/bootstrap";

// custom styles
@import "layout";
@import "pages";
@import ...

Have a look at bootstrap.build. It’s a Bootstrap configuration tool made by Oleg Berman, really cool!

That’s that

Now you’re ready to create, customize and build a bootstrap based website.

I learned a lot figuring out how to get this up and running, this actually the first i did it without using a framework. I feel like I have a better understanding of how frameworks do it and what is needed to actually deliver distributable files.

Speed of running the scripts via the watcher

Though this works, the watcher is a little slow on picking up changes. Also the speed of compilation isn’t top notch. From what I have seen in Youtube, Gulp should be faster, but I couldn’t get stylelint into the pipline like I wanted to. Also npm-run-all might speed up a thing or two, but i got some overlapping scripts and stuff… I was just being foolish I now realize.

And yes I know Webstorm has built in sass compilation, but the other collaborators are using their preferred editor so I needed this.

Wishlist

  • faster compiling
  • faster watcher
  • hot reloading of the styles (changes are instantly pushed to the browser)
  • js scripts

Hope you liked this article/tutorial

--

--

Gijs Boddeus
Afosto
Editor for

Frontend developer @ Afosto, creating ecommerce tools and stores.