Description
Dropdown presents a set of options in a collapsed control. The user clicks to reveal the list and selects one option, which collapses the list and displays the selected value.
Form fields with 7 or more options, filter controls, date/time pickers, and anywhere a radio group would occupy too much vertical space.
When options are too numerous to display simultaneously, hiding them in a dropdown reduces visual clutter without sacrificing completeness. The tradeoff is discoverability — users must open the list to see what's available.
Dropdown selects exactly one item. For multiple selections use Multi-Select. For binary on/off use Switch. For a small mutually exclusive set (under 7 options) where all options should be visible, use Radio Button.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Field label | Required | Describes what the user is selecting. Always visible — never use the selected value as a substitute for a label. |
| Selected value | Required | Shows the current selection. Displays placeholder text ("Choose an option") when nothing is selected yet. |
| Dropdown chevron | Required | Indicates the control expands. Direction reverses (↑) when open. |
| Options list | Required | The expanded list of options. Appears below (or above if space is limited) the trigger control. |
| Option item | Required | Each selectable choice. The current selection is visually highlighted in the list. |
Usage guidelines
When to use Dropdown vs other controls
| Situation | Use |
|---|---|
| 2–6 options, space available | Radio buttons — all options visible without interaction |
| 7+ options | Dropdown — list would be unwieldy as radio buttons |
| Space is constrained regardless of option count | Dropdown — even 3–4 options may suit a dropdown in dense UIs |
| Multiple selections needed | Multi-Select — not Dropdown |
| Binary on/off toggle | Switch — not a two-option Dropdown |
Do / Don't
Do
Use a clear placeholder like "Select a country" rather than blank. Users need to understand that an action is required.
Don't
Don't use a dropdown for 2 options that are simple opposites. "Yes / No" is better as a radio group or a checkbox.
Do
Pre-select a default value when one option is almost always correct. An empty dropdown forces unnecessary interaction.
Don't
Don't put critical actions inside a dropdown list. Options that delete or permanently change data should be in a clear, distinct UI — not buried in a collapsed list.
Layout & Spacing
| Element | Spec |
|---|---|
| Trigger height | 38px (same as text input) |
| Trigger padding | --op-space-12 vertical, --op-space-12 horizontal |
| Option item height | 36px |
| Option padding | --op-space-8 vertical, --op-space-12 horizontal |
| Max list height | 280px (scrollable beyond) |
| Border radius (trigger) | --op-radius-md |
| Border radius (list) | --op-radius-md |
| Chevron size | 16×16px |
| Element | Token |
|---|---|
| Trigger background | --op-color-bg-primary |
| Trigger border | --op-color-border-strong |
| Trigger border (focus) | --op-color-interactive-default |
| Selected value text | --op-color-text-primary |
| Placeholder text | --op-color-text-placeholder |
| Options list background | --op-color-bg-primary |
| Options list shadow | --op-shadow-med |
| Option hover background | --op-color-bg-hover |
| Selected option background | --op-color-bg-selected |
Engineering notes
- Custom listbox components require keyboard support for: Enter/Space to open, arrow keys to navigate options, Enter to select, Escape to close, Tab to close and move focus, and type-ahead filtering for long lists. This is significant implementation work — use native
<select>unless styling requirements make it impossible. - The native
<select>can be styled for most design requirements using CSS (appearance: nonefor custom chevron, custom background, border, padding). Falling back to a custom component is rarely necessary. - On mobile devices, native
<select>triggers the platform's native picker (iOS wheel picker, Android bottom sheet). This is a significant usability advantage — don't override it with a custom listbox for mobile users.
Keyboard interaction
| Key | Action |
|---|---|
| Tab | Moves focus to the select control. |
| Alt + Arrow Down / Space | Opens the option list. |
| Arrow Up / Arrow Down | Navigates between options. |
| Enter | Selects the focused option and closes the list. |
| Escape | Closes the list without changing selection. |
| Type a letter | Jumps to the first option starting with that character. |
Why it matters
Custom dropdown implementations consistently fail accessibility audits because they replicate only part of the keyboard contract. Type-ahead navigation, Escape dismissal, and correct announcement of the selected value on close are all routinely missed. The native <select> handles all of this correctly across every platform and screen reader — and it costs nothing to implement.
Required ARIA
| Pattern | Requirement |
|---|---|
| Native select | <label for> or wrapping <label>. Always visible — no placeholder-as-label. |
| Validation error | aria-invalid="true" on the select when invalid. aria-describedby pointing to the error message element. |
| Required field | required attribute on the select. Visually indicate with an asterisk in the label. |
| Custom listbox | Requires role="combobox", role="listbox", role="option", aria-expanded, aria-selected, and full keyboard implementation. |
Things to avoid
- Don't use the placeholder option ("Select a country") as the label. The label must always be visible above or beside the field.
- Don't build a custom dropdown to avoid native
<select>styling limitations without first trying CSS-only solutions (appearance: none, custom background, border, and chevron via background-image). - Don't use
onChangeto submit a form or trigger navigation immediately when a dropdown value changes. This breaks keyboard navigation and surprises users. Require an explicit submit action.