Multi Select
Multi Select allows users to choose one or more options from a filterable dropdown list. Supports keyboard navigation, validation states, and dense mode (compact) for space-constrained layouts.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Move focus to the trigger button. |
| Shift + Tab | Move focus away from the component. |
| Enter / Space / ↓ / ↑ | Open the dropdown when the trigger is focused. |
| ↓ Arrow Down | Move keyboard highlight to the next visible option. |
| ↑ Arrow Up | Move keyboard highlight to the previous visible option. |
| Home | Move keyboard highlight to the first visible option. |
| End | Move keyboard highlight to the last visible option. |
| Enter / Space | Toggle the currently highlighted option. |
| Escape / Tab | Close the dropdown and return focus to the trigger. |
Screen reader behaviour
| Attribute / Property | Value / Behaviour |
|---|---|
role="combobox" | Trigger div — identifies the toggle as a combobox control. |
aria-haspopup="listbox" | Trigger div — announces that activating opens a listbox. |
aria-expanded | Trigger div — reflects whether the dropdown is open (true/false). |
aria-controls | Trigger div — points to the listbox element by ID. |
aria-labelledby | Trigger div — associates the visible label with the combobox. |
aria-required | Trigger div — set when required={true}. |
aria-invalid="true" | Trigger div — set when state="error". |
aria-describedby | Trigger div — points to the message element when visible. |
role="listbox" | Options container — identifies the dropdown as a listbox. |
aria-multiselectable="true" | Options container — indicates multiple selection is allowed. |
role="option" | Each option row — identifies each item as a selectable option. |
aria-selected | Each option row — reflects the current selected state (true/false). |
aria-disabled | Each option row — set on individual disabled options. |
Focus management
When the dropdown opens, focus moves automatically to the filter input so users can type immediately. Keyboard navigation highlights options — each highlighted option receives focus via setFocus(). When the dropdown closes (Escape, Tab, or click-outside), focus returns to the trigger. The reset (✕) button is reachable by Tab when a selection exists.
WCAG 2.2 compliance
1.4.3 Contrast (Minimum) — AAPass
Trigger text, option labels, and message text use --diwa-text-primary; meet the 4.5:1 minimum contrast ratio in both themes.
1.4.11 Non-text Contrast — AAPass
The trigger border (--diwa-border) and active selection indicator (--diwa-accent) meet ≥ 3:1 contrast against adjacent surface colours.
2.1.1 Keyboard — APass
Full keyboard navigation: Tab opens, arrow keys navigate options, Enter/Space toggles selection, Escape closes. The filter input and reset button are also keyboard-operable.
2.4.7 Focus Visible — AAPass
A tokenized focus outline (var(--diwa-focus-ring-width) solid --diwa-border-focus) is shown on the trigger and all interactive elements via :focus-visible.
4.1.2 Name, Role, Value — APass
role="combobox", role="listbox", role="option", aria-expanded, aria-haspopup, aria-selected, aria-multiselectable, and aria-required are all managed automatically.
Best practices
- Reduced motion — all CSS transitions inside the shadow DOM respond to
prefers-reduced-motion: reduceand are suppressed automatically. - Always provide a label — the
labelprop is required for an accessible control. Sethide-labelonly when a visible label cannot fit the layout — the text is still present in the accessibility tree. - Filter performance — the filter input narrows options in real time. Ensure option labels are descriptive enough to be discoverable when typed partially, especially for screen reader users who cannot see the full list.