Building a hugo site and theme with Bootstrap

hugo is blazing fast

Now that’s its 2018 its time to retool the blog using hugo. Why not? Hugo is built in golang and is blazing fast and everything is cleaner than it was in the middleman years.

One of the reasons that I liked middleman – it’s usage of the rails’ Sprockets library – is no longer a strength. The javascript and front-end world has moved over to WebPack and I’ve personally moved over to create-react-app. We’ll get into that later. First go and download hugo from the main site. And then we’ll create a blank site and theme.

$ hugo new site mysite
$ cd mysite
$ hugo new theme mytheme

Edit the generated config.toml to have it point to this new theme.

theme="mytheme"relativeURLs=true

I like to setup all the content sites so the urls are relative so I can host in subdirectories and the like. (I’ll talk about this at a later post, but this makes it easy to stick things in ipfs and load up development builds of the site on an actual mobile device for testing.) Finally, lets start the server:

This is the _default base layout for everything in the site. You can have specific layouts for different content types, but this is the fallback one that we are going to define for when nothing else is specified. Inside of this we define a few partials and blocks. You can think of partials as included subtemplates that we can use both for structure as well as a convenient place to override the structure of a theme when you want to do big customizations. blocks are replaced with the data from the content of your site, so are better thought of as the parts where the template is filled with data.

The {{ }} stuff is the go templating language, so if you want to really figure out how that works you’ll eventually find yourself in the golang language docs, which is a little intense sometimes. The main thing to remember is that for every page that is rendered on the site its passed in a HugoPage object which is where you need to pull out all of the data. So if you want to know what is available to you, that’s where you start looking.

Head

Inside themes/mytheme/layouts/partials/head.html add bootstrap and the various bits of awesomeness required for that.

This sets up bootstrap to load from the CDN, as well as adding a link to the generated rss feed. Here we can see a few other things. One is that we are setting the <title> using {{ .Title }}. We may want to change that later, but what that means is that we are using the current page’s title attribute to set the title, and if you wanted to include something from the .Site data this is where you’d put the logic.

The other thing here is the range function. In practical terms we are putting in rss discovery links, but what this literally is doing is looping over the AlternativeOutputFormats slice inside of the page. range is the golang looping idiom, which is like a for loop but better.

Footers

Lets put some of these variables into the footer to help debug a bit. This is in themes/mytheme/layouts/partials/footer.html where we are going to expose the hugo page variables so we know what’s happening

Page templates

Now we will define a basic single template, which will be used to render a single object, not a collection of anything. We are going to demo this with the index page initially.
themes/mytheme/layouts/_default/single.html :

{{define"main"}}<divclass="container"><p>This from the single page template</p></div>{{end}}

And out first draft of the index.html template for the site. We’ll put in a little jumbotron in themes/mytheme/layouts/index.html:

{{define"main"}}<divclass="jumbotron"><divclass="container"><h1class="display-4">This could be the title</h1><pclass="lead">Here's some description stuff</p><p>There's also other things that are super nice</p></div></div>{{end}}

At this point you should be able to see the index page when you go to http://localhost:1313 (If hugo didn’t create an empty header.html partial the build might fail, so move on the next section if you don’t have that.)

Header and menus

In config.toml on the top level of your site (out of the theme) lets define a few menu items. We will then use that data to fill in the header.

There are a couple of new concepts here, the first is {{ "/" | relURL }} which is a helper function to transform a variable in somewhat that could make it more palatable to the user. In this case I’m just translating “/” to a relativeURL, which on subpages will have it be something like “../../index.html” or whatever.

{{ $currentPage := . }} is setting a variable so let us refer to the outcontext when we are in the range below. The range links over all of the menu items. The object that . refers to changes inside of the range to be the current item that it’s iterating over, so in order to make any comparisons to the currentPage in this case we need to give it a name. := is very golang.

Also are if statements, which we are using to determine if we include the active class on the link. Note where the operator is, that is the or comes first and the data comes afterwards. So lispy!

Adding post content

Lets now add some basic posts.

$ hugo new posts/sample_post.md

If you didn’t start hugo with -D, you’ll need to make sure that the draft flag isn’t true for it to show up

---
title: "Sample Post"
date: 2018-10-19T16:04:51-04:00
draft: false
---
One of the things I'm very interested in is writing words and seeing them on the page.

This is pretty standard from most static site generators with the front matter on the top and the markdown on the bottom.

We’re using a hugo function humanize to capitalize the type of object that we are looking at, and a whole bunch of bootstrap utility classes to align and only show the published date on larger screens.

The .Date.Format format string is really weird – I’m not sure that I really understand it but it expects the year to be 2006 for things to make sense. This is part of the way that the hugo’s underlying go date formating stuff works that.

Now we click on posts in the nav bar you should see the list of posts. But when we click on the page itself, you’ll notice that we have the text “This from the single page template”. So lets update that themes/mytheme/layouts/_default/single.html template now:

New lets add tags to our menu so we can put it on the nav bar. We’ll also specify menu weights to fix the order. This is done in the site configuration config.toml where we are configuring how this specific site used the theme that we are defining.

