Home > front end >  How to make a hidden menu with elements focusable with keyboard?
How to make a hidden menu with elements focusable with keyboard?

Time:07-25

.menu > .item:nth-child(n 2) {
  display: none;
}

.menu:hover > .item,
.menu:focus > .item,
.menu > .item:focus,
.item:focus ~ .item {
  display: block;
}

.item:focus {
  outline: 3px solid red;
}
<button>Go from here</button>
<div >
  <a  href="#">One</a>
  <a  href="#">Two</a>
  <a  href="#">Three</a>
</div>

If the cursor is over the menu, everything is tabbed back and forth fine. However, while the menu is hidden, it's not possible to focus none of the hidden elements with Tab. Is it possible to circumvent this?

Worth noting that I'd like it to work even in outdated browsers, so it's not a good idea to resort to modern solutions like :focus-within (but a JS fallback might be okay; in very old or marginal browsers, nth-child wouldn't work anyway and the menu would just be always expanded).

CodePudding user response:

From the accessibility point of view, you should just stop using :hover to display or hide elements. It's bad for several reasons:

  • AS you have noticed, hidden elements aren't reachable with the keyboard. There are solutions, but you are probably going to create illogical and confusing navigation where tabbing back and forth don't make you encounter the same elements in the same order. Having a predictable tab order is crucial for keyboard only users.
  • Blind users don't have any notion of :hover at all and so may experience a capricious menu which appears and disappears without notice. Of course it is never there when you need it.
  • Partially sighted people and those with motor disability may find it difficult to precisely move the mouse to a certain point and follow a precise path, what is commonly called interaction tunnel
  • How :hover behaves on a touch screen ?

For all these reasons, it's better to forget about :hover and switch to an explicit show/hide toggle, where the state (shown or hidden) don't change without a clear user interaction.

CodePudding user response:

The solution is to toggle the hidden part of the menu with an invisible checkbox:

.menu {
  position: relative;
}

.menu > input {
  position: absolute;
  top: 0;
  right: 0;
  opacity: 0;
  z-index: -1;
}

.menu > .item:nth-of-type(1),
.menu > label {
  display: inline-block;
}

.menu > .item:nth-of-type(n 2) {
  display: none;
}

.menu:hover > .item:nth-of-type(n 2),
.menu > input:checked ~ .item:nth-of-type(n 2) {
  display: block;
}

.item:focus, .menu > input:focus ~ label {
  outline: 3px solid red;
}

.menu > input:checked ~ label {
  background-color: magenta;
}
<button>Go from here</button>
<div >
  <a  href="#">One</a>
  <input id="c" type="checkbox" />
  <label for="c">↓</label>
  <a  href="#">Two</a>
  <a  href="#">Three</a>
</div>

Thanks to @QuentinC for pointing into this direction.

  • Related