I’ve noticed that recently, a lot of people are obsessed with removing extra JavaScript and doing as much as possible using only CSS. Even I started thinking, if something can be done in a CSS only way, perhaps this is the right way? I got caught up in this too while trying to boost my website’s PageSpeed score…
You can find many tutorials on the internet on how to make a CSS only hamburger menu, CSS only popup or a CSS only accordion. Personally I think it’s kinda fun when you can make things interactive in CSS (for example imitating a click event), but there are some downsides to that. I am going to highlight those downsides in this article.
Well what does even “CSS only” mean? CSS only refers to making things that normally require JavaScript to work, without the need of JavaScript. The main reason behind this is to reduce loading time, because of course more JavaScript = increased CPU workload = slower website. Or even worse, more JavaScript = increased CPU workload = worse PageSpeed score 😛.
CSS only hamburger menu
Let’s for example take a CSS only menu, which uses CSS sibling selectors to work. The idea behind is very simple, you create a checkbox, and depending on the state of that checkbox, you can toggle the visibility of the menu in CSS. The menu needs to be positioned either immediately after the checkbox in the DOM or on the same level. In this example, we will use the ~ selector, which assumes that the element is placed at the same level in DOM.
You can make your HTML look like this:
<input id="hamburger-toggle" type="checkbox">
<label for="hamburger-toggle" class="hamburger-menu">
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
</label>
<nav id="menu">
<ul>
<li><a href="#">Menu 1</a></li>
<li><a href="#">Menu 2</a></li>
</ul>
</nav>
Now, the CSS could look like this:
#hamburger-toggle,
#menu {
display: none;
}
#hamburger-toggle:checked ~ #menu {
display: block;
}
While this may seem to work, the main problem with a menu like this is that is does not provide keyboard support (you cannot open the menu and close by using keyboard only) and you won’t be able to toggle the aria attributes if the state is handled by CSS only. The aria attributes are important, because they can inform screen readers that hidden element needs to become visible. Of course it all depends on your audience, not everybody needs their website 100% accessible.
On the other hand, I think, if you want to keep a menu like this CSS only, and do the absolute minimum in terms of accessibility, you should at least add the keyboard support, which is possible with a few hacks. BTW, I noticed that even some people without disabilities navigate through website with keyboards only, or at least they do not use their mouse if they don’t have to.
So, instead of making the checkbox display: none, you could set it’s opacity to 0, and create a fake outline around the button whenever the checkbox is selected. You will also need to set the checkbox to be positioned absolute. This is because opacity only hides an element without affecting its layout.
#hamburger-toggle {
opacity: 0;
position: absolute;
left: -9999px;
}
#hamburger-toggle:focus ~ .hamburger-menu {
outline: 2px solid white;
outline: auto;
outline-offset: 4px;
}
Now, because the checkbox is no longer display none, but rather just visually invisible, it can still be triggered using keyboard. This is good enough to allow for a basic keyboard interaction. Although, if you want to make your menu more accessible you will have to take this a step further.
Accessible menu with JavaScript
Don’t get me wrong, I am not saying that using the CSS only way is wrong, because like I mentioned before, some sites do not need to be 100% accessible, and adding the hacky keyboard support would be enough for them. I still use the CSS only solution for hamburger menu on some sites and it’s been working good for me. Although, some websites need a good accessibility support.
What does it mean to make a menu fully accessible?
- keyboard support
- proper aria attributes
- using proper html5 tags
Actually in the example above we are already using the proper html5 tags for the menu (nav), and so it will be recognized by screen readers as a menu. Although we still need to add the proper aria tags and a better keyboard support. In order to do so, we can make our HTML and JavaScript look like this:
<button class="hamburger-menu" aria-expanded="false" aria-controls="menu">
<span class="hamburger-line"></span> <span class="hamburger-line"></span><span class="hamburger-line"></span>
</button>
<nav id="menu" aria-label="Main Menu">
<ul>
<li><a href="#" title="Menu Item 1">Menu 1</a></li>
<li><a href="#" title="Menu Item 2">Menu 2</a></li>
</ul>
</nav>
const menu = document.getElementById('menu');
const hamburger = document.querySelector('.hamburger-menu');
if (menu && hamburger) {
hamburger.addEventListener("click", () => {
const isMenuVisible = menu.classList.toggle("active");
hamburger.setAttribute('aria-expanded', isMenuVisible);
});
}
The code above will trigger the aria-expanded attribute whenever you click on the hamburger button, informing screen readers when menu becomes visible. Also, you probably noticed that we replaced the checkbox with a button. Buttons come with keyboard support out-of-the-box and will work with JavaScript. In my opinion, this kind of menu with a little bit of JavaScript is much better in terms of accessibility. You can also add the aria-controls attribute to inform screen readers that the button and the menu are connected.
If you have any ideas how to make it better or any other suggestions, do not hesitate to leave a comment in the section below.