Footnotes are enabled by default. A reference in the body text renders as a numbered superscript marker, and the note itself is collected into a block at the end of the post with a link back to the point of citation.
Writing a footnote
There are two syntaxes and they can be mixed freely in the same post.
A labelled footnote is a reference and a matching definition:
Elixir processes are not OS threads.[^scheduler]
[^scheduler]: They are scheduled by the BEAM across a pool of OS threads,
one scheduler per core by default.An inline footnote puts the note where it is cited, with no label to invent:
Elixir processes are not OS threads.^[They are scheduled by the BEAM across a pool of OS threads.]Reach for a labelled footnote when the note is long, when it wants more than one paragraph, or when you expect to link to it. Reach for an inline footnote for a short aside you do not want to break your writing flow over.
How they behave
Definitions are gathered at the end of the rendered post no matter where they sit in the source, so a labelled definition can live next to the paragraph that cites it.
Markers are numbered by the order the references appear in the text, not the order the definitions are written.
A definition body may run to several paragraphs if the continuation lines are indented by four spaces.
A reference with no matching definition is left as literal text. A stray
[^note]shows up in the rendered page rather than failing the build.Labelled notes get an anchor derived from the label, so
[^scheduler]becomes#fn-scheduler. Inline notes are numbered positionally instead, so their anchors shift if you add another inline note earlier in the post. Use a labelled footnote for anything you intend to link to from elsewhere.
Rendered markup
Knowing the shape helps when writing the stylesheet. A reference renders as:
<sup class="footnote-ref"><a href="#fn-scheduler" id="fnref-scheduler" data-footnote-ref>1</a></sup>And the block at the end of the post as:
<section class="footnotes" data-footnotes>
<ol>
<li id="fn-scheduler">
<p>They are scheduled by the BEAM across a pool of OS threads.
<a href="#fnref-scheduler" class="footnote-backref" data-footnote-backref
data-footnote-backref-idx="1" aria-label="Back to reference 1">↩</a></p>
</li>
</ol>
</section>The backreference link carries its own aria-label, so it is announced usefully without extra work.
Styling
The engine ships no stylesheet — the default template links /css/site.css from your blog's priv/static/. Add the following there.
It is written against custom properties with fallbacks, so it drops into an existing stylesheet whether or not you have those properties defined. Sizes are suggested rather than fixed, and spacing uses logical properties so the block behaves in any writing direction.
/* Footnotes
----------------------------------------------------------------- */
/* The superscript marker in the body text. */
.footnote-ref {
font-size: 0.75em;
line-height: 1;
}
.footnote-ref a {
text-decoration: none;
border-radius: 2px;
/* Widen the tap target without disturbing the line box. */
padding-inline: 0.15em;
padding-block: 0.4em;
margin-block: -0.4em;
}
.footnote-ref a:hover,
.footnote-ref a:focus-visible {
background: var(--highlight, rgba(255, 214, 102, 0.3));
}
/* The block of notes at the foot of the post. */
.footnotes {
margin-block-start: 3rem;
padding-block-start: 1rem;
border-block-start: 1px solid var(--rule, rgba(127, 127, 127, 0.3));
font-size: 0.9rem;
color: var(--muted, #5b5b66);
}
/* The rendered markup carries no heading, so supply one. */
.footnotes::before {
content: "Notes";
display: block;
margin-block-end: 0.75rem;
font-size: 0.8rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.footnotes ol {
margin: 0;
padding-inline-start: 1.5rem;
}
.footnotes li {
margin-block: 0.5rem;
}
.footnotes li > p {
margin-block: 0.35rem;
}
.footnote-backref {
text-decoration: none;
margin-inline-start: 0.25em;
}
/* Jumping between a marker and its note: keep the destination clear of
the viewport edge, and make it obvious which note was landed on. */
.footnotes li,
.footnote-ref a {
scroll-margin-block-start: 2rem;
}
.footnotes li:target {
background: var(--highlight, rgba(255, 214, 102, 0.3));
border-radius: 3px;
}
[data-theme="dark"] .footnotes li:target,
[data-theme="dark"] .footnote-ref a:hover,
[data-theme="dark"] .footnote-ref a:focus-visible {
background: var(--highlight, rgba(255, 214, 102, 0.18));
}
@media print {
.footnote-backref {
display: none;
}
}Two notes on the above. The :target rule is the one worth keeping: without it a reader who clicks a marker lands in a list of notes with nothing indicating which one they came for. And the ::before heading is generated content, which screen readers announce but which is not a real heading, so it does not appear in a document outline or heading navigation.
To render markers as [1] rather than a bare superscript, add:
.footnote-ref a::before { content: "["; }
.footnote-ref a::after { content: "]"; }Footnotes in the RSS feed
The feed puts the full post body in content:encoded, so notes travel with the post and their internal links resolve within the item. Anchors are derived from the label, which means two posts in the same feed that both use [^1] produce two elements with id="fn-1". Descriptive labels — [^scheduler] rather than [^1] — avoid the collision and read better in the source besides.
Turning features off
Both syntaxes are configurable. To go back to labelled footnotes only:
config :static_blog, :markdown,
extension: [inline_footnotes: false]Or to disable footnotes entirely:
config :static_blog, :markdown,
extension: [footnotes: false, inline_footnotes: false]Options are deep-merged over the defaults, so naming one key leaves the rest — tables, strikethrough, autolinks, task lists, smart punctuation — untouched. See StaticBlog.Markdown for the full default set.