I will try to keep it short, because I have 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.
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.
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.
This late-13th century page has wide margins for notes. Although the notes may have been added later in this example, it’s not uncommon for printed texts to have sidenotes from the author.
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. )
.
The problem is, it’s hard to find a properly semantic HTML solution to do this.
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 and .
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
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
Here are some options that I considered, with help from answers on my StackOverflow question.
Footnotes1 appear 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
A common design pattern is to have something like a “card” element that has to be fully clickable. This is usually because it links to another page or triggers a JavaScript action.
The problem though, is that often, you end up with stuff that looks like this:
<divonClick="alert('Nope')">Please don’t ever do this.div>
You should absolutely never attach a click event to a
element, though, even if you sprinkle it with aria roles to “fake” a real button. Although this is technically possible, if assistive technology doesn’t support the aria roles in question, the user will just get
s and nothing else. Not cool.
In this article, we’re going to remedy this common crime by instead using the magic of CSS to give us the desired fully clickable element effect, while also using proper semantic elements and JavaScript and as an enhancement.
To code along, you’re going to need a HTML file, a CSS file and a JavaScript file. I recommend that you use a service like CodePen that does all of this for you.
For the rest of this section, we’re going to be laying the foundations. If you’re in a rush, you can skip this section and use this starter CodePen instead.
<articleclass="box"> <h1>A semantic, breakout buttonh1> <p>This whole box is clickable, but still uses a button element, correctly.p> <buttonclass="breakout-button"type="button">Say Hi ?button> article>
We’ve got an article, a heading, a paragraph and a button. Job done for HTML for now.
Here, we have some simple styles for our button which make it looks nicer. A handy takeaway trick here is that because we are using font: inherit and color: currentColor, we get all of our text styles for free, using the cascade, which is by far my favourite aspect of CSS. Also notice that we are setting .breakout-button to be position: static. This is because we want our “breakout element” (coming up) to literally break out of the button!
Related to this is that the .box element has position: relative, which means that any child elements without a relative parent that have position: absolute will be contained in this .box. Because that’s what our breakout button will do as its sole purpose, you should always remember to make its containing parent behave like this. The position: static on the .breakout-button itself is a failsafe to make sure the “breakout element” isn’t ever contained to the .breakout-button element.
Right, let’s add some more code. Under the .breakout-button component style, add this CSS:
We’ve set both the button and its ::before pseudo-element to have a pointer cursor. There’s some contention around wether a should have a pointer cursor and I very much sit in the camp that by proxy of exposure, it is now an expected style and shouldn’t really have a negative user experience impact.
Anyway, we digress… Add the following CSS to create the “breakout element”:
It’s pretty straightforward. We make this pseudo-element an absolutely positioned block element that will “break out” until it hits the bounds of a relative, absolute or fixed parent. In our context, this parent is the .box element because it has position: relative set.
The “breakout element” is visually hidden, so to help you understand how it behaves, check out this demo where I’ve added some colour to it:
Now that we’ve got the core component setup, let’s style up the interactivity. Add this to your CSS:
.breakout-button:focus{ outline: none; }
.breakout-button:hover{ background: #333333; }
The first thing we do is remove focus outline from the button. I can’t stress this enough, though: you must have visible focus styles for interactive elements, so keyboard users can actually see where their focus currently is. If you remove the default outline CSS rule, you must replace it with something effective and obvious.
This is exactly what we’re going to do now, by adding a solid outline to our “breakout element” when the parent button is focused. Add this CSS:
This makes our box shift up either on hover, or if there’s focus inside it, which means when our button element is focused, our box’s appearance will change.
That’s it! Our “breakout button” finished and this is what it should look like:
Improving our breakout button with progressive enhancement permalink
As usual, I make you think that you’re done, but then slip in some progressive enhancement when you least expect it, because as it stands, our project is just ok. Because we’re using a , it’ll be about as useful as a chocolate teapot when JavaScript isn’t available. What we’re going to do to fix this is hide the button by default with a hidden attribute, and when JavaScript is available, we’ll show it by removing the hidden attribute.
We’re also going to add a bonus bit of attention to detail and only display the interactive states when JavaScript is available too, using a data attribute as a style hook.
Open up your HTML and add a hidden attribute to the like this:
<buttonclass="breakout-button"type="button"hidden>Say Hi ?button>
Now we need to add some JavaScript to show the by removing that hidden element:
Like I said above, though, we should also only show interactive styles such as :hover and :focus states when the button is available too. Let’s replace your existing JavaScript with this JavaScript:
We’ve added an event handler for the button’s click event, but most importantly, we’ve added a data-interactive attribute to the button’s parent, which means we can now use this as a style hook.
Hopefully this article shows that when you think outside the box (or inside it in this context), CSS, semantic HTML and a sprinkle of JavaScript can give you solid, progressive components that work for everyone.