Synopsis
I’ve been researching what is considered “best practice“ for site header/navigation with submenus or hidden content that is toggled between visibility. Frankly, I think most websites have at the very least some version of toggled navigation for browsers at a mobile size.
I’ve noticed a growing opinion in threads and articles that all site navigation should function without JavaScript. While <1% of users are disabling JavaScript in their browser, it’s theorized to be much higher for users who disable JavaScript on a site-by-site basis. The primary reasons for disabling are: the old myth that JavaScript is insecure; plugins that filter and disable some JavaScript like Ad Blocker; or users trying to bypass paywalls (I've seen a ton of videos on TikTok showing how to disable JavaScript to bypass a paywall).
This means any navigation with toggled content would have to be achieved with pure CSS.
Desktop Approaches for Submenus
(CSS) The Old Checkbox Hack
This is an old one (and more widely browser supported). It uses an input checkbox’s :checked state to toggle content between visibility. The actual input is usually hidden and the label is styled to be the toggle. Here’s the very basic setup:
<!-- HTML -->
<input type="checkbox" id="toggle" class="input-toggle">
<label for="input-toggle">Menu Item</label>
<ul class="submenu">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
/* CSS */
.input-toggle {
visibility: hidden;
margin-top: -999px;
}
.submenu {
display: none;
}
.input-toggle:checked ~ .submenu {
display: block;
}
This approach only works if there is one toggle. If there are multiple siblings with toggle inputs, it will open and close all of them. However, an easy solution would be to just use a different CSS combinator and add some JavaScript so multiple checkboxes aren’t checked at once. (I used jQuery, sorry.)
<!-- HTML -->
<input type="checkbox" id="toggle" class="input-toggle">
<label for="input-toggle">Menu Item 1</label>
<ul class="submenu">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
<input type="checkbox" id="toggle" class="input-toggle">
<label for="input-toggle">Menu Item 2</label>
<ul class="submenu">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
<input type="checkbox" id="toggle" class="input-toggle">
<label for="input-toggle">Menu Item 3</label>
<ul class="submenu">
<li>Submenu Item 1</li>
<li>Submenu Item 2</li>
</ul>
/* CSS */
.input-toggle {
visibility: hidden;
margin-top: -999px;
}
.submenu {
display: none;
}
.input-toggle:checked + label + .submenu {
display: block;
}
// jQuery
$(".input-toggle").change(function () {
$(".input-toggle").not(this).prop('checked', false);
});
Yes, we’re already back to using JavaScript. However, if a user has disabled JavaScript then the navigation would still open the submenus—they would just have the added frustration of opening and closing each of the submenus manually. This approach would only work if none of the submenus overlap or completely hide another submenu. We could try using radio inputs instead, but then one portion of the menu would always be open without additional JavaScript
Support
You may be wondering why would I consider this approach first over :focus-within. The :checked state is more widely supported.
The Challenges
- It can only meet accessibility guidelines by using JavaScript and aria labels to add keyboard functionality because the input tag is only meant to be used inside of a form.
- If a user has JavaScript disabled for any reason, then it won’t meet accessibility guidelines.
- It’s not semantic HTML. This isn’t the intended use for the input tag.
(CSS) Using :focus-within
A bit more of a recent approach. This approach uses :focus-within to toggle content. Here are three examples of how it’s been used:
- https://codepen.io/dannievinther/pen/NvZjvz
- https://codepen.io/scottohara/pen/ybjPEx
- https://codepen.io/una/pen/pVvXmK
Support
This approach is slightly less supported.
The Challenges
- Once again, it can only meet accessibility guidelines by using JavaScript and aria labels to add keyboard functionality.
- If a user has JavaScript disabled for any reason, then it won’t meet accessibility guidelines.
- It at least gives us semantic HTML and the hack is in the CSS.
(JavaScript) Just use JavaScript
I’ve seen a lot of suggestions to just keep top-level links in the top navigation. If a user has JavaScript disabled, then they can still navigate the site by the top links. However, this approach has also been shown to be confusing to users—especially with mixed navigation. It would also only work if the submenus show on hover, which tbh isn’t my favorite. I would rather the user have to click on the top menu label to open the submenu.
Mobile Approach For Just A Menu
Almost every site I visit at a mobile size has a side shelf menu… and they’re not functioning without JavaScript. The CSS approaches above could be tailored for sites at a mobile size, but they’ll still have their challenges.
Conclusion
If I’m to truly develop JavaScript-free navigation, then it seems to me that the best practice would be to just have a simple menu with top links—the pages could then have sub navigation links at the top (though now users are having to click and wait.) At a mobile size, I guess I could possibly have the menu links at the top under the site logo with scroll overflow?... I haven't even really stopped to think about what the user experience on mobile would be like with a setup like that.
I could really use some help and insight into how I should be developing navigation. Is it still ok to use JavaScript?
Edit: After some further research I think I'm just going to keep using JS and include a no-js.css file.
<script src="main.js"></script>
<noscript><link href="no-js.css" /></noscript>