On this website, sidenotes appear next to the article when there’s enough space. On small screens, they’re hidden by default and can be made visible with a tap. Here’s an example (sidenote: Actually, here’s the example! This is the actual sidenote. ) .

Luckily, there’s an old typographical solution for that: sidenotes. I can put all the info that’s not critical for the main story there, so you, dear reader, can just skip that kind of frivolry, if you’re not interested.

I noticed that in my writing too. It’s good practice to make blog posts short. But I like to add details, anecdotes and silliness.

I will try to keep it short, because I have a tendency to make stories longer than necessary. My relatives already mocked my rambling when I was a teenager. Complained that my stories had no begin and no end.

Requirements

My sidenotes have two parts:

The sidenote’s content A word or a span of words that it refers to.

My additional requirements for them are:

The span that the sidenote refers to should have a proper semantic HTML element (no abuse of elements made for other purposes)

The content of the sidenote should have a similarly valid HTML tag. Additionally: The sidenote content may contain span elements such as <a> and <em> . The sidenote content may contain clickable elements that can receive keyboard focus. The sidenote content must be stylable.

The elements should not cause auto closing of their parent <p> tag.

Reader modes and apps like Pocket and RSS readers should show sidenote content in a typographically acceptable way. That means at the very least that I can’t rely on website’s CSS to place and style the elements correctly.

The sidenote content should be read by screen readers, in a flow that makes sense. That likely means the two parts should be be placed together.

Elements that don’t work

For sidenote content

Here are some options that I considered, with help from answers on my StackOverflow question.

Let’s first get footnotes out of the way. Footnotes are not the same as sidenotes. They don’t appear in the margin, but below the text. That requires one click to go there and another to go back to the article. Way too much effort. Moreover, most of my sidenotes make no sense without the context of the sentence they refer to. That also rules out the <aside> HTML element.

Asides are meant for, quoting MDN web docs: “a portion of a document whose content is only indirectly related to the document’s main content”.

This is an aside An aside doesn’t require the context of the main article to make sense. It doesn’t just have different styling than a sidenote, it’s semantically different.

The definition element <dfn> could be abused for sidenotes, but my sidenotes are usually not about definitions of words. The <details> element comes close to what I need; it has two parts, like my sidenotes:

<details> <summary> Details </summary> Here would be my sidenote content </details>

Unfortunately, it’s a block element. That means that it shouldn’t be placed inside paragraph. Here’s what happens <summary>when</summary>Sorry, this is hard to follow with the line breaks. you do that. Browsers automatically add a close tag for that paragraph. As you can see, this causes the words following the sidenote to appear in a new paragraph. And Jekyll doesn’t handle it well, but there’s probably a workaround for that.

Finally there’s the abbr element. It’s even closer to a solution, because it can be placed inline. Unfortunately, it’s meant for abbreviations only. Worse, the meaning (or title as it’s called) can only appear on mouse hover and can’t be styled. It can’t contain markup like links either. Example: UX/UI design .

For the phrase that gets the asterisk

I need an HTML tag for the span of words to which the sidenote refers: the phrase with the asterisk in the main text. An simple <a> is commonly used for footnotes, but because I’m not sending the user to another place in the document, that feels off.

On small screens the phrase is clickable and toggles the visibility of the sidenote. The <button> tag would be perfect for it, but buttons are hidden by Safari and Firefox’s reader modes, leaving a gap in the sentence.

In an earlier version of this post, I wrote I use a <span> , but a reader’s comment had something better!

My solution for semantic sidenotes

My solution for now is a combination of HTML, CSS and JavaScript. The HTML looks like this:

<!-- I added line breaks for code readability. In reality, there should be no line breaks before the <span>s. --> <p Some sentence with a < span class= "sidenote sidenote--collapsed" > <span class= "sidenote sidenote--collapsed" > <label class= "sidenote__button" tabindex= "0" title= "Here's the content of the sidenote." aria-describedby= "sidenote-1" onKeypress= "handleSidenoteKeypress(event)" onClick= "handleSidenoteButtonClick(event)" > label </label> <small id= "sidenote-1" class= "sidenote__content" > <span class= "sidenote__content-parenthesis" > (sidenote: </span> Here's the content of the sidenote <span class= "sidenote__content-parenthesis" > ) </span> </small> </span> for the sidenote. </p>

I wrap the whole thing in a span, mainly (sidenote: As a nice side effect, on large screens, where the sidenote appears next to the body text, both asterisks get hover styling when one either the content or the inline span has the mouse cursor over it. ) to keep the required JavaScript simple.

Then there’s the inline phrase that gets the asterisk, or let’s call it a label. I’m using the <label> tag after all. That idea came from Roman Komarov who uses it for this purpose too. I used to think a <label> must refer to an <input> , but that’s actually seems to be not true. With tabindex="0" and some CSS, the thing behaves just like a button. It has a title attribute, so the content is also shown on mouse hover.

For the sidenote content I use the <small> element. MDN says it was made for representing side comments and small print. So semantically that’s pretty good. It has an additional benefit: by default it should be rendered with a smaller font size. So even when CSS is not applied, like in my RSS reader, it should appear smaller.

The spans with the class sidenote__content-parenthesis are hidden with CSS. They’re mainly there for screen readers. I found that when my iPhone reads my posts out loud, only adding parenthesis around the sidenotes is confusing. By adding the phrase ‘sidenote: ‘, it’s much clearer why the sentence is interrupted.

The JavaScript I added is straightforward; it just toggles classes on the wrapper span .sidenote :

function handleSidenoteButtonClick ( e ){ const el = e . target ; el . parentNode . classList . toggle ( ' sidenote--expanded ' ); el . parentNode . classList . toggle ( ' sidenote--collapsed ' ); el . blur (); } function handleSidenoteKeypress ( e ){ if ( e . key === ' Enter ' || e . key === ' ' ){ const el = e . target ; e . preventDefault (); el . parentNode . classList . toggle ( ' sidenote--expanded ' ); el . parentNode . classList . toggle ( ' sidenote--collapsed ' ); }; }

There’s a whole bunch of CSS to make the content appear in the right place, hide it by default on small screens and to add the asterisks. I’m not going to include all of it—use the inspector of you’re interested. But it’s worth mentioning that just hiding the sidenote’s content with display: none causes it to be ignored by screen readers. So instead, I hide it like this:

.sidenote--collapsed .sidenote__content , .sidenote__content-parenthesis { position : absolute ; left : -99999px ; top : auto ; }

Is this solution good enough?

The solution above looks good in the major browsers; when CSS is applied things look as intended. When CSS is not available, default styles make the everything look as intended. In that scenario, the content in the example code above would look like this: “(sidenote: Here’s the content of the sidenote)”. No weird line breaks there.

The only screen reader tests I did were with Safari on iOS and macOS. It speaks the text just as intended, including the ‘sidenote:’ part of it (which is the reason I included that part, otherwise the sentences sound weird).

The reader modes of Firefox and Desktop Safari also do a fine job. They correctly render the content within the <small> tags smaller. Same for Pocket and the RSS reader I use, Reeder. In my previous version (with nested spans) Pocket hid the whole .sidenote component. But now that I use <label> , it’s all good. Chrome’s experimental (sidenote: The experiment ended shortly after I published this post and Chrome no longer has a reader mode at all. ) reader mode adds some superfluous linebreaks, but doesn’t remove any content. Mobile Safari’s reader view hides the sidenote content, but the label remains, so there’s still a readable main article.

There are some issues remaining with this solution though: