Go Hugo

Hugo Tips & Tricks

Hugo is a lightning-fast static site generator written in Go.  With every release, it becomes more flexible and maintains its strong core base of users.  Hugo has excellent support forums and are friendlier than most.  I was originally going to switch over to using Hugo exclusively for this site but honestly, I find it more convenient to use WordPress or Ghost for blogs that you edit on-the-go.  At any rate, it is my opinion that Hugo is the best static site generator, and considerably faster than Jekyll.

The evolution of the web has taken a full speed shift into “reverse” in 2017.  All these years of over-engineering have resulted in people realizing Static HTML and straight Javascript are most often faster.  Where a finely crafted novel is only a couple of hundred k-bytes of text, the web has grown to become a bloated ad delivery system, where the average page is close to 2 mega-bytes.  Thankfully, many small groups of smart people are retracing the steps that the web has taken in an effort to deliver a better web.  Hugo is definitely part of that movement, and doing a great job.

This article was written in May, 2017, and I used v0.19 of Hugo at that time.  The current release is v0.31 and has many new features.

Installation and Environment

I chose NPM as my installation method, and I’ve found node very handy in building a development environment that integrates Hugo. In particular, the “scripts” section within package.json aids in building commands to launch file watchers and even hugo itself. Here are just a couple of examples:

SCSS Watcher

“build-css”: “./node_modules/.bin/node-sass –include-path scss themes/spenpress/static-src/scss/style.scss themes/spenpress/static/css/style.css”
“watch-css”: “./node_modules/.bin/nodemon -e scss -x \”npm run build-css\””

In my Hugo theme, I’m using SCSS. When I’m working on styles, I “npm run watch-css” and leave it running in the background. This generates updated CSS in my static folder anytime I make a change to the CSS.  A huge time saver.

HUGO SERVER

“hugo:server”: “hugo server –watch –verbose –bind=192.168.1.1 –baseUrl=http://192.168.1.1 -D -F”

Executing “npm run hugo:server” and leaving it running gives me the development hugo server, and pages automatically refresh in my browser whenever I make a change to a template or peice of content within Hugo.

 

Hugo Templating

Hugo is written in Go, and thus uses Go templating.  I’m not the biggest fan or advocate of Go templating but it is what it is.  If you want to use Hugo, you’ll need to learn it.  You will be glad you did.

Scratch (Frrr..Frrr…wiki wiki…Frressssh)

The first thing I learned was the value of the $.Scratch.  It is a more recent feature of Hugo, added in version lucky 0.13.0.  To be honest, if this feature didn’t exist, I would not be using Hugo.  It is indispensable.  It is described as a “scratchpad” where you can set and retrieve key value pairs, and retrieve arrays of mapKey sorted values.  It is in essense a global memcache.

By default user defined $variables are not global.  If I define $var := .Params.Image within a range loop, this $var is inaccessible outside of the loop.  However, if I define $.Scratch.Add “Image” .Params.Image, I can retrieve that definition in another template aka a “partial” using $.Scratch.Get “Image” or redefine it using $image := $.Scratch.Get “Image”.

In SpenPress, the first action of my main index template (used by the home page) is to store the bulk of a chosen article in $.Scratch.  This can then be passed to my head.html partial which determines which scripts to include based on various data. Additionally, this data is passed to most of my partials.

For example, in a piece of content, I have the following front matter:

hasCode = “true”
hasPHP = “false”
hasBASH = “true”

in my index.html, inside a range loop, I add a couple of key/value pairs to Scratch:

{{ $.Scratch.Add “HasCode” .Params.hasCode }}
{{ $.Scratch.Add “HasPHP” .Params.hasPHP }}
{{ $.Scratch.Add “HasBASH” .Params.hasBASH }}

Later in my index.html, I include my “head.html” partial”

{{ partial “head.html” }}

This site uses Prism.js for code syntax highlighting. Just for the sake of experimentation, I created a crude method to ensure I only load the pieces I need. So, if a page “HasBASH”, a script tag which loads Prism’s BASH component JS will be included.

{{ $hascode := $.Scratch.Get “HasCode” }}
{{ $hasbash := $.Scratch.Get “HasBASH” }}
{{ $hasphp := $.Scratch.Get “HasPHP” }}
{{ if eq “true” $hascode }}
{{ if eq “true” $hasbash }}

<script type='text/javascript' src='{{.SiteBaseURL}}/js/prismjs/components/prism-bash.min.js' />

{{ end }}
{{ if eq “true” $hasphp }}

<script type='text/javascript' src='{{.SiteBaseURL}}/js/prismjs/components/prism-php.min.js' /&gt

{{ end }}
{{ end }}

This ensures that a script is only loaded if it is actually needed, however, the goal here is simply to show you basic usage of $.Scratch. Also, make sure remember that “true” and true are not the same. If you use var = “true” in your front matter, you must keep it consistent. I only used double quotes out of habit, resulting in having to use them in my conditional evaluations later.

 

Passing Front Matter to Javascript

This site’s init.js gets some of its definition from the front matter.  Passing Global definitions and page .Params to javascript is quite simple, as shown below.

