Customizing Bootstrap 4 with Hugo pipes

Hugo Pipes with Bootstrap
Image: Hugo Pipes with Bootstrap (License: CC-BY-SA-NC-ND Marcelo Canina)
Published:
Last modified:

Overview

We will use npm to handle Bootstrap package, it will make it easy to keep up to date with their releases.

Prerequisites

To make Hugo compile SASS files you need the extended version. As of Hugo version 0.43, Hugo releases two binary types, the extended version makes it possible to edit SCSS files.

If you try to compile SASS files with the regular version you will get the following error message:

$ hugo
Building sites … ERROR 2018/08/06 20:32:24 error: failed to transform resource: TOCSS: failed to transform "sass/main.scss" (text/x-sc
ss): this feature is not available in your current Hugo version

Install dependencies

Create a package.json file with your npm dependencies running npm init .:

$ npm init . 

Install projects with npm install bootstrap jquery popper.js postcss-cli autoprefixer --save:


$ npm install bootstrap --save
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN bootstrap@4.1.3 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself.
npm WARN bootstrap@4.1.3 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself.

+ bootstrap@4.1.3
added 1 package in 2.58s
$ npm install jquery --save
npm WARN bootstrap@4.1.3 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself.

+ jquery@3.3.1
added 1 package in 1.266s
$ npm install popper.js --save
+ popper.js@1.14.4
added 1 package in 14.954s

$ npm install postcss-cli autoprefixer --save
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ postcss-cli@6.0.0
+ autoprefixer@9.1.0
added 255 packages in 20.39s

Ok, no more warning ;)

Now we have all Bootstrap SASS files at node_modules/bootstrap/scss/.

Theory

SASS files

We need to create a resource from an asset SASS file so we can load it and use Hugo pipes.

Page Resources are all kind of documents that can be associated with a page, i.e.:images, other pages or documents. They have page-relative URLs and their own metadata.

Basic Sass processing in Hugo

Sass files in Hugo should be located under the /assets folder.

It can go into the theme’s folder, or project’s root folder, (the latter one enables you to customize an existing theme too).

Create the directory assets/sass mkdir -p assets/sass

Then the assets/sass/main.scss file will contain our customized variables.

Put some example content there to try to load it in Hugo.

For example, in /assets/sass/main.scss:

$not-so-white: #ffe;
body {
	background-color: $not-so-white;
}

Process Sass file from templates

In the head.html partial, we will load the above SASS file with resources.Get.

The most basic way to load the Sass file without any further processing is:

