Inline sidenotes with HTML/CSS/JS
Published on: 2026-02-25
I adapted this example from the note containers used in browser.engineering.
My implementation adds ARIA attributes to make it more accessible for people with disabilities, since <span> doesn't inherently represent anything.
It also adds fallback styling for browsers with JS disabled.
<main>
<p>Here's some text content
<span class="note-container"
role="button" tabindex="0"
aria-expanded="false"
aria-label="Toggle note">
<span class="note">with a secret!</span>
</span>.
</p>
</main>document.body.classList.add("js-enabled");
const containers = document.querySelectorAll(".note-container");
const toggleNote = (el) => {
const isExpanded = el.getAttribute("aria-expanded") === "true";
el.setAttribute("aria-expanded", !isExpanded);
}
containers.forEach((el) => {
el.addEventListener("click", () => toggleNote(el));
el.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
toggleNote(el);
}
});
});body.js-enabled {
counter-reset: fn;
}
.js-enabled .note-container {
cursor: pointer;
counter-increment: fn;
}
.js-enabled .note-container::before {
content: "[" counter(fn);
}
.note-container::before {
content: "[";
}
.note-container::after {
content: "]";
}
.note-container[aria-expanded="true"]::before,
.note-container[aria-expanded="true"]::after {
content: "...";
user-select: none;
opacity: 0.6;
margin: 0 2px;
}
.js-enabled .note {
display: none;
}
.note-container[aria-expanded="true"] .note {
display: inline;
font-style: italic;
}
.note-container {
display: inline;
color: slategray;
}
.note-container:focus {
outline: none;
}
.note-container:focus-visible {
outline: 2px solid blue;
outline-offset: 2px;
border-radius: 2px;
}