<script type=”text/javascript”>
var KSURL = ‘{{ .Site.BaseURL }}/’;
var navSection = ‘{{ lower .Title }}’;
</script>
<script type=’text/javascript’ src='{{ .Site.BaseURL }}/js/init.js’></script>

In short, the variables are defined and then used by init.js. KSURL configures absolute path for loading local resources and navSection is used to determine which top-level site navigation element appears as ‘active’.

Range

1. Determining Which “Article” To Load

This site’s home page template (index.html) is configured to load “the most recent ‘non-draft’ page within the ‘articles’ section.”   This required a “double where”, resulting in Hugo building an array of “articles”, then building [from that array] an array containing only the articles which have front-matter of draft = false, then range only considers the “first 1”, which is the most recent published article. (I use “articles” instead of “posts” out of personal preference.)

{{ range first 1 (where (where .Site.Pages “Section” “articles”) “Draft” false) }}
… $.Scratch.Add a bunch of data related to this article …
{{ end }}

2. Pagination

My theme shares a single default list template, which resides in layout/_default/list.html.  It is configured to display links to published articles (“non-draft”, e.g. draft = false) in groups of 10, newest-to-oldest.  This is the default template for tags/ and groups/ index pages.  In building this, I already knew I needed the same “double where” clause but what took me a minute was determining where to put my “10”.  After some trial and error, and re-reading the docs on this more carefully, it finally became clear.  Here is the entire range loop and associated previous/next pagination linkage.

{{ range (.Paginate (where (where .Data.Pages “Section” “articles”) “Draft” false) 10 ).Pages }}
<div class=”summary_list”>
<h4><a href=”{{ .RelPermalink }}”>{{ .Title }}</a></h4>
<div class=”bold”><i>{{ .Date.Format “January 2, 2006″ }}</i></div>
<div>{{ .Description }}</div>
</div>
{{ end }}
{{ if .Paginator.HasPrev }}
<span class=”link”><a href=”{{ .Paginator.Prev.URL }}”>
< Previous Page
</a></span>
{{ end }}
{{ if .Paginator.HasNext }}
{{ if .Paginator.HasPrev }}{{ end }}
<span class=”link”><a href=”{{ .Paginator.Next.URL }}”>
Next Page >
</a></span>
{{ end }}

3. Tag Cloud

A hot item for any blog is the infamous Tag Cloud.  I came up with a very basic tag cloud implementation which involved a couple of styles and a range loop which references .Site.Taxonomies.tags.  In each content/articles/ md file, I have a piece of front matter similar to:

tags = [“how-to”, “hugo”, “web”]

To build my tag cloud, I made a couple of styles which simply determine font-size.  The intention is similar to what is often seen in WordPress sites, where more commonly used tags are displayed in a larger font-size.  I was happy to find that the range loop defines .Count, which was the key ingredient I was hoping to find.  Long story short, I loop through each tag, and use the count to define which class to use (storing the result in $.Scratch.)  At the time of developing the tag cloud partial, I only had four articles.  As total count grows, occasionally come back and expand the conditions of the loop to apply additional classes, as tag counts increase.  For this example, assume any tag is used either once or twice.  Here is the result:

<div class=”tag_cloud”>
<h3>Tags</h3>
{{ range $name, $taxonomy := .Site.Taxonomies.tags }}
{{ $count := .Count }}
{{ if le $count 1 }}
{{ $.Scratch.Set “tagclass” “tag_one” }}
{{ else }}
{{ if ge $count 2 }}
{{ $.Scratch.Set “tagclass” “tag_two” }}
{{ end }}
{{ end }}

<a href=”/tags/{{ $name }}”>{{ $name }}</a>
</span>
{{ end }}
</div>

As the number of articles and subsequent tag .Count increases, you can check to see the count for each tag, in order to determine your tag usage “min/max” and how many conditions/classes you’d like to add.  Another option could be to have a piece of content that is a draft which displays this information, viewable by running hugo server with -D.  The -D option is also helpful for having an entire site section that is viewable in development but never rendered to public/ when you are ready to deploy a new version of your site.

 

Go Hugo!

Hugo – Main Site

Hugo @ GitHub

Linux Swap Basics

Print or Share:PrintemailFacebookTwitterLinkedinStumbleUponRedditI was recently asked about the Linux swap system, and promised a recap here on the blog.  The inquiry was regarding a newly installed Ubuntu on a dual-booting PC, which, by default, created swap space on the primary SSD.  The user realized this was a generally “bad” idea, and asked me how to […]

Postfix Auto-Reply Using Vacation

Postfix Auto-Reply Using Vacation

Print or Share:PrintemailFacebookTwitterLinkedinStumbleUponRedditPostfix Auto-Reply Using Vacation If you are looking for a simple way to setup Postfix Auto-Reply using vacation on any Linux, BSD or OSX system, look no further!  This article assumes you have a working Postfix mail server up and running, as well as a basic understanding of the command line. Vacation was […]

CodeSpin

Automating Remote Backups With Linux

Print or Share:PrintemailFacebookTwitterLinkedinStumbleUponRedditAutomating Remote Backups With Linux This brief article outlines the procedure for automating remote backups with Linux using SSH and rsync, including some basic security measures to consider when setting up a new server.  This is written for both the new admin and the seasoned professional whom may be rusty and needs a […]