How to build an accessible WheelPicker in React

The column is a role="listbox" with role="option" rows, is focusable, and points aria-activedescendant at the selected row, so assistive technology tracks the selection without moving DOM focus per row. Selection follows focus — ArrowUp/ArrowDown, Home/End and PageUp/PageDown move the selection and scroll to match — which is the APG single-select listbox variant that needs no explicit commit; the pattern is deliberately not declared as `apgPattern: listbox` because that guard requires an Enter key this variant has nothing to bind. The wheel itself is CSS scroll-snap, so touch momentum and rubber-banding are the platform's own and remain correct under assistive gestures. Smooth scrolling is applied only under prefers-reduced-motion: no-preference. The selection band is decorative and aria-hidden.

When to use a WheelPicker

When not to use it

Keyboard interactions

Role listbox, verified at WCAG 2.2-AA.

Common mistakes

Avoid: Rendering it uncontrolled and reading the value from the DOM

Prefer: Pass `value` and `onValueChange`

The component is controlled; without a `value` it cannot scroll to the current selection or reflect an external change

Avoid: Leaving `ariaLabel` off a wheel with no visible label

Prefer: Label every column — "Hour", "Minute"

A listbox with no accessible name is announced only as a list, so the user cannot tell which field they are in

Example

<WheelPicker
  ariaLabel="Hour"
  value={hour}
  onValueChange={setHour}
  options={[
    { value: '09', label: '09' },
    { value: '10', label: '10' },
    { value: '11', label: '11' },
  ]}
/>

See the full WheelPicker reference →