Dropdown
How the Dropdown works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.
The Dropdown follows the WAI-ARIA select-only combobox pattern: a button with role="combobox" that opens a listbox. Focus stays on the trigger throughout, and aria-activedescendant tells assistive tech which option is active.
Keyboard#
Moves focus to the trigger. From an open menu, closes it and moves on.
Opens the menu. While open, moves to the next option, skipping disabled ones.
Opens the menu. While open, moves to the previous option.
Opens the menu. While open, chooses the active option and closes it.
Does the same as Enter.
While open, moves to the first option. End moves to the last.
While open, moves to the next option starting with the letters typed.
Closes the menu without changing the value.
Semantics#
role="combobox"On a <button>, with aria-haspopup="listbox". Announced with its label and current value.
Triggeraria-expandedTrue while the menu is open.
Triggeraria-controlsPoints at the listbox.
Triggeraria-activedescendantThe id of the active option, so it is announced while focus stays on the trigger.
Openaria-labelledbyThe label and the trigger itself, so it is read as Role, Designer.
Triggerrole="listbox" / "option"Each row is an option, with aria-selected on the chosen one and aria-disabled on unavailable ones.
Menuaria-invalid, aria-describedbyAs for the Input.
Hint or errordisabledThe trigger leaves the tab order and the menu does not open.
DisabledColour contrast#
Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.
Value and options · Trigger, Menu
The value, option labels and chevron in #2a2a2a, and descriptions in #6d6d6d, on white and the 4% active-row wash. All pass. The option badge is #005cf8 on #ebf2ff and passes.
WCAG 1.4.3 Contrast (Minimum) ↗Placeholder · Empty
#8f8f8f, 3.23:1 at rest. It darkens to #2a2a2a while the trigger has focus or the menu is open.
WCAG 1.4.3 Contrast (Minimum) ↗Trigger boundary · Default, Hover
The 1px #e1e1e1 stroke, 1.31:1. The chevron (#2a2a2a) also shows it is a control, which helps.
WCAG 1.4.11 Non-text Contrast ↗Active option · Open
The active row is a 4% wash with a #f2f2f2 hairline, far under 3:1 against the menu. A keyboard user may not see which row is active; the chosen row's tick is #005cf8 and passes.
WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗Focus indicator · Focus visible
A 2px ring in #1a6ef8 off a white gap, on keyboard focus only. It measures 4.54:1 on white, so it clears 3:1 on every light surface the library uses.
WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗Disabled · Disabled
Inactive controls are exempt. The disabled text and glyphs (#cbcbcb) are deliberately faint.
WCAG 1.4.3 Contrast (Minimum) ↗Measured contrast#
The trigger against its white fill and hover wash, and each option against the white menu and the active row's 4% wash, from the ds.css tokens; text is held to 4.5:1 and the stroke and chevron to 3:1. 4 of 21 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.
Motion#
- The trigger's fill and stroke change over 150ms, and the chevron turns over 200ms when the menu opens. Both are turned off under prefers-reduced-motion.
- The menu appears without a transition.
Checklist#
Give the dropdown a visible label; the listbox is named by it too, so without one the menu has no name.
Home, End and typeahead work only while the menu is open. Alt + ↓ is not handled; ↓ alone opens it.
The active row's wash is too faint to see from the keyboard. It needs a stronger fill or a visible edge at 3:1 before the code library ships.
Placeholder (#8f8f8f, 3.23:1) and the resting stroke (#e1e1e1, 1.31:1) fall under their minimums, as on the Input.
Disabled options stay in the list and are announced as unavailable. Say why in the description where it is not obvious.
For lists people search, use an editable combobox with filtering instead; this one has typeahead but no text field.