We are checking to see if the menu item is tags, and if so look through the tags taxonomy to display the dropdown with all of the items. We need to go through some hoops to figure out exactly how to get the name and the url, but things are looking good now!

Tag list page

If we look at the http://localhost:1313/tags we see that its showing the tags by date. Which doesn’t make sense. So lets create a list page for the tags. There are two types of things – one is the list of tags, and one is the list of posts that for a specific tag. themes/mytheme/layouts/tag/list.html:

We first look to see if we are on a the term page, and if so we get a list of tags based upon the count. He were list the number of posts per tag. If we are on the tag page itself, we use the same logic at the posts/list.html page to show the articles for that tag by date.

Adding code highlight

Hugo comes with pygments built in, so we are going to use that with a custom theme. Lets first generate the css and put it in our themes static folder.

Here we are adding some metadata to the top of the page, showing the subtitle if it’s set and putting in the published date and some tags. We are tweaking the layout a bit using the bootstrap utility classes to add some more spacing. Then the body content which we wrap it inside of an article.

We are showing a few other features that hugo gives us. One is the Next and Prev posts in the section, which is by date and content type. Another is the related pages to this particular page.

Adding some style tweaks to head.html

Here’s some small tweaks to the css to make things a little bit more readable on larger screens. We’ll get into really expanding on bootstrap later.

This uses the $.Scratch object to help keep track of the last year that was shown. When we see a year for the first time, we show a small paragraph with muted text with the year to help split things out.

Another content type: Photos

Lets add something different – a photo album. We’ll look at how different content types work, and some image processing options. First thing we need to do is to grab some photos and create a few albums. I’ll let you sort that out. But create a couple folders under content/photos and then an index.md file in each of them. Here’s what the directory structure should look like:

Now if you go to http://localhost:1313 you should see the normal list of pages that is generated with the default list template. Let’s now create a list template for our new photos type in themes/mytheme/layouts/posts/list.html:

We are iterating through all of the pages, and then looking for the first image page resource. Page resources are children of the page, and we are getting the 0 index of it. We then use hugo smart image cropping features to resize that image so it fits into the card. When you generate the site you will see a new resources folder that is part of the root, this is done automatically for you.

Now lets add the page for the gallery itself, in themes/mytheme/layouts/photos/single.html:

This resizes the images to fit with in an 800x600 px box and then throws them on a grid. This is not a great looking gallery but shows a little bit of hugos image processing abilities as well how you can use different layouts for different page types.

Don’t forget to edit the config.toml if you want to add a link to photos in the header!

Cleaning up index page

Lets cleanup the homepage. We are going to add recent posts and start filling in the jumbotron based upon site content. In the main content part of the site, we are going to create content/_index.md This file will generate the .Content that is displayed as well as showing an example of how to pass variables from the front matter into the template itself.

---
title: "This is a title"
recentposts: 10
---
Here is a bunch of amazing stuff about what it is that I'd like to see. It's super amazing.

Now for the template in themes/mytheme/layouts/index.html. We first figure out if the recentposts parameter is set, and if not we default to 5. We are also going to only show pages that are of type posts. If you want to show all recent entries on your site, replace (where .Pages "Type" "posts") with simply .Pages:

Bootstrap advanced theming

Bootstrap is much more interesting when you start using the SASS version of it and are able to tweak the generated code. Let’s look at how we’d get this into our pipeline. This requires that you have npm or yarn installed.

And now your site should look slightly different! I’m not going to go into all of the things that can be tweaked but its really worth looking into what you can do with bootstrap to make it look like you want.

Overriding a theme file on the local site

When you think about how you want to break things into partials you should consider how the end site might want to override things. One way is to pass in variables inside of the main site config.toml that can switch features on and off, but another way is to override the partials themselves.

The bootstrap header is such a recognizable thing that people might want something all together different, so lets see how we can change that. Create layouts/partials/header.html in the top level site directory:

Read next

See also

This article walks through the motivations driving and benefits of using a the Seed Architecture for building performant websites using Middleman, React, and a seperate API server such as Parse. The benefits are:
You get full SEO with a heavy client JavaScript site without having to do crazy things with PhantomJS or meta fragments. Hosting and operations become both cheap and doesn’t require a support team. Scaling out the system is mainly a bandwidth problem, and secondarily a API scaling problem for a subset of behavior.

I’ve been using Rake more and more for data collection and processing tasks. Rake is pretty pretty powerful. Most people know it as way to add external tasks to a Rails app, but it’s actually very powerful build system. We’re going to take advantage of that to build out a framework that will make it easy to collect, process, and interpret data while keeping it all in sync.
In fact, if you just want to start playing with stuff now, head over to the rake-data site to go through some walk throughs.

As part of the process of getting this site to work, I learned some more things about how to better build a site with middleman. Building off of our foundational article here are a few other things that I found very useful when using middleman to build a static site with a bunch of dynamically generated content.
Partials The index.html.haml, articles.html.haml, tag.html.haml and calendar.html.haml pages all use the same partial to list out the post archives, which are mostly the same.