CSS-only responsive sidenotes in Hugo
Adding simple sidenotes to my blog in CSS
For this blog I am using Hugo ʕ•ᴥ•ʔ Bear Blog; I like it because of its minimalist, clean, responsive design. When writing my recent post about emails, I wanted to add sidenotes (or maybe more accurately, margin notes) to it for side comments, so obviously I spent more time adding support for sidenotes than actually writing the article itself1.
I want to mention two articles that were inspirational, although in the end I have a simpler implementation that I will describe in this post:
The implementation keeps the responsiveness and is Javascript free by using CSS Flexbox.
Usage as a Hugo shortcode2
I created a sidenote
Hugo shortcode that can be used like so:
When rendered, it looks like this:
This a regular part of the main body of the article. It can have newlines and even Markdown which will be rendered normally.
Within the implementation of the shortcode, this content can be referred to as the first parameter with .Get 0.
This is the content of the sidenote. It can also have newlines and will render Markdown normally.
Within the implementation of the shortcode, this content can be referred to as the “inner” parameter with .Inner.
So, it will add the note as a box to the right of the first content. And the only limitation (which you can probably notice even in this example) is that if the sidenote content is longer than the actual content, then extra vertical whitespace is added (which I am fine with, and can be avoided by adding enough content that this is no longer the case).
Implementation
Hugo expects your shortcodes to be defined in a HTML file with the following path pattern:
layouts/shortcodes/<shortcode>.html
so, in this case,
layouts/shortcodes/sidenote.html
And this is the full content of this file:
<div class="paragraph-with-sidenote">
<div class="sidenote-paragraph">
{{ .Get 0 | markdownify }}
</div>
<div class="sidenote-box">
<div class="sidenote-note">
{{ .Inner | markdownify }}
</div>
</div>
</div>
We organize the content in a container div
, which has
- one
div
for the main content where we render with{{ .Get 0 | markdownify }}
(themarkdownify
bit is there to make it render the content as Markdown). - and a second
div
for the sidenote, first an outer one that we will use to set the width of the note properly, and then an inner one to set the height and draw the box with the background.
In order to have this additional CSS available in my pages, I added a layouts/partials/custom_head.html
file including it like this:
{{ range .Site.Params.customCss -}}
<link rel="stylesheet" href="{{ . | absURL }}">
{{- end }}
And then I can add this in the Params of the config.toml
:
customCss = ["css/sidenote.css"]
And this is the full content of that file:
.paragraph-with-sidenote {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
/* allow this box to go beyond the normal width to the right
so that there is space for the sidenote
*/
width: calc(720px + (100vw - 720px)/2.1);
}
.sidenote-paragraph {
padding-right: 1rem;
width: 720px;
}
.sidenote-box {
width: calc((100vw - 720px)/3);
}
.sidenote-note {
background-color: whitesmoke;
border: solid 1px grey;
border-radius: 10px;
box-sizing: content-box;
padding: 0.8rem;
}
/* below 1000 there isn't enough space for a sidenote on the right margin anyway.
force widths so that it goes on a block below the paragraph.
*/
@media screen and (max-width: 1000px) {
.paragraph-with-sidenote {
width: 100%;
}
.sidenote-box {
width: 100%;
}
}
For this design I do rely heavily on the fact that the blog content width is fixed to 720px.
About the width calculations:
width: calc(720px + (100vw - 720px)/2.1);
The window has a 100vw width, out of which we need to reserve 720px for the blog content, so the space available for the sidenote is half of that (the other half is the left margin). For the flexbox, we need it to have width equal to blog content + sidenote content, hence the formula. However I was seeing a small horizontal scrollbar which shouldn’t be there (maybe a padding somewhere?) so to avoid that I reduced it a bit further by dividing between 2.1.
As for the width of the sidenote:
width: calc((100vw - 720px)/3);
I found that it doesn’t look nice if the sidenote stretches to the very edge of the window, and wanted to cut it a bit shorter, therefore /3 instead of /2.
-
Well, actually, my wife helped me a lot to make it work, since I don’t actually know anything about HTML or CSS. ↩︎
-
See shortcodes and Create Your Own Shortcodes. ↩︎