Link + Popover Navigation

This is a redress of my 2019 post Link + Disclosure Widget Navigation, except (as the title implies), I’ve modified it to use native HTML popovers instead of ARIA or HTML disclosure widgets.

Popover has the benefit of using appropriate HTML structure and semantics while removing the need for scripting and ARIA. I use some ARIA here regardless.

popover vs. Disclosure Widgets

ARIA-based disclosure widgets (<button aria-expanded>) require script to work. HTML-based disclosure widgets (<details> / <summary>) expand when there is a hit for in-page searches (in two engines), have inconsistent exposure to accessibility APIs (across browsers and over time), and in my experience confuddle teams with styling overrides. I have a 2020 post going into some detail on each.

The popover feature requires CSS for positioning. Cross-browser anchor positioning support only came about recently. Because popovers appear on the top layer, you have to be careful with anything that uses z-index. Only use this pattern if all the dialogs on your site are already using the native HTML <dialog> element, otherwise overlapping issues happen.

The Pattern

Embedded below or visit it directly at Codepen. There’s also a debug version.

See the Pen Link + Popover Nav by Adrian Roselli (@aardrian) on CodePen.

The Container

The navigation lives in a <nav> as a list with nested lists.

<header>
  <nav id="Nav">
    <ul>
    […]
    </ul>
  </nav>
</header>

The links are links. They work with a keyboard, assistive technology knows what to do with them, and links are generally well understood by users.

I identify that the current parent page is the About page and the current page is Job Postings by using aria-current="page". I also use that as my CSS selector and you should too.

<li>
  <a href="[…]" id="Item02" aria-current="page">About</a>
  […]
  <ul id="SubItem02" popover>
    […]
    <li><a href="[…]" aria-current="page">Job Postings</a></li>
  </ul>
</li>

The Button Trigger

As my older pattern, the <button> element is the trigger. Unlike my older pattern, it doesn’t use aria-expanded.

Instead it uses popovertarget, which brings the programmatic state aria-expanded for free. The value of popovertarget must be the id of the content whose appearance you want to control (the popover content). The popover content needs the popover attribute for this to work.

No CSS or JavaScript is needed to make this content appear or disappear.

<button type="button" id="btnItem02" aria-labelledby="Item02" popovertarget="SubItem02">
  <svg […]>[…]</svg>
</button>
<ul id="SubItem02" popover>
 […]
</ul>

Naming the Button

This is the second place I still use ARIA. To avoid text duplication and auto-translation issues, I use aria-labelledby to reference the text of the preceding link. Screen reader users hear / feel a different control type and that it’s expandable, which has proven to be sufficient context. For voice users, saying the visible text presents them with the option to choose the link or the button.

<a href="[…]" id="Item02" aria-current="page">About</a>
<button type="button" id="btnItem02" aria-labelledby="Item02" popovertarget="SubItem02">
  <svg […]>[…]</svg>
</button>

Focus Order Support

You must place the popover content immediately after the popover trigger, otherwise you risk reading order and focus order problems for users. The popover feature does not automatically do any focus management.

Esc Support

It comes for free with popover.

Click-to-Close Support

It comes for free with popover.

Focusout-to-Close Support

You’ll have to write your own.

Styles

Not all of these styles will work for you, but these may matter:

There is a button on the demo to remove all styles from the page, making it easier to see how this will perform as raw HTML, for easy testing, or for CSS Naked Day.

Popover Positioning

I haven’t played with popovers much. Positioning it to work across Firefox, Chromium, and Safari while also not overflowing the window or falling apart with text direction changes proved to be a challenge. I welcome suggestions on improvements.

My goal was to have the popover appear to the right edge of the parent list item and just below the button — for left-to-right languages — and in a corresponding location for other language text directions.

[popover] {
  inset: auto;
  position-anchor: auto;
  inset-block-start: anchor(self-end);
  inset-inline-end: calc(anchor(self-end) - 1em);
}

I flailed about trying different values from the spec and struggled to understand logical properties with anchor positioning. As it is, the popover positioning falls apart with the vertical writing modes, but I can’t be sure if that’s my code or browser bugs (¿Por qué no los dos?).

Pattern History

There are reasons I built this pattern.

Early in 2017 I filed an issue against ARIA Authoring Practices (APG) requesting a change to the menu navigation pattern. Despite a great deal of feedback in agreement, it continues to languish. In late 2017 I wrote Don’t Use ARIA Menu Roles for Site Nav and started actively campaigning against the APG pattern. In 2019, Sarah Higley proposed a disclosure-only APG pattern and then a disclosure and link pattern proposal in 2020.

A plate full of slightly under-baked mini popovers, or tiny Yorkshire puddings, with a web site navigation over the top and a similar vertical navigation down the left.
An under-baked batch of mini popovers I made for my solo Easter during the first year of the pandemic. I still ate them all.

HTML (via Open-UI) has since stepped in to fill some gaps, so here we are.

Wrap Up

Standard disclaimers apply: may not work for your audience, likely has bugs, support can change, new features may moot this, my design sense is poor, yes you have to write script to get whatever custom interactions you think are nifty but which probably annoy users, who even writes code anymore, using [insert random element here] in a way that’s not mentioned in this post is your problem not mine, etc.

And if you have more experience with multilingual popover anchor positioning, please share!

No comments? Be the first!

Leave a Comment or Response

  • The form doesn’t support Markdown.
  • This form allows limited HTML.
  • Allowed HTML elements are <a href>, <blockquote>, <code>, <del>, <em>, <ins>, <q>, <strong>, and maybe some others. WordPress is fickle and randomly blocks or allows some.
  • If you want to include HTML examples in your comment, then HTML encode them. E.g. <code>&lt;div&gt;</code> (you can copy and paste that chunk).