When trying to find a way to create a navbar, you will notice that there is no semantic way to make one with good UX in both mobile and desktop. The reason is simple, we want an element that is collapsed in mobile and expanded in desktop. Like what you see in my website. Because that’s what we usually see on most websites.
The implementation for responsive navbars are usually one of the following — ordered by the number of times I see it on the internet
div-soup with a front-end JavaScript framework
I notice this the most, and to be frank, it’s usually more accessible than most people think due because most use of aria-expanded attribute and role property.
The cons are following:
Bloat
I don’t mean it in just in file size, the page often takes a long-time load, especially when I am on mobile data. The types of sites that does this are typically restaurants and grocery websites.
Buggy
It is buggy more often than not when resizing the window. I resize webpages a lot since I use a tiling window manager on my laptop.
When on mobile, it is buggy, probably because the JavaScript takes a long time to load/run. I almost always need to wait a few seconds before it even notice that I tapped on something.
At least I learnt some patients this way because I know better to not tap on again just because it’s not doing anything.
This one is a simple hack, and it works well, but it’s usually not accessible.
The idea is to create a label with an icon (typically hamburger menu), and hide the checkbox. The checkbox can toggle if you press on the label, and CSS is used to style the sibling element when the checkbox is toggled.
The styling is done in many ways, the best way I know is to position the navbar off-screen and move it into the view when the checkbox is toggled.
From the link, this is the first example and it works well.
nav {
position: absolute;
top: 0;
left: -300px; /* width of the menu */
width: 300px;
height: 100vh;
transition: 0.3s;
}
#menuToggle:checked + label + nav {
left: 0;
}
Moving the navbar on and off-screen is better than hiding it because screen readers can still navigate into the navbar without toggling anything.
The only con I can think of is:
It’s not Accessible
This is for multiple reasons.
- The Label would be just a CSS with three lines that does not describe what it is, i.e, an empty label, so it’s confusing to navigate into with assistive tech. Devs with more knowledge are now adding visually hidden text or CSS alt texts for content, or just adding a visible
Menu
label. - Even if the checkbox is labelled, it wouldn’t announce that something has changed in the screen because CSS cannot set
aria-expanded. - Not everyone can tap on small buttons on the top corner, even the able personals.
Semantic Details Tag
HTML has plenty of interactive elements, like dialog, select elements, and popover attribute.
The details tag is one of the oldest one that works is most browsers.
For example, this snippet produces an interactive disclosure element.
<details>
<summary>Spoiler</summary>
This is a pure HTML interactive element.
</details>
Spoiler
This is a pure HTML interactive element.
But what if you want to show only one menu at a time? You can use the `name` attribute, though [it doesn't work](https://caniuse.com/mdn-html_elements_details_name) on the older browsers.
<details name="accordion-menu">
<summary>First interactive menu</summary>
This is another pure HTML interactive element.
</details>
<details name="accordion-menu">
<summary>Second interactive menu</summary>
This is yet another pure HTML interactive element.
</details>
First interactive menu
This is another pure HTML interactive element.
Second interactive menu
This is yet another pure HTML interactive element.
With a bit of CSS, you can make it a menu that can overlay elements below it like a typical menu.
/*
* I will be using this website's styles instead of this style my demo because,
* I want it to work in all themes I have, but this CSS will give you a working menu.
* You should be able to make it pretty~
*/
.navbar-nav {
display: flex;
align-items: center;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
flex-wrap: wrap;
}
.nav-dropdown {
position: relative;
}
.nav-dropdown summary {
list-style: none;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 1rem;
}
.nav-link:hover {
background-color: #23F;
color: #FF2;
}
.nav-dropdown .dropdown-menu {
list-style: none;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
min-width: 220px;
background-color: #fff;
border: 2px solid #555;
border-radius: 10px;
padding: 1rem;
margin-top: 0;
z-index: 1000;
}
<nav class="navbar-nav">
<details class="nav-dropdown" name="interactive-menu">
<summary class="nav-link">Menu 1</summary>
<ul class="dropdown-menu" role="menu">
<li>Menu Item 1</li>
<li>Menu Item 2</li>
</ul>
</details>
<details class="nav-dropdown" name="interactive-menu">
<summary class="nav-link">Menu 2</summary>
<ul class="dropdown-menu" role="menu">
<li>Menu Item 1</li>
<li>Menu Item 2</li>
</ul>
</details>
</nav>
Menu 1
Menu 2
Cool, huh? Now For mobile, we can just wrap the nav inside another details tag because HTML allows it:
<details class="nav-dropdown">
<summary class="nav-link">Mobile Menu</summary>
<nav class="navbar-nav mobile-nav">
<details class="nav-dropdown" name="interactive-menu">
<summary class="nav-link">Menu 1</summary>
<ul class="dropdown-menu" role="menu">
<li>Menu Item 1</li>
<li>Menu Item 2</li>
</ul>
</details>
<details class="nav-dropdown" name="interactive-menu">
<summary class="nav-link">Menu 2</summary>
<ul class="dropdown-menu" role="menu">
<li>Menu Item 1</li>
<li>Menu Item 2</li>
</ul>
</details>
</nav>
</details>
Mobile Menu
Menu 1
Menu 2
If you are not on a Mobile device, Resize this browser window or open this page in a mobile to see what the mobile menu will look like.
It still needs JavaScript
You will notice that the menu stays open even if you click outside, that’s because the details tag doesn’t handle clicks outside it.
The Navigation will be confusing for most users who are used to click outside or press the Esc key to close the menu.
One way to not have this issue would be to not use details tag, and instead use popover or the dialog element I mentioned before. But they do not work in most browsers presently used (at the time of writing) because people don’t always update their browsers. So I am fine with that inconvenience. Just click the menu again to dismiss.
We can handle the clicks with JavaScript.
window.addEventListener("click", function (event) {
// if clicks are not on the menu button itself
if (!event.target.closest(".nav-dropdown")) {
// Hide all the menu if open.
Array.from(document.querySelectorAll('.nav-dropdown[open]')).forEach(
menu=>menu.removeAttribute("open"))
}
});
That will work, but JavaScript is disabled on this site, so you can’t test it here.
Needs duplication of navbar
To switch between desktop and mobile navigation, if you have many items in the navbar, it is common practice to make the navbar itself a menu. Unfortunately, there is no way to show the contents of a detail tag when it is closed using any means. So the best possible way would be to create two navbars and show/hide them using media queries.
You may ask why the duplication of navbar is an accessibility issue when we can hide it. Well, the eye candy CSS can be disabled for people who don’t need it. There can be network issues, or some plugin might have blocked it. Or your CSS file might be corrupted, or visitors might be using a text-only browser, the list goes on.
Neat hidden Attribute
This section was added on 2026-01-20
One way to tackle hiding an element in all browsers is by using the hidden global attribute. It can be overwritten easily using CSS by styling it with display CSS property with any value besides none (i.e display:block will show the element regardless of its hidden HTML attribute).
Chris Coyier wrote on CSS tricks that The
. But it is one of the best ways to optionally element when the browser does not load CSS or JavaScript.hidden Attribute is Visibly Weak
Unfortunately, even the feature-rich text-based browsers I tried does not treat the hidden attribute as a global one. If you are one of the developers who work on such browsers, please add support for it.
The Best Navbar
The best JS-free, responsive, semantic, and accessible navbar is to not have a navbar. Yes, you read that correctly.
Instead of a navbar, there are two ways I can think of:
Make it a Navblock
A navblock is a list of links on the top (header), or bottom (footer), of the page. I have seen many government websites doing this. It’s mostly because they have too many pages and not everything can be listed without overwhelming the visitor. Instead, show all relevant links somewhere on the page.
Write a sentence
For small blogs and indie sites, it might be better to just write a small paragraph with the things you do and add links to them. For example, in the home page I wrote the following:
This is my personal website containing my blogs, projects, stories, other things I do, and ways to contact me. It partially supports the IndieWeb while keeping accessibility, minimalism, and your privacy in mind.
Adding this in every page will accomplish two things:
- Some description about the author will be present in every page.
- Avoid navbar and free up some screen space.
I am sure you have better ideas, let me know it in using webmention below, or just send an email :)

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.