Accessible hamburger menu

Back to list

Hamburger menus are a great way to hide your menus on smaller screens (particularly if you have large sidebars). The problem is they're usually implemented in a way that's inaccessible to keyboard and screen reader users or requires Javascript to even function at all. This one uses the HTML details tag, so it's easy for accessibility tools to deal with and just needs some CSS to look like a hamburger menu.

This requires the use of 2 images, the hamburger icon and the close icon. They are vectors because we can change their colors with the CSS mask property.

Javascript isn't required for the basic functionality of this menu. However, I've also provided a script that lets people with Javascript enabled exit out of the menu by pressing the escape key. Alternatively, there's a hidden link at the end of the menu that will take you back to the menu toggle button, which saves keyboard users the hassle of reverse-tabbing through all the links just to exit out of the menu.

Demo

Here is a demo with a simple full screen menu and slightly transparent background. Here is another demo with more links in the menu - instead of taking up the full screen, the menu pops out from the side and an overlay appears over the page behind it. And it scrolls! If you're on a computer, you'll need to resize your screen, zoom in, or open up the responsive view mode to see.

Code

As usual, this code is mostly unstyled, so you'll need to fiddle with it to get it to look pretty. Or you can borrow the styling from the demos.

CSS:

:root {
    --background:#FFF;
    --color:#000;
    
    --burgericon:url(/assets/burger.svg); /* Path to burger vector */
    --closeicon:url(/assets/close.svg); /* Path to close vector */
}

#burger {
    width:100%;
    background:var(--background);
    position:fixed;
    z-index:1000;
}

#burger summary {
    list-style-type: none;
    cursor: pointer;
    display:flex;
    align-items:center;
    color:var(--background);
    padding:5px;
}

#burger summary::before, #burger[open]>summary::before {
	height:40px;
	width:40px;
    background:var(--color);
}

#burger summary::-webkit-details-marker {
    display: none;
}

#burger summary::before {
    content: '';
	mask-image:url(/assets/burger.svg);
	-webkit-mask-image:url(/assets/burger.svg);
	mask-size:40px;
	-webkit-mask-size:40px;
}

#burger[open]>summary::before {
    content: '';
	mask-image:url(/assets/close.svg);
	-webkit-mask-image:url(/assets/close.svg);
	mask-size:40px;
	-webkit-mask-size:40px;
}

#burger[open] {
	position:fixed;
}

.skip a {
    position:absolute;
    left:-10000px;
    top:auto;
    width:1px;
    height:1px;
    overflow:hidden;
}
 
.skip a:focus {
    position:static;
    width:auto;
    height:auto;
}

#burger #menu {
    height:calc(100vh - 50px);
    z-index:1000;
    position:relative;
    overflow:auto;
    background:var(--background);
}

#burger #menu ul {
    list-style:none;
    margin:0;
    text-align:center;
}

Javascript:

window.onload = function () {
    let burger = document.getElementById("burger");

    burger.addEventListener("keydown", (event) => {
        let key = event.code;
        if (key == "Escape") {
            burger.removeAttribute("open");
        }
    });
};

HTML:

<details id="burger">
<summary id="toggle">Menu</summary>
<nav id="menu">
<ul>
    <li><a href="">Link</a></li>
    <li><a href="">Link</a></li>
    <li><a href="">Link</a></li>
    <li><a href="">Link</a></li>
    <li class="skip"><a href="#toggle">Close menu</a></li>
</ul>
</nav>
</details>