Table of Contents

Sidenotes in Hugo

Yet another sidenote implementation.

21 Oct 2023. 374 words.


Sidenotes are amazing. They add just enough distraction and provide more context while reading the main text. There are already so many different tutorials on how to implement sidenotes in Hugo, but when I first designed this blog, I followed Tufte CSS’s approach. In short,

  1. Put whatever you want to be in the sidenote in a <span> tag inside a paragraph.
  2. Float the <span> to the side using relative positioning and a negative margin.
  3. Give clear: right property to the <span> to prevent it from overlapping with the next sidenote.
  4. Add a <label> tag and an invisible checkbox to toggle the visibility of the sidenote in mobile environments.

Below is the screenshot of this paragraph and its source code from Tufte CSS’s documentation.

Screen capture of Tufte CSS
<p>
    One of the most distinctive features of Tufte’s style is his extensive use of sidenotes.
    <label for="sn-extensive-use-of-sidenotes" class="margin-toggle sidenote-number"></label>
    <input type="checkbox" id="sn-extensive-use-of-sidenotes" class="margin-toggle">
    <span class="sidenote">This is a sidenote.</span>
    Sidenotes are like footnotes, except they don’t force the reader to jump their eye to the bottom of the page, but instead display off to the side in the margin. Perhaps you have noticed their use in this document already. You are very astute.
</p>

This approach works well, but it has a few problems:

Slight modification

Here is a slightly different approach we can take.

Below is a minimal example of this approach in Hugo.

$aside-width: ...;
$aside-gap: ...;

aside {
    // sidenotes in mobile
    border: 1px solid currentColor;
    border-left-width: 3px;
    background-color: rgba(0, 0, 0, 0.2);

    // sidenotes in desktop
    @media (min-width: 768px) {
        position: relative;
        float: left;
        clear: left;

        width: $aside-width;
        margin: 0;
        margin-left: -$aside-width - $side-gap;
        margin-bottom: 1rem;
        padding: 0;

        text-align: right;
        border: none;
        background-color: initial;
    }
}
<aside>
  {{ .Inner | .Page.RenderString }}
</aside>