Stockio

Accessible Dropdown Menus: Use Disclosure Navigation

Build dropdown site navigation that works with keyboards, touch, and screen readers without imposing the complex behavior of an application menu.

By Theo Okafor

UX Researcher ·

On this page

Site navigation often borrows the word menu from restaurant lists and desktop applications, but those interfaces do not share one interaction model. A header containing links to product pages usually needs ordinary link navigation with expandable sections. Giving it application-menu roles can make familiar links harder to discover and creates keyboard responsibilities the implementation may not fulfill.

Distinguish navigation from application commands

Start by identifying the content. Links to pricing, documentation, and account pages are navigation destinations. Commands such as changing a drawing tool may justify an application-style menu. Choose semantics from the task, not from the fact that a panel appears beneath a button.

Use the Flexbox Playground to prototype header wrapping and spacing while preserving a logical source sequence. Keep each disclosure button adjacent to the links it controls. This makes source order easier to understand when styles are unavailable or the header collapses on a small screen.

The WAI disclosure navigation example explains why typical site navigation does not need menu and menuitem roles. Its examples also emphasize testing assistive technology support rather than copying demonstration code without verification.

If a heading both navigates and expands, split those actions into separate controls. A Products link can lead to the overview while an adjacent button expands product links. Do not make the first click expand and the second click navigate; that hidden rule is difficult to discover on touchscreens and through voice control.

<nav aria-label="Main">
<ul>
  <li>
    <button type="button" aria-expanded="false"
            aria-controls="product-links" id="product-toggle">
      Products
    </button>
    <ul id="product-links" hidden>
      <li><a href="/products/editor">Editor</a></li>
      <li><a href="/products/reports">Reports</a></li>
    </ul>
  </li>
</ul>
</nav>

The button supplies native Enter and Space activation. The links remain ordinary Tab stops when visible. There is no need to add a roving tabindex system, arrow-key-only navigation, or a menu role simply because the design resembles a desktop dropdown.

If you include an arrow icon, keep it decorative unless it conveys information missing from the button's state. A screen reader should announce the control's name and expanded state, not a filename or a redundant description of a down-pointing triangle.

Synchronize visibility and expanded state

The disclosure has two representations of one fact: whether the panel is available. Update both together. A collapsed panel must not leave invisible links in the Tab sequence, while an expanded panel must expose its content to assistive technology.

const toggle = document.querySelector('#product-toggle');
const links = document.querySelector('#product-links');
function setOpen(open) {
toggle.setAttribute('aria-expanded', String(open));
links.hidden = !open;
}
toggle.addEventListener('click', () => {
setOpen(toggle.getAttribute('aria-expanded') !== 'true');
});
links.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
  setOpen(false);
  toggle.focus();
}
});

Avoid CSS that accidentally overrides the hidden attribute with a permanent display rule. If you animate closing, ensure content stops being interactive at the right time rather than leaving a fading but focusable panel in the page. Simpler immediate visibility changes are often more robust than elaborate transitions.

The MDN aria-expanded reference explains how the state belongs on the controlling element. It does not belong only on the panel, where users activating the button would miss it.

Make dismissal predictable across input methods

For a disclosure, opening normally leaves focus on the trigger. The next Tab reaches the first revealed link. This preserves ordinary page navigation and avoids unexpectedly sending the user into a panel they may only be exploring. Closing from inside with Escape should return focus to the trigger.

Clicking outside can close the panel as an extra convenience. It must not be the only dismissal route. Be cautious about closing on blur alone: focus briefly moves during many legitimate interactions, and a badly timed handler can remove the link before it activates.

Hover support should supplement clicking, not replace it. If content appears on hover or focus, consider the WCAG requirements for dismissibility, hoverability, and persistence. Users must be able to move the pointer into the revealed panel without crossing a tiny gap that immediately hides it.

Multiple open sections can be valid, as can a one-open-at-a-time rule. Whichever you choose, do not close a section that currently contains keyboard focus without moving that focus somewhere sensible. Removing the active element creates an avoidable interruption and makes the next Tab destination unpredictable.

Verify responsive and assistive technology behavior

