Responsive Footnotes

Published

In the responsive footnotes pattern, the footnotes at the very bottom of the page transform into sidenotes alongside body paragraphs. The numbered links within body text also appear and disappear, depending on the width of the browser. It’s a useful technique to display additional information on devices with ample screen space but still present the information to people using smaller devices.

Let’s check out the code that makes it work.

HTML

First off, we’re going to need a container for our content. I’m partial to an <article> element with the class name post. Fill it with a few paragraphs inside <p> elements as usual, supplying whatever content you see fit.

HTML
<article class="post">
<p><!-- paragraph text--></p>
<p><!-- some more text--></p>
<p><!-- yet even more text--></p>
</article>

Now, in order to mark the locations of your annotations, use the following format:

HTML
<sup class="post-marker"><a href="#note:1">1</a></sup>

The <sup> tag semantically identifies the child <a> element as a superscript and also allows us to use CSS properties on two unique elements later on. The href="#note:1" attribute allows users to click annotation markers, repositioning the browser window to reveal related footnotes.

Once the body text is annotated with the intended markup, it’s time to provide the content of our footnotes and sidenotes. Right before the closing </article> tag, add the following ordered list, modified as needed for your content.

HTML
<ol class="post-footnotes">
<li id="note:1">This is the first annotation.</li>
<li id="note:2">This one’s the second.</li>
<li id="note:3">And this is the third.</li>
</ol>

And that’s it for the HTML. Not so bad, right?

JavaScript

The first thing we need to do is create variables for the jQuery objects we’ll be using later.

JavaScript
var $document = $(document),
$post = $(".post"),
$markers = $(".post .post-marker"),
$footnotes = $(".post-footnotes");

Once that’s taken care of, we need to outline our goals and write code to do the following tasks:

  1. Identify each individual footnote within the .post-footnotes list.
  2. Contain the parent of each .post-marker inside a <div> for future presentation.
  3. Loop through each new <div> and add a child <aside> element that contains the related footnote text.

In JavaScript, that looks a lot like this:

JavaScript
function createSidenotes() {
var $footnoteArray = $footnotes.children();
$markers.parent().wrap("<div class='post-subject'></div>");
for (var i = 0; i < $markers.length; i++) {
$($(".post-subject")[i]).append(
// role='complementary' provided for ARIA support
"<aside class='post-sidenote' role='complementary'><p>" +
$($footnoteArray[i]).html() +
"</p></aside>"
);
}
}

CSS has no way of styling parent elements, so we’re also going to need to indicate whether or not our .post contains annotations. The following code checks whether or not any footnotes exist and then adds a class to the .post if the conditional statement returns true.

JavaScript
function toggleNotes() {
if ($footnotes.length > 0 && $markers.length > 0) {
$post.addClass("has-sidenotes");
}
}

Now wrap those two functions in a jQuery ready() method for good measure and you’re almost done.

JavaScript
$document.ready(function () {
createSidenotes();
toggleNotes();
});

CSS

Without any CSS to style and structure the new markup, our footnotes remain visible at the bottom of the page, regardless of browser width. Even worse, the sidenotes fall directly beneath related <p> elements, making them completely ineffective until fixed.

Before writing any CSS, let’s identify our goals this time around:

  1. Style annotation markers to be visually distinct from body text.
  2. Connect each annotation marker with its subject so they are not separated by new lines.
  3. Hide footnotes and annotation markers when the browser width is greater than a certain width.
  4. Display sidenotes when the browser width is less than a certain width.
  5. Style sidenotes to position themselves within the margin of the page.

Using the following CSS, let’s take care of all of those goals one by one and finish things up.

CSS
.post-marker {
font-size: 70%;
position: relative;
top: -0.4em;
}
/* Thin non-breaking spaces are not universally supported,
so we need to shrink a regular non-breaking space. */
.post-marker::before {
content: "\00a0"; /* non-breaking space */
font-size: 25%; /* make the space very thin */
}
.post-marker a {
letter-spacing: 1px; /* Puts narrow spaces between brackets */
}
.post-marker a::before {
content: "[";
}
.post-marker a::after {
content: "]";
}
.post-subject {
position: relative;
}
.post-sidenote {
box-sizing: border-box; /* Add vendor prefixes as needed */
display: none;
left: 100%;
padding-left: 20px; /* Your site’s gutter value */
position: absolute;
top: 0;
width: 25%;
}
@media screen and (min-width: 800px) {
.post.has-sidenotes .post-marker,
.post.has-sidenotes .post-footnotes {
display: none;
}
.post.has-sidenotes .post-sidenote {
display: block;
}
}

And there you have it: responsive footnotes that display as footnotes on wide browsers, yet degrade gracefully to footnotes in narrow browsers and environments without JavaScript support.

Gotchas

If you’ve been writing CSS for a while, you might’ve already considered a few of the limitations associated with this approach, mainly:

With those constraints in mind, I think responsive sidenotes are a worthwhile addition to the design of many blogs—especially when compared to footnotes alone. Responsive sidenotes move additional information out of the main flow of a blog post or article, removing potential distractions to readers while also making annotations easier to read when needed.