Using Bootstrap 4 with Metalsmith

Christian Emmer
Christian Emmer
Sep 22, 2019 · 6 min read
Using Bootstrap 4 with Metalsmith

As a primarily back-end developer I tend to use Bootstrap in most of my full-stack applications. It's a great way to save some time getting a small project or prototype up and running. Using Bootstrap in Metalsmith isn't complicated, it just takes 3 plugins.

Project setup

This guide assumes some base understanding of Metalsmith, see "Starting a Metalsmith Project" on how to set up a basic project.

Starting a Metalsmith Project
Starting a Metalsmith Project
Sep 19, 2019 · 12 min read

Metalsmith is a plugin-based static site generator originally from Segment. It's a current favorite of mine because of how sites are built as a pipeline of plugins where the output of each plugin is the input of the next. This allows for strong control over what happens and when it happens.

Installing packages

Starting with an empty project, install some Metalsmith plugins:

npm install --save metalsmith metalsmith-layouts jstransformer-handlebars metalsmith-markdown metalsmith-sass metalsmith-autoprefixer metalsmith-include-files
  • metalsmith, metalsmith-layouts, jstransformer-handlebars, metalsmith-markdown as a base for our source parsing and templating.
  • metalsmith-sass to compile the Bootstrap Sass which lets us customize its variables.
  • metalsmith-autoprefixer to add vendor prefixes to CSS rules. The Bootstrap build also does this .
  • metalsmith-include-files to include external JavaScript in our source files.

And then install jQuery and Bootstrap :

npm install --save jquery bootstrap

We'll ignore the Popper.js peer dependency as we'll use the bundled Bootstrap JavaScript which includes it.

Source file structure

Create the following directories and files for use in the build pipeline:

.
├── index.js
├── layouts
│   └── page.hbs
└── src
    ├── index.md
    └── static
        └── css
            └── bootstrap.scss
  • layouts/page.hbs will be our primary template for metalsmith-layouts for common elements like the navigation bar.
  • src/static/css/bootstrap.scss will be our primary Sass file that will optionally configure some variables before including Bootstrap.
  • src/index.md will be our home page to processed through metalsmith-markdown.
  • index.js will be the Metalsmith build file.

Writing the source files

Now that we have all those plugins installed and files created let's start filling them in.

Metalsmith build file

Set up your index.js file like this:

const Metalsmith   = require('metalsmith');
const sass         = require('metalsmith-sass');
const autoprefixer = require('metalsmith-autoprefixer');
const include      = require('metalsmith-include-files');
const markdown     = require('metalsmith-markdown');
const layouts      = require('metalsmith-layouts');

Metalsmith(__dirname)
    .source('./src')          // source directory for the pipeline
    .use(sass())              // compile Sass
    .use(autoprefixer())      // add vendor prefixes to CSS rules
    .use(include({            // include external JavaScript
        'static/js': [
            './node_modules/jquery/dist/jquery.slim.min.js',
            './node_modules/bootstrap/dist/js/bootstrap.bundle.min.js'
        ]
    }))
    .use(markdown())          // convert Markdown to HTML
    .use(layouts({            // process all HTML files with Handlebars
        pattern: '**/*.html',
        default: 'page.hbs',
        engine: 'handlebars'
    }))
    .destination('./build')   // destination directory of the pipeline
    .clean(true)              // clean the destination directory before build
    .build(function (err) {   // execute the build
        if (err) {
            throw err;
        }
    });

This will:

  • Compile all *.scss files with metalsmith-sass.
  • Add vendor prefixes to CSS rules.
  • Add the pre-built Bootstrap JavaScript and its dependencies as input files.
  • Build src/index.md into build/index.html using layouts/page.hbs as the template.

Note the pattern configuration for metalsmith-layouts, without it our CSS and JavaScript files will also use the layout/page.hbs template.

HTML template

The Bootstrap team does a great job of providing many layout examples , so to keep this simple we'll use the "Starter Template ."

Fill in your layouts/page.hbs like this:

<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>{{ title }}</title>
    <link href="static/css/bootstrap.css" rel="stylesheet">
</head>
<body cz-shortcut-listen="true">
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
            aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarsExampleDefault">
        <ul class="navbar-nav mr-auto">
            <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="#">Link</a>
            </li>
            <li class="nav-item">
                <a class="nav-link disabled" href="#">Disabled</a>
            </li>
            <li class="nav-item dropdown">
                <a class="nav-link dropdown-toggle" href="http://example.com" id="dropdown01" data-toggle="dropdown"
                   aria-haspopup="true" aria-expanded="false">Dropdown</a>
                <div class="dropdown-menu" aria-labelledby="dropdown01">
                    <a class="dropdown-item" href="#">Action</a>
                    <a class="dropdown-item" href="#">Another action</a>
                    <a class="dropdown-item" href="#">Something else here</a>
                </div>
            </li>
        </ul>
        <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
        </form>
    </div>
</nav>
<main role="main" class="container">
{{{ contents }}}
</main>
<script src="static/js/jquery.slim.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Things to note are:

  • The local <link> and <script> sources, they will end up in our build/ directory thanks to metalsmith-sass and metalsmith-include-files.
  • The {{ title }} placeholder to be replaced with the frontmatter from any src/*.md files.
  • The {{{ contents }}} placeholder to be replaced with the contents of any src/*.md files.

Index page

Continuing to use the example content from the "Starter Template " example, fill src/index.md in with:

---

title: Starter Template for Bootstrap

---

# Bootstrap starter template

Use this document as a way to quickly start any new project.

All you get is this text and a mostly barebones HTML document.

Primary Sass file

To start, fill out src/static/css/bootstrap.scss with only:

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

That will get the stock Bootstrap building.

But the value of using metalsmith-sass to compile the Bootstrap Sass instead of using the pre-built CSS is we can customize various variables. If we wanted to change the primary color from blue to something else all we have to do is write:

$primary: darkorange;

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

Refer to the official documentation on available variables and how they work.

From here we can also add some custom CSS after all the Bootstrap rules:

$primary: darkorange;

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

body {
    background: darkgray;
}

Conclusion

With using just 2 Metalsmith plugins we're able to customize and build the Bootstrap CSS from source and include the pre-built Bootstrap JavaScript in our input files.