This post details how to outfit your Docusaurus site's doc pages and blog posts with a footnotes section.
Overview
Usage considerations
The details outlined in this post are meant to illustrate how to enable footnotes for doc pages and blog posts on your Docusaurus site. As Wikipedia notes, footnotes are notes at the foot of the page — these notes should be supplemental information to the main body of the text, information that should be entirely optional for the reader to consume. This differs from the use of, say, an in-line asterisk with a tool-tip that conveys something important in-place in the main body of the text.[*] Both options have their use. This post details how to use footnotes by means of a package-based solution, namely react-a11y-footnotes
.
Package details
As noted in its docs, the react-a11y-footnotes
package has a trim API:
FootnotesProvider
: We will need to swizzle the Docusaurus root to use this effectively.a component with no HTML footprint that needs to wrap the content part of your application.
FootnoteRef
: This is the actual component we will need to use on different doc pages and blog posts whenever we want to have footnotes.an inline component wrapping a footnote reference, rendering an anchor link (
<a>
) to the correct footnote in the footer.Footnotes
: This component will be inserted into swizzled Docusaurus componentsDocItem/Layout
andBlogPostItem/Content
, resulting in us being able to use footnotes on doc pages and blog posts, respectively.a component rendering the actual footnotes, usually placed at the end of the content area.
Default styling for the package can be overriden and customized using CSS. The library provides namespaced data
attributes as styling anchors:
data-a11y-footnotes-ref
: applied to every single footnote referencedata-a11y-footnotes-footer
: applied to the footnotes wrapperdata-a11y-footnotes-title
: applied to the footnotes titledata-a11y-footnotes-list
: applied to the footnotes listdata-a11y-footnotes-list-item
: applied to every individual footnotedata-a11y-footnotes-back-link
: applied to every individual back link
The example below shows how the anchors above are targeted on the CodeSandbox demo.
CodeSandbox styling example
* {
box-sizing: border-box;
}
body {
margin: 1em;
font-size: 125%;
line-height: 1.4;
max-width: 600px;
margin: 0 auto;
}
[data-a11y-footnotes-footer] {
margin-top: 2em;
border-top: 1px solid silver;
font-size: 80%;
}
[data-a11y-footnotes-list] {
padding-left: 1.25em;
}
/**
* Initialiazing a `footnotes` counter on the wrapper
*/
body {
counter-reset: footnotes;
}
/**
* Inline footnotes references
* 1. Increment the counter at each new reference
* 2. Reset link styles to make it appear like regular text
*/
[data-a11y-footnotes-ref] {
counter-increment: footnotes; /* 1 */
text-decoration: none; /* 2 */
color: inherit; /* 2 */
cursor: default; /* 2 */
outline: none; /* 2 */
}
/**
* Actual numbered references
* 1. Display the current state of the counter (e.g. `[1]`)
* 2. Align text as superscript
* 3. Make the number smaller (since it's superscript)
* 4. Slightly offset the number from the text
* 5. Reset link styles on the number to show it's usable
*/
[data-a11y-footnotes-ref]::after {
content: "[" counter(footnotes) "]"; /* 1 */
vertical-align: super; /* 2 */
font-size: 0.5em; /* 3 */
margin-left: 2px; /* 4 */
color: blue; /* 5 */
text-decoration: underline; /* 5 */
cursor: pointer; /* 5 */
}
/**
* Resetting the default focused styles on the number
*/
[data-a11y-footnotes-ref]:focus::after {
outline: thin dotted;
outline-offset: 2px;
}
[data-a11y-footnotes-back-link] {
font-size: 80%;
}
/**
* Highlight target note
*/
footer :target {
background: yellow;
}
All we have to do now is actually use the package!
Install footnotes package
Start by installing the react-a11y-footnotes
package (see the demo CodeSandbox for an example different from the one included on this post):
npm install react-a11y-footnotes
Swizzling
As noted above, we will need to swizzle the Docusaurus Root
, DocItem/Layout
, and BlogPostItem/Content
components.
Root
As noted in the Docusaurus docs:
The
<Root>
component is rendered at the very top of the React tree, above the theme<Layout>
, and never unmounts. It is the perfect place to add stateful logic that should not be re-initialized across navigations (user authentication status, shopping card state...). Swizzle it manually by creating a file atsrc/theme/Root.js
:/src/theme/Root.jsimport React from 'react';
// Default implementation, that you can customize
export default function Root({children}) {
return <>{children}</>;
}
If we follow the example outlined in the react-a11y-footnotes
package documentation, then we will see that the Docusaurus Root
is where we need to add the FootnotesProvider
:
import React from 'react';
import { FootnotesProvider } from 'react-a11y-footnotes'
// Default implementation, that you can customize
export default function Root({ children }) {
return <FootnotesProvider>{children}</FootnotesProvider>;
}
DocItem/Layout
Swizzle the DocItem/Layout
component (warnings can be safely ignored since the change is so minor):
npm run swizzle @docusaurus/theme-classic DocItem/Layout -- --eject
Update the index.js
file for the swizzled DocItem/Layout
component to include the Footnotes
component as follows:
import React from 'react';
// ...
import { Footnotes } from 'react-a11y-footnotes';
// ...
export default function DocItemLayout({ children }) {
// ...
return (
// ...
<article>
<DocBreadcrumbs />
<DocVersionBadge />
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
<Footnotes />
<DocItemFooter />
</article>
// ...
);
}
BlogPostItem/Content
Swizzle the BlogPostItem/Content
component (warnings can be safely ignored since the change is so minor):
npm run swizzle @docusaurus/theme-classic BlogPostItem/Content -- --eject
Update the index.js
file for the swizzled BlogPostItem/Content
component to include the Footnotes
component as follows:
import React from 'react';
// ...
import { Footnotes } from 'react-a11y-footnotes';
// ...
export default function BlogPostItemContent({ children, className }) {
// ...
return (
<div
// ...
<MDXContent>
{children}
<Footnotes />
</MDXContent>
</div>
);
}
Styling
For the sake of completeness, my personal configuration for footnote styling includes the following options (largely from the CodeSandbox demo):
/* begin react-a11y-footnotes styling */
[data-a11y-footnotes-footer] {
margin-top: 2em;
font-size: 80%;
}
/**
* Initialiazing a `footnotes` counter on the wrapper
*/
body {
counter-reset: footnotes;
}
/**
* Inline footnotes references
* 1. Increment the counter at each new reference
* 2. Reset link styles to make it appear like regular text
*/
[data-a11y-footnotes-ref] {
counter-increment: footnotes; /* 1 */
text-decoration: none; /* 2 */
color: inherit; /* 2 */
cursor: default; /* 2 */
outline: none; /* 2 */
}
/**
* Actual numbered references
* 1. Display the current state of the counter (e.g. `[1]`)
* 2. Align text as superscript
* 3. Make the number smaller (since it's superscript)
* 4. Slightly offset the number from the text
* 5. Reset link styles on the number to show it's usable
*/
[data-a11y-footnotes-ref]::after {
content: "[" counter(footnotes) "]"; /* 1 */
vertical-align: super; /* 2 */
font-size: 0.65em; /* 3 */
margin-left: 2px; /* 4 */
color: var(--ifm-link-color); /* 5 */
text-decoration: underline; /* 5 */
cursor: pointer; /* 5 */
}
/**
* Resetting the default focused styles on the number
*/
[data-a11y-footnotes-ref]:focus::after {
outline: thin dotted;
outline-offset: 2px;
}
[data-a11y-footnotes-back-link] {
font-size: 80%;
}
/* end react-a11y-footnotes styling */
Examples
From the package docs on ID generation for footnotes:
If no
id
is passed to references (<FootnoteRef>
) — which is usually the case — theid
will be computed from the content of the reference. For instance if the text says "CSS counters", the resolved identifiers will becss-counters-ref
andcss-counters-note
.
Hence, if no id
is passed for two footnotes that have identical contents, then the first footnote will be displayed correctly, but the second footnote will not show up, and it will also mess up the numbering for all subsequent footnotes. Try it yourself:
This <FootnoteRef description="First footnote with 'footnote' as its contents.">footnote</FootnoteRef>
will show just fine.
This <FootnoteRef description="Second footnote with 'footnote' as its contents.">footnote</FootnoteRef>
will not show at all; additionally, numbering will be messed up for subsequent footnotes.
This <FootnoteRef description="A different footnote.">differnent footnote</FootnoteRef>
would show up as the third footnote, but only two footnotes would be present.
Word to the wise: be sure such collisions are not possible by the unique nature of a footnote's contents or provide a unique id
prop to the FootnoteRef
component that you will then be responsible for tracking.
All examples below come from the examples in the package documentation (the source code input is shown, immediately followed by the output):
Example 1
Maintaining <FootnoteRef description='Footnotes are notes placed at the bottom of a page. They cite references or comment on a designated part of the text above it.'>footnotes</FootnoteRef> manually can be a pain.
Maintaining footnotes manually can be a pain.
Example 2
<FootnoteRef description='Cascading Style Sheets'>CSS</FootnoteRef> can be used to our advantage here.
Example 3
It becomes easy to maintain footnotes *automatically* by using CSS <FootnoteRef id='with-a-custom-id' description={<> <a href='https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Lists_and_Counters/Using_CSS_counters' target='_blank' rel='noopener noreferrer'> CSS counters </a>are, in essence, variables maintained by CSS whose values may be incremented by CSS rules to track how many times they’re used.</>} >counters</FootnoteRef> to add the numbered references in the text and an ordered list to display the actual footnotes in the footer.
It becomes easy to maintain footnotes automatically by using CSS counters to add the numbered references in the text and an ordered list to display the actual footnotes in the footer.