Footnotes Sidenotes
Footnotes Sidenotes
/plugin-footnotes transforms standard GFM footnotes ([^1]) from a bottom-of-page list into elegant sidenotes displayed in the page margin — a typographic pattern popularised by Tufte CSS and used widely in long-form writing.
On narrow viewports (below collapseBreakpoint) the plugin gracefully falls back to the standard footnote layout produced by remark-gfm.
Installation
pnpm add /plugin-footnotes
Setup
// stedefast.config.ts
import { defineConfig } from "@stedefast/core";
import { FootnotesPlugin } from "/plugin-footnotes";
export default defineConfig({
// ...
plugins: [
FootnotesPlugin({
position: "right",
collapseBreakpoint: 1200,
sidenoteWidth: 260,
}),
],
});
Writing Footnotes
Use standard GFM footnote syntax — remark-gfm must be active in your pipeline (it is by default):
The Stedefast build pipeline runs in seven stages.[^1]
Modules are the secret sauce.[^2] They bridge the static/dynamic gap elegantly.
[^1]: Config → Content → Module Export + Asset Pipeline (parallel) → Render → CF Functions → Manifest.
[^2]: Each module implements `buildStaticExport()` for build-time JSON and `workerHandlers` for edge mutations.
On wide screens, the footnotes appear in the right margin, vertically aligned with their inline reference. On narrow screens, they are shown at the bottom as usual.
How It Works
The plugin is a pure headAssets injection — no rehype tree transformation required.
- At build time, Stedefast injects a small
<style>block and a<script>block into every page's<head>. - On page load, the script finds the
<section data-footnotes>element added byremark-gfm. - For each footnote reference link (
<a data-footnote-ref>), the script creates a<span class="sf-fn-sidenote">adjacent to the reference and moves the footnote content into it. - CSS positions the sidenote in the margin (absolute, right of the article) on wide screens, and hides it on narrow screens.
Requirements
remark-gfmmust be active (Stedefast enables it by default via/content)- Your article element should have
position: relativeand enough right/left margin for the sidenotes (typicallymargin-right: 280pxon the article whenposition: "right")
Options
| Option | Type | Default | Description |
|---|---|---|---|
position |
"right" | "left" |
"right" |
Which side of the article to show sidenotes |
classPrefix |
string |
"sf-fn" |
CSS class prefix for all injected elements |
collapseBreakpoint |
number |
1200 |
Viewport width (px) below which sidenotes collapse to bottom footnotes |
sidenoteWidth |
number |
260 |
Width of the sidenote column in pixels |
CSS Customisation
The plugin uses CSS custom properties you can override in your theme's global.css:
:root {
--sf-fn-width: 260px;
--sf-fn-gap: 20px;
--sf-fn-color: #666;
--sf-fn-border-color: #e2e8f0;
--sf-fn-font-size: 0.85em;
--sf-fn-line-height: 1.5;
}
For dark mode, override in your dark theme selector:
[data-theme="dark"] {
--sf-fn-color: #9ca3af;
--sf-fn-border-color: #374151;
}
Responsive Behaviour
| Viewport | Behaviour |
|---|---|
≥ collapseBreakpoint |
Sidenotes in margin, bottom footnotes section hidden |
< collapseBreakpoint |
Sidenotes hidden, standard bottom footnotes shown |
The prefers-reduced-motion media query is respected — no transitions are applied.
Article Margin
For right sidenotes to show without overlapping content, your article wrapper needs sufficient right margin. A Tailwind example:
// theme/layouts/default.tsx
<article className="mx-auto max-w-2xl px-4 xl:mr-72">
{/* sidenotes will appear in the xl:mr-72 gutter */}
{children}
</article>