New Feature: Post Labels

Now you might not see this too much since I plan to use the feature sparingly, categories and tags are both capable of sorting everything to a satisfactory level, but I’m using this thing that I just made to add extra little labels, which have a few cool uses.

Now what I mean with a “post label” is just a small little indicator that should hopefully draw your attention for just a second, to call something out like the last time a piece of content was updated (like with guides), or that there’s going to be multiple different posts that all logically tie together. Something small, simple, and easy enough to probably color-code, once you’ve seen a few, just seeing that blob of color tells you the meaning without even having to read it first.

Implementation Troubles

So really, I’d have love to shortcode this, and just throw something like {{< multipart "1 of 2" >}} in at the top, but that places anything I’d add under the TOC for most posts, if one was generated. And generally if posts have a TOC, like the few ones that I’d be using this on: My IPv6 rant, or my homelab overview, they’re pretty long, meaning you’d have a lot of scrolling just to see something that, ideally, I’d have showing right in the preview. And as much as some hack like setting toc: false in the front-matter, and then slotting one in myself with the {{< toc >}} shortcode could solve that issue, I just wasn’t feeling it. The rest of this theme so far is pretty well templatized, and everything similar to this is already controlled with their own front-matter settings, why can’t I do that with this? Turns out, I can, but my current implementation isn’t that extensible as it stands.

Theme Modifications

Layout

For reference, the Bilberry Hugo theme, and by extension, my fork, works on a rather nested mechanism for templates. The starting point, baseof.html, is where Hugo starts, which can then add other “partials” like article-wrapper.html or default-content.html. And yes, partials can include partials. The one that I’m concerned with right now is the partial that will contain the actual post content, which is in layouts/partials/default-content.html. Here’s the modified section:

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    {{ if .ctx.Params.showComments | default true }}
        <span class="disqus-comment-count" data-disqus-url="{{ .ctx.Permalink }}">Unable to load comment count</span>
    {{ end }}
</div>

{{ if .ctx.Params.chips }}
<div class="chips">
    {{ with .ctx.Params.chips.part }}
    <span class="chip">
    <span class="chip-left">Part</span>
    <span class="chip-right">{{ . }}</span>
    </span>
    {{ end }}
    {{ with .ctx.Params.chips.lastUpdate }}
    <span class="chip">
    <span class="chip-left">Updated</span>
    <span class="chip-right">{{ . }}</span>
    </span>
    {{ end }}
    {{ if .ctx.Params.chips.needsRework }}
    <a href="/page/needs-rework">
    <span class="simple-chip" id="rework-chip">
    Needs rework
    </span>
    </a>
    {{ end }}
    {{ with .ctx.Params.chips.outdated }}
    <a href="{{ . }}">
    <span class="simple-chip" id="outdated-chip">Outdated</span>
    </a>
    {{ end }}
    {{ if .ctx.Params.chips.improved }}
    <span class="simple-chip" id="improved-chip">New and improved!</span>
    {{ end }}
</div>
<hr />
{{ end }}

{{ if or (eq .template_type "single") (.ctx.Params.noSummary) }}
    {{ if and (gt .ctx.WordCount .ctx.Site.Params.tocMinWordCount ) (.ctx.Params.toc) }}
        <h1>{{ i18n "tableOfContents" | default "tableOfContents" }}</h1>
        {{ .ctx.TableOfContents }}
    {{ end }}

Right above the TOC part, I include a few ifs and withs for each of the labels (obviously, internally referred to as “chips”, after their visual similarity to the similarly named Material Design element1). The ones that have extra data along with them have a with, and the ones that don’t have an if.2 The entire <div> that contains them is also wrapped in an if so that if I don’t even include the key in the front-matter, this entire thing is skipped.

Now because of where I inserted this, this means that even in the “summary” view, say, as you see posts on the homepage or a category / tag listing, they will still be present, and will also show up above everything no matter what view its in.

Styling

You’ll notice that I gave everything there either an id, a class, or both. This is 100% just for styling, there’s no JS here. I could do this with style attributes, and indeed, when testing that’s what I did, and in the first revision of this that you didn’t see, that is what I did, but this is in production now, and I’d rather not have duplicate values all over the place, especially since the CSS for this theme is generated from a few different SASS files, which contain reusable variables, for things like text color, and the base theme color (my favorite green which nobody has yet guessed the source for, fun fact). All these exist in assets/sass/_variables.scss for this theme. I edited _article.scss, adding in some new rules:

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
div.chips {
    display: block;

    span.simple-chip {
        border: 2px $base-color solid;
        border-radius: 50px;
        overflow: hidden;
        display: inline-flex;
        padding: 2px 5px 0 5px;
        font-family: $headline-font;
        font-weight: bold;
        font-style: normal;
        color: $page-background-color;
        background-color: $base-color;
    }

    span#rework-chip.simple-chip {
        border-color: red;
        background-color: red;
    }

    span#outdated-chip.simple-chip {
        border-color: orange;
        background-color: orange;
        color: $text-color;
    }

    span#improved-chip.simple-chip {
        border-color: #0ee;
        background-color: #0ee;
        color: $text-color;
    }

    span.chip {
        border: 2px $base-color solid;
        border-radius: 50px;
        overflow: hidden;
        display: inline-flex;
        font-style: normal;

        span.chip-left {
            background-color: $base-color;
            color: $page-background-color;
            overflow: hidden;
            padding: 2px 4px 0 5px;
            font-family: $headline-font;
            font-weight: bold;
        }

        span.chip-right {
            padding: 2px 5px 0 4px;
            font-family: $headline-font;
        }
    }
}

