Heads-up: This article is being migrated to a new format and may be incomplete. Some of the contents, media, and images may be missing or appear incorrectly. Please forgive any issues while I bring it up to par.
Introduction
Writing footnotes, endnotes, and reference lists can be a daunting and monotonous task, particularly when factoring in the need for compatibility with assistive tools. The following outlines a few interesting techniques that can be used to enhance the usability and accessibility of footnotes, and partially automate number tracking, ultimately making the task a little more manageable.
Demo
In the following, CSS counters are used to automatically generate individual reference numbers. When a reference marker link is activated, it navigates to and highlights the corresponding description. Activating the reference number in the footer will lead you back to the originating marker.
See the Pen Automated accessible footnotes, endnotes, and references by Eric Niquette (@ericniquette) on CodePen.
Basic HTML structure
To get started, we first need some body text, footnotes, and a handful of reference markers. Using an ID, link every marker to its respective footnote.
<article>
<h2>My article</h2>
<p>Lorem ipsum dolor sit amet <a href="#amet">[1]</a>, consectetuer adipiscing elit.</p>
<p>Sed pretium, ligula <a href="#ligula">[2]</a> sollicitudin laoreet viverra.</p>
<p>Eget blandi nunc tortor <a href="#tortor">[3]</a> eu nibh.</p>
<footer>
<h2>Footnotes</h2>
<ol>
<li id="amet">Description of footnote 1</li>
<li id="ligula">Description of footnote 2</li>
<li id="tortor">Description of footnote 3</li>
</ol>
</footer>
</article>
Return links
Scrolling to and from references can cause the user to lose track of where they were. To quickly return to the referring marker, we can add a link by assigning a unique ID to individual markers.
At the end of each footnote, we include a link to the marker's IDs with a unicode arrow icon. To make it more accessible, we'll add an aria-label value that provides context for the symbol.
<article>
<h2>My article</h2>
<p>Lorem ipsum dolor sit amet <a href="#amet" id="amet_ref">[1]</a>, consectetuer adipiscing elit.</p>
<p>Sed pretium, ligula <a href="#ligula" id="ligula_ref">[2]</a> sollicitudin laoreet viverra.</p>
<p>Eget blandi nunc tortor <a href="#tortor" id="tortor_ref">[3]</a> eu nibh.</p>
<footer>
<h2>Footnotes</h2>
<ol>
<li id="amet">Description of footnote 1</li> <a href="#amet_ref" aria-label="Return to content">↩</a>
<li id="ligula">Description of footnote 2</li> <a href="#ligula_ref" aria-label="Return to content">↩</a>
<li id="tortor">Description of footnote 3</li> <a href="#toror_ref" aria-label="Return to content">↩</a>
</ol>
</footer>
</article>
Descriptive markers
To make the markers more descriptive, we can add a section heading with an ID which we can use on the reference markers with an aria-labelledby attribute. This will cause screen readers to announce the reference link [2] as "Link 2 Footnotes" instead of just "Link 2", which is a bit less informative.
The heading will also serve a navigation marker if the user is skipping through headings to find information.
<article>
<h2>My article</h2>
<p>Lorem ipsum dolor sit amet <a href="#amet" id="amet_ref" aria-labelledby="notes_heading">[1]</a>, consectetuer adipiscing elit.</p>
<p>Sed pretium, ligula <a href="#ligula" id="ligula_ref" aria-labelledby="notes_heading">[2]</a> sollicitudin laoreet viverra.</p>
<p>Eget blandi nunc tortor <a href="#tortor" id="tortor_ref" aria-labelledby="notes_heading">[3]</a> eu nibh.</p>
<footer>
<h2 id="notes_heading">Footnotes</h2>
<ol>
<li id="amet">Description of footnote 1</li> <a href="#amet_ref" aria-label="Return to content">↩</a>
<li id="ligula">Description of footnote 2</li> <a href="#ligula_ref" aria-label="Return to content">↩</a>
<li id="tortor">Description of footnote 3</li> <a href="#toror_ref" aria-label="Return to content">↩</a>
</ol>
</footer>
</article>
Complete HTML
When put together, the solution should be structured similarly to this. While this is a perfectly functional solution, the usability can enhanced with a few CSS tricks in the next section.
<article>
<h2>My article</h2>
<p>Lorem ipsum dolor sit amet <a href="#amet" id="amet_ref" aria-labelledby="notes_heading">[1]</a>, consectetuer adipiscing elit.</p>
<p>Sed pretium, ligula <a href="#ligula" id="ligula_ref" aria-labelledby="notes_heading">[2]</a> sollicitudin laoreet viverra.</p>
<p>Eget blandi nunc tortor <a href="#tortor" id="tortor_ref" aria-labelledby="notes_heading">[3]</a> eu nibh.</p>
<footer>
<h2 id="notes_heading">Footnotes</h2>
<ol>
<li id="amet">Description of footnote 1</li> <a href="#amet_ref" aria-label="Return to content">↩</a>
<li id="ligula">Description of footnote 2</li> <a href="#ligula_ref" aria-label="Return to content">↩</a>
<li id="tortor">Description of footnote 3</li> <a href="#toror_ref" aria-label="Return to content">↩</a>
</ol>
</footer>
</article>
Target highlighting
When facing a long list of entries, it's easy to lose track of which footnote you've selected when being redirected. To remediate this, the CSS :target
pseudo-class can be used to highlight the footnote or reference marker that is being targeted.
article :target { background: yellow; }
Automated numbering with CSS counters
Constantly updating the numbering scheme as you add, remove, or rearrange entries is a tedious chore. Instead of keeping track of numbers manually, CSS counters can be utilized to automate the task.
Given that this inserts CSS pseudo content, which may not be compatible with some screen readers, it's a bit of a judgement call on whether you should use this technique. If that's a concern, you can check out the JavaScript solution.
With that said, the counter increments for each element that contains the aria-labelledby attribute as this is an attribute we've set earlier in the HTML.
article {
counter-reset: references;
}
[aria-labelledby="notes_heading"] {
counter-increment: references;
}
[aria-labelledby="notes_heading"]::after {
content: '[' counter(references, decimal-leading-zero) ']';
font-size: 0.875rem;
vertical-align: text-top;
}
To use the counter, remove the text value from the reference markers in the HTML, effectively leaving the anchor empty. They will instead be populated by CSS.
<article>
<h2>My article</h2>
<p>Lorem ipsum dolor sit amet <a href="#amet" id="amet_ref" aria-labelledby="notes_heading"></a>, consectetuer adipiscing elit.</p>
<p>Sed pretium, ligula <a href="#ligula" id="ligula_ref" aria-labelledby="notes_heading"></a> sollicitudin laoreet viverra.</p>
<p>Eget blandi nunc tortor <a href="#tortor" id="tortor_ref" aria-labelledby="notes_heading"></a> eu nibh.</p>
<footer>
<h2 id="notes_heading">Footnotes</h2>
<ol>
<li id="amet">Description of footnote 1</li> <a href="#amet_ref" aria-label="Return to content">↩</a>
<li id="ligula">Description of footnote 2</li> <a href="#ligula_ref" aria-label="Return to content">↩</a>
<li id="tortor">Description of footnote 3</li> <a href="#toror_ref" aria-label="Return to content">↩</a>
</ol>
</footer>
</article>
Numbered return links
Rather than use an arrow, another option is to use the reference numbers as return links. This approach is a little more visually appealing and retains its accessibility features. A second counter is used to keep track of the number of list entries.
By using positioning, the reference number will appear before the reference text but is read at the end of it by screen readers. This is done to avoid having to move back to return, which would be counterintuitive.
ol {
counter-reset: footnotes;
list-style: none;
}
li {
display: block;
margin-bottom: 0.625rem;
position:relative;
}
[aria-label="Return to content"] {
counter-increment: footnotes;
font-weight: 700;
margin-right: 0.625rem;
position: absolute;
right: 100%;
top: 0;
}
[aria-label="Return to content"]::before {
content: counter(footnotes, decimal-leading-zero);
}
Enhanced JavaScript automation
It is possible to fully automate the management of ID and link pairs so you don't need to type them in manually. The following script will increment the values automatically and populate the necessary values.
You may need to tweak the selectors to match your layout but this should give you something to build on. By default, I'm targeting links with the previously-used aria-labelledby attribute, and the individual list items in the footer.
document.addEventListener("DOMContentLoaded", function() {
var articleAnchors = document.querySelectorAll('article a[aria-labelledby="notes_heading"]');
var footerPairs = document.querySelectorAll('footer ol li');
articleAnchors.forEach((anchor, index) => {
var id = "ref_" + (index + 1);
anchor.id = id;
anchor.href = "#note_" + (index + 1);
});
footerPairs.forEach((pair, index) => {
pair.id = "note_" + (index + 1);
pair.children[0].href = "#ref_" + (index + 1);
});
});