Hugo, Shortcodes, & XKCD refs

xkcd style popup references in Hugo

4 minute read

Way back when I started with hugo, a few days ago, I said in this post that I really liked the xkcd footnote/reference popup/popover things[1]Like this! he uses in his “what if?” posts.

I decided to try to add them to Hugo, first just as part of a theme, then make it easier to use them by making a shortcode for them. After many travails*mostly due to either span/div hover issues with iOS, or history issues with an approach using anchor refs with a pure css approach, I realized that jQuery was already loaded in the theme I was using anyway, and that using the same approach they used would be simple and easy, and work on mobile as well as desktop (one of the issues I had with some pure CSS approaches)

After getting the span based html/css/js version working I went on to making a shortcode version so I could do something like this:

{{% refpo "[1]" "Like this!" %}}

vs. this

<span class="ref"><span class="refnum">[1]</span><span class="refbody">Like this!</span></span>

If the popup should have a larger chunk of text, links, etc. arbitrary markdown or html, I could do this:

{{% refpo "[2]" %}}Lots of stuff, with [links](http://www.xkcd.com) and some html <i>italic tags</i> etc.{{% /refpo %}}

Which would give me this[2]Lots of stuff, with links and some html italic tags etc. .

That is useful because Markdown isn’t rendered in the 2nd arg in the single shortcode version, though html is. For example same as above in single version[3]Lots of stuff, with [links](http://www.xkcd.com) and some html italic tags etc. .

Unfortunately, I ran into an issue where the non-paired version of the shortcode causes extra <p> tags to be placed around some of the shortcodes.[4]it p'd all over my code! . Apparently this is an ongoing and hard to eradicate set of issues, but also I’m new to hugo, go, and shortcodes so it’s certainly possible it’s definitely the case I’ve misunderstood something.

The shortcode is pretty simple and I’ve tried a few variations and not been able to avoid the extra <p> tags in any variant where I tried to accomplish both variations in one shortcode. For example,

<span class="ref"><span class="refnum">{{.Get 0}}</span><span class="refbody">{{ if len .Params | eq 2 }}{{.Get 1|safeHTML}}{{end}}{{ with .Inner}}{{ .|safeHTML}}{{end}}</span></span>

causes the issue. It seems that having .Inner is part of the trigger for the issue. I thought maybe I couldn’t test the existence of .Inner like that, so I tried this variant:

<span class="ref"><span class="refnum">{{.Get 0}}</span><span class="refbody">{{ if len .Params | eq 2 }}{{.Get 1|safeHTML}}{{end}}{{ if len .Inner}}{{ .Inner|safeHTML}}{{end}}</span></span>

But the actual issue turned out to be that the existence of .Inner in the shortcode itself (whether or not it has a value) is a flag of sorts to hugo which makes it consider the shortcode to be the paired type, as per discussion here. Apparently if you don’t then use closing tags, bad things happen ranging from the extra <p> codes to chunks of your text getting pulled into the shortcode. Once I knew that, I thought everything would be ok because I’d read

A shortcode with .Inner content can be used without the inline content, and without the closing shortcode, by using the self-closing syntax:

{{< innershortcode />}}

That quote uses the “shortcode without markdown syntax” rather than the “with markdown syntax”, but I figured it was the same for both. Either I was wrong about it working for the with markdown version, or something is wrong altogether, because when I do

{{% refpo "test1" "Some stuff" /%}}

I get a hugo error from the next shortcode about not having a closing shortcode*and actually, I get an error for the no markdown versions too, so clearly I'm assuming something that isn't true . But if I do:

    {{% refpo "test1" "Some stuff" %}}{{% /refpo %}}

Things work fine.

So, based on that, kinda makes the shortcode not so short for the simple case. So I broke down and made two different shortcodes*and changed the name while I was at it. Better? Meh, /shrug , poref for the simple, non-paired case, and porefx for the extended, paired version that can handle markdown, etc.

poref:

<span class="ref"><span class="refnum">{{.Get 0}}</span><span class="refbody">{{ if len .Params | eq 2 }}{{.Get 1|safeHTML}}{{end}}</span></span>

porefx:

<span class="ref"><span class="refnum">{{.Get 0}}</span><span class="refbody">{{ .Inner|safeHTML}}</span></span>

I’d considered using a scratch variable and making the refnum arg optional, with it counting up on its own if not provided, but I kind of like having arbitrary reference indicators, whether it’s 1, [1], *, or whatever. So I think I’ll call them done for now and move on.

I mean, it’s not groundbreaking or anything, but I think it’ll be handy. I’d prefer a shorter syntax more akin to markdown footnotes, but it’s not awful.

Now to work on the style a bit and figure out if I should use a different main font for the whole site - seeing too many jaggies for my taste here. I didn’t really notice it on the original HTML5 UP theme, so need to investigate.

comments powered by Disqus