Test the header before and after the mobile breakpoint. A collapsed mobile navigation button needs its own expanded state and a clear relationship to its panel. Switching breakpoints should not leave a focused desktop link hidden inside an unavailable mobile region. Resize while focus is inside an open section to expose these state bugs.

Use the contrast checker for text and focus styling on each header surface. Check that selected-page styling is not communicated by color alone. Where appropriate, aria-current="page" identifies the current destination independently of the disclosure's expanded state.

Run a task that opens a section, follows a link, returns, and opens another section. Repeat using only the keyboard and then a screen reader. Listen for clear button names, collapsed or expanded announcements, and the expected number of ordinary links. A navigation landmark label should distinguish this header from footer or account navigation.

The WCAG content on hover or focus guidance helps assess floating panels. Store the chosen behavior with other accessibility patterns, including Escape handling, breakpoint transitions, and whether multiple sections may stay open. Those details are where inconsistent implementations usually emerge.

Make failure and enhancement states usable

The minimal example assumes its script initializes successfully. If the panel begins hidden and JavaScript fails, those destinations disappear altogether. Plan a progressive enhancement strategy: render essential links visibly by default, then activate collapsing only after the controller is ready, or provide an always-available overview page containing the same destinations. Do not leave a working-looking toggle that does nothing.

When a framework hydrates the header, keep initial visibility and expanded state consistent between the server response and the client. A mismatch can briefly expose hidden links or move focus during initialization. Test a slow connection and a failed script request, not only the fully loaded application. Critical navigation should remain discoverable even when nonessential enhancement fails.

Use a short interaction checklist for every header variant. Open Products with Enter, Tab through its links, close with Escape, then reopen with Space. Move the pointer between the trigger and panel, zoom until the header wraps, and change orientation while a link has focus. Confirm that each action preserves a visible focus target and an accurate expanded announcement.

Finally, test translated labels and a larger default font. A trigger that fits in English may wrap onto two lines, altering hover boundaries and panel placement. Prefer layout that tolerates that change over fixed coordinates calculated for one screenshot. Good disclosure behavior depends on the rendered content, not just the original component dimensions.

Key takeaways

  • Use disclosure buttons for ordinary expandable site navigation.
  • Keep navigation destinations separate from expansion actions.
  • Synchronize aria-expanded with real content visibility.
  • Preserve ordinary Tab navigation and predictable Escape behavior.
  • Test hover, touch, keyboard, and responsive state transitions separately.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in Accessibility
Accessibility

Keyboard Navigation Testing: Fix Focus Order and Traps

Build a repeatable keyboard testing workflow that catches unreachable controls, confusing focus order, and keyboard traps before your next release.

Includes: Flexbox playgroundNoor HaddadNoor Haddad
HTML & CSS

10 Best Online Code Playgrounds Like CodePen

Compare ten browser-based places to write, test, and share code, from quick HTML experiments to full-stack demos, with practical advice for choosing the right workspace.

Includes: Flexbox playgroundJonah WeissJonah Weiss
Motion & Animation

10 Best JavaScript and CSS Animation Libraries

Compare ten web animation libraries for interface feedback, scrolling effects, and expressive motion. Find a practical match for your coding comfort, project size, and performance needs.

Includes: Flexbox playgroundAiko TanakaAiko Tanaka
UI Design

Search Bar and Filter UI Design for Catalogs

Create search and filter interfaces with clear scope, predictable application rules, useful result feedback and responsive layouts for catalogs and dashboards.

Includes: Flexbox playgroundMaya LindqvistMaya Lindqvist
JavaScript & Frameworks

Interactive Documentation: Why Live Code Demos Win

Design fast, accessible documentation demos that start with readable static examples, add focused controls, and measure whether readers complete real tasks.

Includes: Flexbox playgroundDaniel KimDaniel Kim
JavaScript & Frameworks

shadcn vs Radix vs Headless UI: UI Libraries Compared

Compare Radix, Shadcn, and Headless UI through ownership, accessibility, styling, and maintenance, then choose a practical foundation for your next interface.

Includes: Flexbox playgroundSofia MarchettiSofia Marchetti