This entire file is wrapped in an article selector, meaning that none of these rules will take effect if found outside of a post’s markup. As for my styling, there’s two main classes: “simple” labels with no extra data, they either exist, or don’t, and labels that do have extra data, like the last updated one. Within the class I define some defaults, but then for each specific one’s id I can override them.

Note that none of the SASS here is actually live, it needs to be converted into the theme.css file, which is done by running npm run production from the theme’s root in this case.3 npm run dev runs a lot quicker, which I can use when iteratively improving this piece by piece.

Usage

As I think I’ve mentioned a few times, Hugo posts can have “front matter”, which is either a JSON, YAML, or TOML block at the beginning of the file with some metadata. This theme’s templates (“archetypes”) use YAML. Here’s this post’s front matter:

---
title: "New Feature: Post Labels"
date: 2021-02-13T21:38:35-05:00
draft: false

categories: ["Blog improvements"]
tags: ["CSS", "JavaScript", "SASS"]
toc: false
author: "Teknikal_Domain"
---

Everything between the two ---4 is the front matter block. There’s a few values that aren’t present by default, like showComments, or googleCharts, or littlefoot. And then there’s… chips. For example, the Hackintosh post looks like this now:

---
title: "Hackintosh Up and Ready!"
date: 2020-01-07T21:47:35-05:00
draft: false

categories: ["Guides", "Hackintosh", "Tech tales"]
tags: ["Lenovo", "MacOS", "ThinkPad T440"]
toc: true
author: "Teknikal_Domain"
chips:
    lastUpdate: Feb 4th 2020, 14:56 EST
---

This causes the little “last updated” label to appear with its value in there.5 For “simple” ones with no value, setting it to true will show them, plain and simple.

List of Labels

Part 1 of 5
Probably the least useful one of them all, but I’ll put this in there for multi-part posts, so as they’re coming out, you can see how many are going to be in that particular little series.
Updated Feb 4th 2020, 14:56 EST
If I’ve ever updated a post after its initial publication, I’ll put this on there to mark that, A) it’s been updated, and B) when it was updated.
This particular post just needs to be taken down and re-written (see also: IPv6). Afterwards, if I deem the original content isn’t relevant any longer and can be replaced, this will become an updated label, otherwise I’ll use…
Outdated
Sort of like the inverse of the improved label, this post is outdated and has a newer version up, but I decided that the original still had enough significance to keep it around. This one is also a link, clicking it will take you to the newer post automatically.6
New and improved!
If a post is a rewrite, revision, or just overall newer version of a previous one, this will point out that what you’re about to read you might have seen before but it should be better this time around.

A Collateral Bugfix

So here’s something funny: I accidentally found and then fixed a bug as I was going through this. What I noticed was that any messing with the TOC’s position, be it by manually adding a {{< toc >}} shortcode, or by putting markup above the TOC in the templates, caused it to be collapsed whenever you toggled the top navbar. This is because the theme uses <nav> for the topnav, and, originally, that was the only thing that it was used for, meaning the jQuery that handles the toggling just selects nav. Since generated TOCs are also contained in a <nav> element, that means that they get collapsed or expanded too. Well the simple fix is to change the jQuery to select nav#topnav instead, and give the topnav an id="topnav" so now it will select one element instead of all elements of a given type. Done!


  1. Note that chips in MD usually represent an input or action or some sort. The actual element that inspired this was GitLab’s issue labels, which is why I use the terms “label” and “chip” pretty interchangeably here. ↩︎

  2. The real difference is that an if just checks if it exists, or in this case, it set to true. A with will allow me to get the content of that variable with {{ . }} in that scope, but only if it actually exists, otherwise it’s skipped entirely. ↩︎

  3. That npm invocation will generate a lot of files, the two main ones being theme.css and theme.js, but also all the fonts, icons, and other static resources that it uses. Really, it rebuilds the entire theme and minifies it down into those static resources. ↩︎

  4. --- is the YAML separator, expect to see +++ for TOML, just a plain { and } for JSON, and, I didn’t realize until writing this footnote, a group of #+KEY: VALUE lines for org-mode front matter. ↩︎

  5. If you’re kinda used to YAML but not completely familiar with it, you’re probably thinking I’m missing a dash before lastUpdate, but you’d be incorrect. In YAML, prefixing your indented lines with - makes the outer item a normal array, but not using that - on each item makes it a key/value dictionary. ↩︎

  6. As you might have seen from the layout markup, this is the only one that uses the “simple” class and yet also has an actual data value: the path of the new post to link to. ↩︎