Skip to content

Fix VoiceOver + Safari: menu closes on Down Arrow#791

Open
AntoineGirard wants to merge 1 commit into
alphagov:mainfrom
AntoineGirard:fix-voiceover-arrow-key
Open

Fix VoiceOver + Safari: menu closes on Down Arrow#791
AntoineGirard wants to merge 1 commit into
alphagov:mainfrom
AntoineGirard:fix-voiceover-arrow-key

Conversation

@AntoineGirard

Copy link
Copy Markdown

Fixes #247

Problem

When using VoiceOver on macOS with Safari, Firefox or Chrome, pressing the Down Arrow key while the autocomplete input is focused causes the suggestion menu to close immediately instead of navigating into the list.

The root causes are:

  1. Missing aria-haspopup="listbox" — without this attribute, VoiceOver does not recognise the input as an ARIA 1.2 combobox and falls back to its default behaviour for a plain text input. On Down Arrow, it moves its virtual cursor to the next element in the page rather than navigating the suggestion list.

  2. Race condition in handleInputBlur — when VoiceOver navigates away from the input, a blur event fires before Preact has a chance to commit the setState queued by handleDownArrow. At the time handleInputBlur reads this.state.focused, it still holds −1 (the input), so focusingAnOption is false and the menu is closed.

  3. Spurious click events from VoiceOver — VoiceOver fires synthetic click events (event.detail === 0) as it moves its virtual cursor across options, unintentionally confirming a selection before the user has made one.

This issue has been open since January 2022 and is labelled ⚠️ high priority. PR #348 (superseded by #355) had already identified the combobox role placement; this PR picks up the remaining work.

Changes

aria-haspopup="listbox" added to the input (src/autocomplete.js)

Completes the ARIA 1.2 combobox pattern on the <input> element. The attribute was already partially in place (role="combobox", aria-expanded, aria-controls, aria-autocomplete, aria-activedescendant) but aria-haspopup was missing, preventing VoiceOver from treating the field as a combobox.

handleInputBlurrelatedTarget guard + deferred state read

  • If focus moves to an element inside the component wrapper (e.g. an option receiving real DOM focus), the handler returns early and defers to handleOptionBlur.
  • The rest of the logic is wrapped in setTimeout(..., 0) so that any setState queued by a concurrent keydown handler is flushed before this.state.focused is read. This eliminates the race condition where VoiceOver fires blur before Preact commits the update from handleDownArrow.

handleOptionClick — synthetic click guard

if (event.type === 'click' && event.detail === 0) { return }

Blocks synthetic clicks generated by VoiceOver's virtual cursor. The event.type === 'click' guard is intentional: handleEnter and handleSpace call handleOptionClick with a KeyboardEvent (whose detail is also 0), so keyboard selection is unaffected.

Testing

VoiceOver (macOS) — Safari and Chrome

  • Type into the field → suggestions appear
  • Down Arrow → menu stays open, VoiceOver announces the first option
  • Down Arrow again → moves to next option
  • Enter → option is confirmed, menu closes
  • Escape → menu closes without confirming

Regression — keyboard (no AT)

  • Down Arrow navigates the list
  • Enter confirms, Space confirms (when option is focused)
  • Tab closes menu without confirming (when confirmOnBlur: false)

Regression — mouse

  • Click on an option → selection is confirmed normally
  • Click outside → menu closes

Regression — other screen readers

  • NVDA + Firefox: no regression
  • JAWS + Chrome: no regression

@netlify

netlify Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploy Preview for accessible-autocomplete ready!

Name Link
🔨 Latest commit aa95100
🔍 Latest deploy log https://app.netlify.com/projects/accessible-autocomplete/deploys/6a43b7e6239b0d000853fc1f
😎 Deploy Preview https://deploy-preview-791--accessible-autocomplete.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can’t access autocomplete input using Mac Voiceover modifier key + arrow keys (VO-Right Arrow)

1 participant