Accessible CSS-only burger menu
I was talking to my friend Samuel about frontend development and the use of JS for "nice-to-have" features and he shared this idea of an animated menu with CSS only, using a checkbox.
I had never done it that way before, usually using JS or jQuery for that kind of stuff, so I decided to try it out but suddently came across accessibility issues.
The basic idea was to use a label to control a checkbox's
:checked
state and then use that as a reference to animate its sibling
containing the menu.
So when the checkbox is :checked
, the menu opens up.
Solution 1 - The checkbox method
We wanted to keep the actual checkbox hidden and leave only the
label
(which we can use to check/uncheck the input) visible.
It took just a bit to make sure it was fully accessible because, while trying to hide the checkbox, I kept making it inaccessible to the keyboard.
This is the solution we got:
HTML
<label for="menu-btn">menu</label>
<input type="checkbox" name="menu-btn" id="menu-btn">
<nav>
<ul>
<li>menu item</li>
<li>menu item</li>
</ul>
</nav>
CSS
On my first attempt, I actually wrapped the label
in a button
but
then I couldn't access it with the keyboard to check the checkbox.
So, I used CSS to render the label with a "button" shape and make it
visibly clear this was an interactive element.
Then, as mentioned before, the menu's visibility and position had to
be set up in both the checkbox's states - default and
:checked
.
I also used opacity to avoid layout shift. Without that, you'd keep seeing the menu sliding up on load, which was really annoying.
#checkbox-nav {
overflow: hidden;
& ul {
opacity: 0;
transform: translateY(-200%);
}
}
input[type="checkbox"]:checked + #checkbox-nav > ul {
opacity: 1;
transform: translateY(0);
}
And it actually works! You can click or hit space on the Checkbox Menu "button" and it opens/closes the menu.
Solution 2 - The Popover toggle method
But while researching a bit about the
<button>
element I came accross the
popover
attribute.
With this attribute, the element remains hidden via
display:none
until a control element like a
<button>
opens it.
You can read all about it on the mdn web docs: mdn web docs - popover
In this case, we only needed the button
and to add the
right props to that and the nav
, like the popovertarget
on the first referring to the id
on the second.
HTML
<button popovertarget="button-nav" popovertargetaction="toggle">
button menu
</button>
<nav id="button-nav" popover>
<ul>
<li>menu item</li>
<li>menu item</li>
</ul>
</nav>
CSS
The CSS was used only to restyle the popover because, by default,
it's rendered like a lightbox, and also to set its position on
:popover-open
.
#button-nav {
position: fixed;
background: none;
border: 0;
}
#button-nav:popover-open {
margin: 53px 0 auto auto;
}
Final remarks
I'm sure there are an infinite ways of doing this.
These are just two methods I had fun experimenting with.
Between these solutions, I find the second one better to work with.
I liked the fact that the toggle is automatic, saving some css
lines checking states and, above all, the interaction is
better with both mouse and keyboard.
You can close the menu by simply clicking outside of it or hitting
the escape key no matter what's focused on the page, as opposed to the checkbox version where the only way to do it is with the label
, by hitting it or pressing space while having it focused.
Another pro factor is that, while navigating using the keyboard, it also skips the menu content if it's closed, unlike the checkbox solution.