I like the concept of blog post series — a large topic or project, broken up in smaller pieces. Easier to write, easier to read.

Here is how I implemented a series taxonomy in Hugo 👇

I’ve later improved this series implementation, documented in this blog post.

Configuration

First we need to define the taxonomy in the configuration:

[taxonomies]
  tag = "tags"
  series = "series"

Now we can use the series front matter parameter, and we have a /series/ page, like mine.

Taxonomies must be arrays in the post front matter. This solution only supports a single series, but it must still be defined as an array.

series = ["ZFS SSD pool"]

Supporting multiple series is not something I’ve needed (yet).

Layout

Next we need to add some code to the default layout for a post: /layouts/_default/single.html.

Before the content

Show a notification that the post is part of a series, use the first index in the term array series. Links to the series index list.

{{- if .Params.series -}}
  {{- with index (.GetTerms "series") 0 -}}
    <div class="notice--info">
      <p>This post is part of the <a href="{{ .Permalink }}" style="font-weight: bold">{{ .LinkTitle }}</a> series.</p>
    </div>
  {{- end -}}
{{- end -}}
Notification before the post content

I’m using a simple notice class:

@mixin notice($notice-color)
{
    margin: 1em 0;
    padding: 0.5em 1em;
    border-left: 0.5em solid $notice-color;

    > *:first-child {
        margin-top: 0;
    }

    > *:last-child {
        margin-bottom: 0;
    }
}

.notice--info {
    @include notice(#8ebeeb);
}

Bottom of the page

Show all posts in the series — in chronological order, with links. Highlight the current post in bold.

I think this gives readers a nice overview, and it updates when new posts are added to the series. No manual editing required 👍

{{- if .Params.series -}}
  <div class="post-series">
    {{- $series := where .Site.RegularPages.ByDate ".Params.series" "intersect" .Params.series -}}
    {{- with $series -}}
    <h3 id="series">This series</h3>
    <ol>
        {{- range . -}}
        <li>
            {{- if eq .File.UniqueID $.File.UniqueID -}}
                <b>{{ .Title }}</b>
            {{- else -}}
                <a href="{{ .Permalink }}">{{ .Title }}</a>
            {{- end -}}
        </li>
        {{- end -}}
    </ol>
    {{- end -}}
  </div>
{{- end -}}
List of posts in series, bottom of page

Done!

And that’s it, simple and effective 🙂

Last commit 2024-04-05, with message: Tag cleanup.