26
loading...
This website collects cookies to deliver better user experience
<noscript>
element allows you to insert HTML if JavaScript can't function. So I could close the menu by default but open it using <noscript>
if JavaScript was unavailable. This sounded like the perfect solution until I read the description on MDN more carefully: The HTML <noscript>
element defines a section of HTML to be inserted if a script type on the page is unsupported or if scripting is currently turned off in the browser
.<noscript>
doesn't work if JavaScript fails to load due to a connection error. Being realistic, hardly anyone is going to turn off JavaScript these days but plenty of people are going to have errors from a bad mobile connection.<script>
element inline with my HTML would ensure that JavaScript can't fail to load due to a connection error. I could combine it with closing the menu by default and a <noscript>
element to apply open menu styles if JavaScript is unsupported. However, there are three trade offs with this approach:<input>
element with a type="checkbox"
attribute gives us access to the checked
attribute whenever it's selected. Unlike a <button>
element, this means that I could determine whether the menu is open without any JavaScript. The code would go something like this:<nav>
<label for="nav-menu-checkbox" hidden>Open menu</label>
<input type="checkbox" id="nav-menu-checkbox" aria-controls="nav-menu-content" hidden>
<ul id="nav-menu-content">
<li>Nav item 1</li>
<li>Nav item 2</li>
<li>Nav item 3</li>
</ul>
</nav>
label[for="nav-menu-checkbox"],
input[type="checkbox"] {
display: inline-block;
}
button[aria-expanded="false"] + ul,
input + ul {
transition-delay: 0ms, 0.25s;
transform: translateY(-100%);
visibility: hidden;
}
button[aria-expanded="true"] + ul,
input:checked + ul {
transition-delay: 0ms;
transform: translateY(0);
visibility: visible;
}
button[aria-expanded] + ul,
input + ul {
transition: 0.25s ease-out;
transition-property: transform, visibility;
}
<label>
and <input>
elements have a hidden
attribute which is overridden by display: inline-block
in the CSS. This ensures that if the CSS isn't loaded there won't be a redundant checkbox that doesn't work. If you're wondering when this would be applicable, read Sara Soueidan's article on optimising content for reader modes and reading apps.aria-controls
attribute on the <input>
linked to the nav menu list. This tells assistive technology that supports it that the two are linked so enhanced functionality can be provided.:checked
pseudo class is combined with an adjacent sibling combinator
to toggle the menu. You could also use the general sibling combinator
if elements are farther apart.visibility: hidden
is used instead of display: none
or opacity: 0
to keep transitions while still removing the menu from the DOM when closed. display: none
would hide the menu before the transition could finish and opacity: 0
would hide the menu after the transition but only visually, causing issues with assistive technology.transition-delay: 0ms, 0.25s
is used when closing the menu to allow the transform to finish before visibility
is set to hidden
. visibility
doesn't fade like opacity
but it is still an animatable property.<button>
with an aria-expanded
attribute because the <input>
and <label>
must be replaced with JavaScript as soon as possible to avoid accessibility issues. <input>
doesn't have the same functionality as <button>
such as being triggered by the enter key or being listed in shortcut menus.<details>
and <summary>
were first supported in 2011 but didn't receive full adoption from all the major browser engines until 2020 when Edge switched to Chromium. However, current support for <details>
and <summary>
is excellent and it's only Internet Explorer that doesn't and will never recognise them, treating them each as a <div>
instead.<details>
isn't animatable — but there is a way around this. Usually, everything within a closed <details>
element except <summary>
is removed from the DOM which doesn't give any time for a closing animation to end before it's removed. This can be avoided by moving the content outside of the <details>
element:<nav>
<details>
<summary aria-controls="nav-menu-content" hidden>Open menu</summary>
</details>
<ul id="nav-menu-content">
<li>Nav item 1</li>
<li>Nav item 2</li>
<li>Nav item 3</li>
</ul>
</nav>
open
attribute on <details>
.<noscript>
element because there were better alternatives, I was left with 4 options:<details>
and <summary>
elements was my next favourite but supporting Internet Explorer meant this wasn't possible.26