{{ $style := resources.Get "sass/main.scss" | toCSS }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">

Go and try to build your site checking that there is a main.css file with the processed content:


$ hugo
$ cat public/sass/main.css 
body {
  background-color: #ffe; }

Possible transformations

You can also use the following transformation options in your pipes:

  • resources.PostCSS or postCSS: Process your CSS with PostCSS. Config file support (project or theme or passed as an option).
  • resources.Minify or minify: Currently supports css, js, json, html, svg, xml.
  • resources.Fingerprint or fingerprint: Creates a fingerprinted version of the given Resource with Subresource Integrity.
  • resources.Concat or concat: Concatenates a list of Resource objects.
  • resources.ToCSS or toCSS: Compile SCSS or SASS into CSS.
    • also accepts SASS directives in a dict.

For example, putting all of the above together:

{{ $options := (dict "targetPath" "css/style.css" "outputStyle" "compressed" "enableSourceMap" true "includePaths" (slice "themes/simpleit/node_modules")) }}
{{ $style := resources.Get "sass/main.scss" | toCSS $options | postCSS | minify | fingerprint }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}" >

$ hugo
$ cat public/css/style.css 
body{background-color:#ffe}

/*# sourceMappingURL=style.css.map */

Look the above path, our CSS file is now located at css/style.css as set in the targetPath options dictionary key.

Customizing Styles

Having understood how basic SASS processing in Hugo works, now we focus on customizing Bootstrap.

To customize website styles I want to achieve two goals:

  • customize the variables that Bootstrap uses in their own SASS files
  • use some of those SASS variables in my website SASS files

To achieve this we will create three files:

  1. assets/sass/main.scss: the main SASS file that import other SASS files in the right order.
  2. assets/sass/custom_variables.scss: the customized Bootstrap variables.
  3. assets/sass/styles.scss: main website styles with all Bootstrap variables available (customized and non-customized).

Custom Bootstrap variables

In assets/sass/main.scss we load the custom variables, then the rest of the Bootstrap styles and finally we load our custom website styles:

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

So to customize Bootstrap we need to copy any variable from node_modules/bootstrap/scss/_variables.scss into our own assets/sass/custom_variables.scss.

If we are importing full Bootstrap SCSS after customizing its variables, why do they won’t get replaced? This is because the !default directive.

Every Sass variable in Bootstrap 4 includes the !default flag allowing you to override the variable’s default value in your own Sass without modifying Bootstrap’s source code. Copy and paste variables as needed, modify their values, and remove the !default flag. If a variable has already been assigned, then it won’t be re-assigned by the default values in Bootstrap.

Example

Let’s make Bootstrap use some stylish serif fonts instead of sans-serif.

In assets/sass/custom_variables.scss create the $font-family-serif variable and replace Bootstrap’s $font-family-base:

$font-family-serif: Lucida Bright, Lucida Fax, Palatino, "Palatino Linotype", Palladio, "URW Palladio", serif;
$font-family-base:            $font-family-serif !default;

Use Bootstrap variables in our styles

All the Bootstrap variables are available in our assets/sass/styles.scss file.

Example

body {
    font-size: $font-size-lg * 2;
}

$ cat public/css/styles.css
/*!
 * Bootstrap v4.1.3 (https://getbootstrap.com/)
 * Copyright 2011-2018 The Bootstrap Authors
 * Copyright 2011-2018 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */:root{--blue: #007bff;
 
 ...
 
body{font-size:3.08rem}

/*# sourceMappingURL=style.css.map */

Add javascript

Lastly, add required Javascript files:

Copy the required javascript files to assets/js/vendor. Here is a simple Make recipe:

ASSETS_DIR := assets/js/vendor/
build-js:
	mkdir -p $(ASSETS_DIR)
	cp node_modules/jquery/dist/jquery.min.js $(ASSETS_DIR)
	cp node_modules/popper.js/dist/umd/popper.min.js $(ASSETS_DIR)
	cp node_modules/bootstrap/dist/js/bootstrap.min.js $(ASSETS_DIR)

Run make build-js to copy them to the assets location.

Now include them in your template before the closing </body> HTML tag:

{{ $jquery := resources.Get "js/vendor/jquery.min.js" }}
{{ $popperjs := resources.Get "js/vendor/popper.min.js" }}
{{ $bootstrap := resources.Get "js/vendor/bootstrap.min.js" }}
{{ $js := slice $jquery $popperjs $bootstrap | resources.Concat "js/bundle.js" | resources.Minify }}

<script src="{{$js.RelPermalink}}"></script>

gitignore

Keep your repo clean, add to .gitignore:

vendor/
resources/
/node_modules

Final tree structure:

.
β”œβ”€β”€ node_modules/
β”‚   β”œβ”€β”€ bootstrap
β”‚   β”œβ”€β”€ jquery
β”‚   └── popper.js
└─  assets/
	β”œβ”€β”€ js
	β”‚Β Β  └── vendor
	β”‚Β Β      β”œβ”€β”€ bootstrap.min.js
	β”‚Β Β      β”œβ”€β”€ jquery.min.js
	β”‚Β Β      └── popper.min.js
	└── sass
		β”œβ”€β”€ custom_variables.scss
		β”œβ”€β”€ main.scss
		└── styles.scss

Then our styles file style.css would be minified and looking something like: public/style/style.min.d58f8b04f38cc0130246ca80fac07c79f453344c800db4f815543d84a8bc4bf2.css at the final site.

Repos

There is a Github repo available that shows a hugo site with all these steps applied at: https://github.com/marcanuy/hugo-pipes-bootstrap

This approach it is also used by Hugo’s theme SimpleIT Hugo Theme (it will look familiar, this website is built with it ;)

References

Uruguay
Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus


How to add Bootstrap 4 to Hugo so you can customize its SASS variables, i.e.: theming.

Clutter-free software concepts.
Translations English EspaΓ±ol

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme

·