Select
Select allows users to choose a single option 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 | Select the highlighted option and close the dropdown. |
| Escape / Tab | Close the dropdown without changing the selection. |
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="false" | Options container — indicates single selection only. |
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 to the filter input. Selecting an option closes the dropdown and returns focus to the trigger automatically. Escape and Tab also return focus to the trigger.
WCAG 2.2 compliance
1.4.3 Contrast (Minimum) — AAPass
Trigger text and option labels use --diwa-text-primary; meet the 4.5:1 minimum contrast ratio against the component background in both themes.
1.4.11 Non-text Contrast — AAPass
The trigger border (--diwa-border) and selected option indicator (--diwa-accent) meet ≥ 3:1 contrast against adjacent surface colours.
2.1.1 Keyboard — APass
Full keyboard navigation: Tab opens, arrow keys highlight options, Enter/Space selects, Escape closes without selection change.
2.4.7 Focus Visible — AAPass
A tokenized focus outline (var(--diwa-focus-ring-width) solid --diwa-border-focus) is shown on the trigger via :focus-visible.
4.1.2 Name, Role, Value — APass
role="combobox", role="listbox", role="option", aria-expanded, aria-haspopup, aria-selected, 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. - Error messaging — always pair
state="error"with a meaningfulmessageprop — colour alone is insufficient for users who cannot distinguish red from other colours.