Skip to content
Devix Open Source

Guide

The keyboard and screen readers

The most used jQuery time picker has had an issue titled "Time Picker accessibility issues" open for years, and the usual answer elsewhere is a hidden <select>. This one is built from real controls.

The field

A real <input type="text"> — so a paste, a password manager and a phone keyboard all work — marked up as a combobox:

Attribute What it does
role="combobox" It is a field with a list attached.
aria-expanded Whether the list is showing.
aria-controls Points at the list.
aria-activedescendant Which row the keyboard is on, without moving focus off the field.
aria-autocomplete="list" Typing narrows what is offered.
aria-invalid Set when what was typed is not a time, or not an available one.
inputmode="numeric" The right keyboard on a phone.

Focus never leaves the field while the list is open, so what you type keeps going where you are looking.

The keyboard

Key What it does
↓ ↑ Open the list, then move through the times that are available — stepping over the ones that are not.
Page Down / Page Up Move by four steps.
Home / End The first and last time on offer.
Enter Take the highlighted time; or, with nothing highlighted, read what you typed.
Escape Close the list and keep the value.
Tab Close the list and move on.

Tabbing into the field does not open the panel. A keyboard user moving through a form has not asked for a list yet, and one that opens by itself is something to escape from rather than a help. Clicking the field does open it, because that is what a click is for.

Enter with the list open applies the time and stops there. It does not submit the form the picker is sitting in — a small thing that pickers mounted inside forms get wrong.

The list

  • role="listbox" with one role="option" per time and aria-selected on the chosen one.
  • Times that cannot be picked carry aria-disabled="true" and a visually hidden reason — "fully booked", "Not available" — so the row is not just mysteriously dead.
  • They stay in the list rather than disappearing. A list with the middle missing tells nobody anything; a list with 12:30 crossed out says the place is shut for lunch.
  • A role="status" live region announces a refusal and each nudge of the arrow keys.

On a phone

Small touch screens get a bottom sheet: full width, at the bottom where a thumb is, with rows at least 44px tall. Not a dropdown pinned to a field that the on-screen keyboard covers — which is the subject of open bugs in both of the libraries this replaces.

Seeing it

  • Focus rings on every control, never colour alone.
  • @media (forced-colors: active) gives the panel system borders and Highlight for the active row.
  • The sheet's entry animation is behind prefers-reduced-motion.
  • Unavailable rows are struck through as well as greyed, so the difference is not only contrast.
Updated 12 Sep 2026