What I Learned

About How :focus-within Doesn't Quite Work in Safari

It has been a long, long time since I implemented drop-down menus for a website. In my last job, all the websites had them and there was well-used code that we applied to this problem. But that was in the before times. In 2023, how does one go about creating drop-down (fly-out, some say) menus for a website if you’re not automatically going to fall back on a framework or a library?

I remember, several years back, one of the developers I worked with attempted to do this with no/minimal JavaScript by using the :focus-within pseudo-class. At the time, this was a relatively new approach that was not supported in all cases. The attempt was abandoned in favor of our “tried-and-true” implementation, but I did not forget the attractiveness of a possible CSS-only solution. Today, with the pending need for dropdown menus on websites in my current job, I decided to give it another go.

My initial attempt was as shown in the CodePen below. The website for which I am developing this menu is based on Drupal, so the HTML for the menu is an unordered list of links inside a <nav> element. Drop-down menus are nested unordered lists. Clicking on a top level option places the focus on the link; CSS styles for the list item containing the link use the :focus-within pseudo-class to display the nested unordered list which is hidden by default.

See the Pen CSS-Only Dropdown Menu with :focus-within (part 1) by Aaron Pinero (@aaronpinero) on CodePen.

In Firefox, this simple implementation works fine. However, it fails in Safari, and I cannot figure out why. In Safari v16, if you open the developer tools and force the :focus-within state on the list item, the dropdown will appear. But either the link will not capture focus when clicked or the focus on the link does not trigger the :focus-within state on the list item.

There is a fix for this. If I add the attribute tabindex="-1" to the link, then everything functions correctly.

I wish that had been the end of the story.

Accessibility of this approach is a concern. I want to make sure that our website have accessible dropdown menus. A brief Google search resulted in a few articles describing how to create accessible dropdown menus:

These examples of accessible dropdowns all use a <button> element as the trigger for the menu. As I understand it, a button is more semantically correct in this instance and provides a better user experience to users browsing with the help of various assistive technologies. So I replaced the link in my example with a button.

See the Pen CSS-Only Dropdown Menu with :focus-within (part 2) by Aaron Pinero (@aaronpinero) on CodePen.

Unfortunately, Safari once again has a problem. Here I thought a button element should be more agreeable for capturing focus and triggering the :focus-within state on the parent list item element. Instead, the situation is worse. I cannot get the button to show the dropdown in Safari, no matter what attributes I add.

For now, I am going to try using a link with the tabindex="-1" and role="button" attributes to see if that will provide the desired level of accessibility. However, it would be nice if Safari would simply handle the :focus-within state as well as Firefox does.

And no, I have not even tried Chrome yet. *sigh*