The CSS dropdown

It's easy to style a dropdown to open on hover with just CSS:

<li>
    <a href="#">Parent</a>
    <ul>
        <li>
            <a href="#">Child</a>
        </li>
    </ul>
</li>
li ul {
    display: none;
}
li:hover ul {
    display: block;
}

But, as we've already seen, this doesn't work for keyboard navigation. What if we apply the same styles to the focus state? Well, they can't be exactly the same: our hover styles are applied to the <li> but that element isn't interactive so it doesn't have a focus state.

The solution is to hook the styles to the focused <a> instead, targeting its sibling:

li ul {
    display: none;
}
li:hover ul,
li a:focus + ul {
    display: block;
}

At this point, tabbing through the top menu will reveal the dropdowns, but we still can't tab into them. Plus, if you're on Chrome, you won't be able to move past the second menu item as you will keep getting redirected to the first link on the page. Whoops.

What's happening here? We attached the CSS that makes the dropdown visible to the focused state of the parent anchor. But this CSS will only be applied while that anchor is focused. As soon as we tab away from it, the dropdown gets hidden again.

In addition to that, we're using display: none; to hide the dropdown, so its child anchors become unfocusable as soon as they are hidden. This explains Chrome's strange behaviour: when we tab away from the parent anchor, the next element in the tab order is the first child anchor in the dropdown, but as soon as we leave the parent anchor the child anchor becomes unfocusable. Chrome's default behaviour in this situation is to move focus back to the top of the page. Firefox moves focus to the next element, which at least avoids creating a keyboard trap. Even so, we need to find a better solution.