HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<article>
<h1>Endnotes/Footnotes/Sidenotes</h1>
<p>I like being able to add extra information, tangents, or conjecture to my writing, but I don't always wish to clutter or disrupt the main flow of text. Footnotes are great for this, but the typical implementation is less than ideal as it has users jumping all over the page every time they wish to read one.</p>
<p>Thus, I have this implementation. Footnotes are marked up as usual, but some JavaScript-based progressive enhancement improves the experience. On horizontally challenged viewports, clicking a footnote will open it in a popover. Otherwise it'll also appear as a sidenote. This overall experience is very much influenced by <a href="https://gwern.net/sidenote">Gwern's implementation and research</a>.</p>
<p>There is a note after this. <sup><a id="footnote-ref-1" href="#footnote-1" data-footnote-ref aria-describedby="footnote-label">1</a></sup></p>
<p>Here is a lot of them, to see if they break… <sup><a id="footnote-ref-2" href="#footnote-2" data-footnote-ref aria-describedby="footnote-label">2</a></sup> <sup><a id="footnote-ref-3" href="#footnote-3" data-footnote-ref aria-describedby="footnote-label">3</a></sup> <sup><a id="footnote-ref-4" href="#footnote-4" data-footnote-ref aria-describedby="footnote-label">4</a></sup> <sup><a id="footnote-ref-5" href="#footnote-5" data-footnote-ref aria-describedby="footnote-label">5</a></sup> <sup><a id="footnote-ref-6" href="#footnote-6" data-footnote-ref aria-describedby="footnote-label">6</a></sup> <sup><a id="footnote-ref-7" href="#footnote-7" data-footnote-ref aria-describedby="footnote-label">7</a></sup> <sup><a id="footnote-ref-8" href="#footnote-8" data-footnote-ref aria-describedby="footnote-label">8</a></sup></p>
<p>Lorem ipsum odor amet, consectetuer adipiscing elit. Dui fames viverra ultrices nulla augue, suscipit mollis ligula. Leo egestas interdum dapibus egestas est neque ligula dictum. Lectus dictum parturient eget proin mauris ornare scelerisque dui pretium. Mollis neque ex mus non lobortis mauris ad. Facilisi et viverra ullamcorper nullam ex. Sodales sociosqu eu rutrum erat ipsum, volutpat porttitor mattis. Senectus lectus dis diam fringilla, class dapibus mollis.</p>
<p>Nostra maximus volutpat, etiam placerat varius eros. Rutrum tempor blandit placerat pretium netus sit nullam sollicitudin. Quam pulvinar orci finibus vel sapien sapien tempus. Orci at maecenas parturient facilisi senectus enim rhoncus. Porttitor neque nam mus dui taciti turpis platea. Lacinia nibh ornare odio vestibulum ex curabitur. Fames fusce vitae sed aenean quisque. Platea ut quam volutpat pulvinar sem. Molestie neque euismod netus arcu purus natoque?</p>
<p>Quis augue tempus quisque ultricies; in lectus metus. Rhoncus vitae nunc habitant habitant; egestas porta scelerisque. Vivamus cursus interdum in sem turpis tincidunt laoreet varius. Tristique velit est ipsum ac in. Sagittis ornare imperdiet suspendisse porta platea tristique per. Accumsan integer sociosqu semper leo natoque dictum laoreet. Mi mus vestibulum donec etiam leo feugiat parturient.</p>
<p>Bibendum scelerisque donec consequat maximus dapibus vitae, felis nisl luctus. Curabitur ex curae ligula mattis facilisis ut dolor pretium placerat. Efficitur leo imperdiet mauris mattis convallis, massa pulvinar duis? Aenean imperdiet hac condimentum rhoncus aliquet felis tempus curae. Justo egestas natoque at donec ante enim; eget curabitur. Diam cursus aliquet sapien convallis pretium taciti. Fusce sed potenti efficitur phasellus natoque per. Phasellus pretium semper aliquam ante enim sapien a viverra conubia.</p>
<p>Efficitur tellus facilisis nascetur, bibendum nunc sit. Finibus adipiscing vestibulum proin dis nascetur dui enim faucibus. Ut volutpat integer, suscipit netus orci fringilla commodo feugiat. Sociosqu gravida posuere egestas praesent fames egestas. Torquent lobortis maecenas, neque id bibendum faucibus quisque. Dis phasellus tristique dignissim himenaeos accumsan tortor venenatis. Porta tellus aliquet euismod sollicitudin posuere torquent. Iaculis enim semper penatibus et facilisis auctor eget accumsan dui.</p>
<section class="footnotes" data-footnotes>
<h2 id="footnote-label" class="sr-only">Footnotes</h2>
<ol>
<li id="footnote-1">
<p>This is a footnote, but more accurately it’s an endnote, and it’ll appear as a sidenote on larger screens. <a href="#footnote-ref-1" data-footnote-backref aria-label="Back to reference 1">↩</a></p>
</li>
<li id="footnote-2">
<p>Magni dolorum ipsa voluptas tempore est ut in. Dicta corrupti ipsa aspernatur. Soluta sunt quae impedit. Eos nobis accusantium ut amet. <a href="#footnote-ref-2" data-footnote-backref aria-label="Back to reference 2">↩</a></p>
</li>
<li id="footnote-3">
<p>Id ex ipsum rerum tempore. Sunt ab deserunt impedit architecto reprehenderit ex. Corporis dolores qui accusamus dolorem. Laudantium optio voluptatem eum id qui. Est ut laborum unde. <a href="#footnote-ref-3" data-footnote-backref aria-label="Back to reference 3">↩</a></p>
</li>
<li id="footnote-4">
<p>Dolor expedita repudiandae voluptas. Veritatis beatae quis impedit id veniam. Nisi illum animi voluptatem magnam labore sunt omnis. <a href="#footnote-ref-4" data-footnote-backref aria-label="Back to reference 4">↩</a></p>
</li>
<li id="footnote-5">
<p>Est illo fuga autem fugiat qui quaerat inventore. Suscipit in aut vero libero labore ut. Facere enim nihil itaque. Enim aut dignissimos non velit autem odio quae. Libero et corrupti eos. <a href="#footnote-ref-5" data-footnote-backref aria-label="Back to reference 5">↩</a></p>
</li>
<li id="footnote-6">
<p>Similique laboriosam quas ipsam molestias quia. Earum maxime quo veniam modi. Exercitationem et odit cum. Aspernatur et ad dolor voluptatem aperiam nihil et ut. <a href="#footnote-ref-6" data-footnote-backref aria-label="Back to reference 6">↩</a></p>
</li>
<li id="footnote-7">
<p>Cum eligendi quaerat blanditiis. Nostrum distinctio vel veritatis ut quod vel ea ipsa. Doloribus quae quia quibusdam veritatis provident reiciendis reiciendis quisquam. <a href="#footnote-ref-7" data-footnote-backref aria-label="Back to reference 7">↩</a></p>
</li>
<li id="footnote-8">
<p>Eaque perferendis eos illo. Cum quos qui aut commodi. Et cumque quaerat molestiae. Quo laboriosam itaque odit dolorem perferendis eius. <a href="#footnote-ref-8" data-footnote-backref aria-label="Back to reference 8">↩</a></p>
</li>
</ol>
</section>
</article>
:root {
color-scheme: light dark;
--black: oklch(18% 0.003 17.5);
--dark_grey: oklch(24% 0.006 17.6);
--grey: oklch(36% 0.003 17.3);
--bright_grey: oklch(47% 0.005 17.3);
--white: oklch(76% 0.03 55);
--bright_white: oklch(94.75% 0.04 73);
--blue: oklch(56.5% 0.13 253);
--cyan: oklch(71.2% 0.112 198.7);
}
body {
margin: 3rem;
font-family: "EB Garamond", serif;
color: light-dark(var(--black), var(--bright_white));
background: light-dark(var(--bright_white), var(--black));
@media (max-width: 700px) {
margin: 0 0.8rem;
}
}
article {
max-width: 80ch;
* + h2 {
margin-top: 2rem;
}
> * + * {
margin-top: 1rem;
}
}
h1,
h2 {
text-transform: uppercase;
text-wrap: balance;
font-weight: 700;
}
h1 {
font-size: 2.5rem;
line-height: 2.8rem;
}
h2 {
font-size: 1.8rem;
line-height: 2rem;
}
a,
.footnote-button {
color: inherit;
text-decoration: underline 0.1rem light-dark(var(--cyan), var(--blue));
-webkit-text-decoration-line: underline;
-webkit-text-decoration-color: light-dark(var(--cyan), var(--blue));
text-decoration-skip-ink: none;
text-underline-offset: 0.2rem;
&:hover,
&:active {
text-decoration-thickness: 0.7rem;
text-underline-offset: -0.37rem;
}
}
.sidenote {
display: none;
@media (min-width: 960px) {
display: block;
position: absolute;
left: 45rem;
right: 3rem;
max-width: 60ch;
font-size: small;
line-height: 1.5;
color: light-dark(var(--grey), var(--white));
padding-bottom: 1rem;
transition: background-color 0.25s;
border-top: 1px solid light-dark(var(--white), var(--bright_grey));
p {
padding-top: 0.25rem;
}
> * + * {
margin-top: 0.5rem;
}
}
}
.footnote-button.highlight,
.sidenote.highlight {
outline: 2px solid var(--blue);
color: inherit;
}
.sidenote:hover {
color: inherit;
}
.footnote-popover {
position: absolute;
inset: auto auto auto 3rem;
border: 2px solid light-dark(var(--white), var(--bright_grey));
background: light-dark(var(--bright_white), var(--black));
padding: 0.5rem 1rem;
font-size: small;
line-height: 1.5;
@media (max-width: 700px) {
inset: auto 0.8rem;
}
.footnote-content > * + * {
margin-top: 0.5rem;
}
[data-footnote-backref] {
display: none;
}
}
.footnotes li {
font-size: small;
line-height: 1.5;
p {
border-left: 2px solid light-dark(var(--white), var(--bright_grey));
padding-left: 1rem;
margin-left: 0.5rem;
}
&:target p {
border-color: var(--blue);
}
* + * {
padding-top: 0.5rem;
}
}
.footnote-button {
background: none;
border: transparent;
font: inherit;
}
class FootnotesSidenotes {
constructor() {
this.sidenotesBreakpoint = 960;
this.references = document.querySelectorAll("sup a[data-footnote-ref]");
this.sidenoteContainer = this.createSidenoteContainer();
this.articleWidth = this.getArticleWidth();
this.init();
}
init() {
if (!this.references.length) {
console.warn("No footnote references found");
return;
}
this.setupReferences();
}
getArticleWidth() {
const article = document.querySelector('article');
return article ? article.offsetWidth : null;
}
createSidenoteContainer() {
const container = document.createElement("div");
container.className = "sidenote-container";
document.body.appendChild(container);
return container;
}
toggleHighlight(element, action) {
element.classList[action]("highlight");
}
positionSidenote(sidenote, reference, index) {
if (window.innerWidth < this.sidenotesBreakpoint) {
return;
}
const rect = reference.getBoundingClientRect();
let top = rect.top + window.scrollY;
if (index > 0) {
const prevSidenote = this.sidenoteContainer.children[index - 1];
const prevBottom = prevSidenote.offsetTop + prevSidenote.offsetHeight;
if (top < prevBottom) {
top = prevBottom + 10;
}
}
sidenote.style.top = `${top}px`;
}
createPopover(content, index) {
const popover = document.createElement("div");
popover.id = `footnote-popover-${index}`;
popover.setAttribute("popover", "auto");
popover.className = "footnote-popover";
if (this.articleWidth) {
popover.style.maxWidth = `${this.articleWidth}px`;
}
const contentWrapper = document.createElement("div");
contentWrapper.className = "footnote-content";
contentWrapper.innerHTML = content;
popover.appendChild(contentWrapper);
return popover;
}
positionPopover(popover, button) {
const buttonRect = button.getBoundingClientRect();
const popoverRect = popover.getBoundingClientRect();
const scrollTop = window.scrollY;
popover.style.top = `${buttonRect.bottom + 10 + scrollTop}px`;
}
setupReference(reference, index) {
const footnoteId = reference.getAttribute("href").substring(1);
const footnoteContent = document.getElementById(footnoteId).innerHTML;
const sidenote = document.createElement("div");
sidenote.className = "sidenote";
sidenote.innerHTML = footnoteContent;
this.sidenoteContainer.appendChild(sidenote);
const button = document.createElement("button");
button.className = "footnote-button";
button.id = `footnote-ref-${index + 1}`;
button.innerHTML = reference.innerHTML;
const popover = this.createPopover(footnoteContent, index);
document.body.appendChild(popover);
reference.parentNode.replaceChild(button, reference);
["mouseenter", "mouseleave"].forEach((event) => {
const action = event === "mouseenter" ? "add" : "remove";
button.addEventListener(event, () => this.toggleHighlight(sidenote, action));
sidenote.addEventListener(event, () => this.toggleHighlight(button, action));
});
button.setAttribute("popovertarget", popover.id);
button.setAttribute("popovertargetaction", "toggle");
const updatePosition = () => {
this.articleWidth = this.getArticleWidth(); // Update article width on resize
if (this.articleWidth) {
popover.style.maxWidth = `${this.articleWidth}px`;
}
this.positionSidenote(sidenote, button, index);
};
window.addEventListener("load", updatePosition);
window.addEventListener("resize", updatePosition);
popover.addEventListener("toggle", (e) => {
if (popover.matches(":popover-open")) {
this.positionPopover(popover, button);
}
});
}
setupReferences() {
this.references.forEach((reference, index) =>
this.setupReference(reference, index)
);
}
}
document.addEventListener("DOMContentLoaded", () => {
new FootnotesSidenotes();
});
Also see: Tab Triggers