Select
Dropdown select with searchable options, keyboard navigation, and full ARIA listbox semantics for accessible form inputs.
<arc-select> Overview
>
The Select component replaces the native `<select>` element with a fully styled, accessible dropdown that integrates seamlessly with ARC UI's design tokens. It provides built-in type-ahead search filtering, allowing users to quickly locate options in long lists without scrolling.
Select implements the ARIA listbox pattern, ensuring screen readers announce the current selection, available options, and navigation cues. Keyboard users can open the dropdown with Enter or Space, navigate with arrow keys, filter by typing, and confirm a choice with Enter — all without reaching for a mouse.
Use Select any time you need a single-choice dropdown in a form: assigning a team member, choosing a category, picking a status, or selecting a locale. For multi-choice scenarios, reach for MultiSelect instead. For short lists of three or fewer visible options, consider RadioGroup for faster scanning.Guidelines
When to use
- Always provide a visible label so users understand what they are selecting
- Use a meaningful placeholder like "Choose a team member..." rather than generic "Select..."
- Keep option labels concise — ideally under 40 characters
- Order options logically (alphabetical, by frequency, or by workflow step)
- Pre-select a sensible default when one exists to reduce interaction cost
- Use the disabled state to indicate temporarily unavailable choices (e.g. permissions)
When not to use
- Do not use Select for fewer than 3 options — use RadioGroup for better scannability
- Do not nest selects inside other selects — flatten the hierarchy or use a staged flow
- Do not rely solely on placeholder text as a label — placeholders disappear once a value is chosen
- Do not use Select for navigation — use NavigationMenu or tabs for moving between views
- Do not disable the component without explaining why — pair disabled state with a tooltip or helper text
Features
- Type-ahead search filtering narrows options as the user types
- Full keyboard navigation: Arrow keys, Home, End, Enter, Escape
- ARIA listbox role with active-descendant tracking for screen readers
- Supports placeholder text for empty-state guidance
- Controlled and uncontrolled value modes
- Disabled state prevents interaction and conveys unavailability visually
- Automatic scroll-into-view for the highlighted option in long lists
- Works with dynamic option lists — update the options array at any time
- Consistent styling across browsers via Shadow DOM encapsulation
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<!-- Basic select -->
<arc-select label="Assign to team member" placeholder="Choose a person...">
<arc-option value="alice-chen">Alice Chen</arc-option>
<arc-option value="bob-martinez">Bob Martinez</arc-option>
<arc-option value="carol-nguyen">Carol Nguyen</arc-option>
<arc-option value="david-okafor">David Okafor</arc-option>
<arc-option value="eva-johansson">Eva Johansson</arc-option>
</arc-select>
<!-- Pre-selected value -->
<arc-select label="Status" value="in-progress">
<arc-option value="open">Open</arc-option>
<arc-option value="in-progress">In Progress</arc-option>
<arc-option value="review">In Review</arc-option>
<arc-option value="done">Done</arc-option>
</arc-select>
<!-- In a native form -->
<form action="/api/assign" method="post">
<arc-select name="assignee" label="Assignee" placeholder="Pick someone...">
<arc-option value="alice">Alice</arc-option>
<arc-option value="bob">Bob</arc-option>
</arc-select>
<button type="submit">Save</button>
</form>
<script>
document.querySelector('arc-select')
.addEventListener('arc-change', (e) => {
console.log('Selected:', e.detail.value, e.detail.label);
});
</script> import { Select, Option } from '@arclux/arc-ui-react';
import { useState } from 'react';
function TeamAssigner() {
const [assignee, setAssignee] = useState('');
return (
<Select
label="Assign to team member"
placeholder="Choose a person..."
value={assignee}
onArcChange={(e) => setAssignee(e.detail.value)}
>
<Option value="alice-chen">Alice Chen</Option>
<Option value="bob-martinez">Bob Martinez</Option>
<Option value="carol-nguyen">Carol Nguyen</Option>
<Option value="david-okafor">David Okafor</Option>
<Option value="eva-johansson">Eva Johansson</Option>
</Select>
);
}
function StatusPicker() {
return (
<Select label="Status" value="in-progress">
<Option value="open">Open</Option>
<Option value="in-progress">In Progress</Option>
<Option value="review">In Review</Option>
<Option value="done">Done</Option>
</Select>
);
} <script setup>
import { Select, Option } from '@arclux/arc-ui-vue';
import { ref } from 'vue';
const assignee = ref('');
</script>
<template>
<Select
label="Assign to team member"
placeholder="Choose a person..."
:value="assignee"
@arc-change="assignee = $event.detail.value"
>
<Option value="alice-chen">Alice Chen</Option>
<Option value="bob-martinez">Bob Martinez</Option>
<Option value="carol-nguyen">Carol Nguyen</Option>
<Option value="david-okafor">David Okafor</Option>
<Option value="eva-johansson">Eva Johansson</Option>
</Select>
</template> <script>
import { Select, Option } from '@arclux/arc-ui-svelte';
let status = 'open';
</script>
<Select label="Task status" value={status}
on:arc-change={(e) => status = e.detail.value}>
<Option value="open">Open</Option>
<Option value="in-progress">In Progress</Option>
<Option value="review">In Review</Option>
<Option value="done">Done</Option>
<Option value="closed">Closed</Option>
</Select> import { Component } from '@angular/core';
import { Select, Option } from '@arclux/arc-ui-angular';
@Component({
imports: [Select, Option],
template: `
<arc-select label="Assign to team member" placeholder="Choose a person..."
[value]="assignee" (arc-change)="assignee = $event.detail.value">
<arc-option value="alice-chen">Alice Chen</arc-option>
<arc-option value="bob-martinez">Bob Martinez</arc-option>
<arc-option value="carol-nguyen">Carol Nguyen</arc-option>
<arc-option value="david-okafor">David Okafor</arc-option>
<arc-option value="eva-johansson">Eva Johansson</arc-option>
</arc-select>
`,
})
export class AssignmentComponent {
assignee = '';
} import { Select, Option } from '@arclux/arc-ui-solid';
import { createSignal } from 'solid-js';
function CategoryPicker() {
const [category, setCategory] = createSignal('');
return (
<Select label="Category" placeholder="Choose a category..."
value={category()} onArcChange={(e) => setCategory(e.detail.value)}>
<Option value="bug">Bug Report</Option>
<Option value="feature">Feature Request</Option>
<Option value="docs">Documentation</Option>
<Option value="question">Question</Option>
</Select>
);
} import { Select, Option } from '@arclux/arc-ui-preact';
import { useState } from 'preact/hooks';
function LocalePicker() {
const [locale, setLocale] = useState('en');
return (
<Select label="Language" value={locale}
onArcChange={(e) => setLocale(e.detail.value)}>
<Option value="en">English</Option>
<Option value="es">Español</Option>
<Option value="fr">Français</Option>
<Option value="de">Deutsch</Option>
<Option value="ja">日本語</Option>
</Select>
);
} API
-
valuestring'' - The currently selected value. Must match one of the child `arc-option` value attributes. Setting this programmatically updates the displayed label and internal selection state.
-
placeholderstring'Select...' - Hint text displayed inside the trigger button when no option is selected. Use it to communicate what kind of choice the user should make, such as "Choose a team member..." or "Pick a status". The placeholder disappears once a value is chosen.
-
labelstring'' - Visible label rendered above the select trigger. Also serves as the accessible name for assistive technologies. Always provide a label for accessibility compliance.
-
namestring'' - Form field name submitted with the selected value. Required for native form integration via ElementInternals.
-
disabledbooleanfalse - When true, the select trigger becomes non-interactive: it cannot be opened, focused via keyboard, or clicked. The component renders with reduced opacity to visually convey the unavailable state.
-
size'sm' | 'md' | 'lg''md' - Controls the select trigger size.
-
errorstring'' - Error message displayed below the select. When set, the trigger border turns red.
-
openbooleanfalse - Controls whether the dropdown is visible. Set programmatically to open or close the dropdown. Automatically set to `false` when an option is selected or the user clicks outside.
-
formAssociatedbooleantrue -
propertiesobject{ required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, } - Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
-
autoValidatesbooleantrue - Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
-
form -
validity -
validationMessage -
requiredbooleanfalse -
readonlybooleanfalse
Events
-
arc-change - Fired when the selected option changes
Option
<arc-option> Individual option rendered inside a Select or MultiSelect. Each Option provides a value for form submission and displays its text content as the label in the dropdown.
-
label - Expose text content as label
-
valuestring'' - The value identifier for this option, used to match against the parent control value.
-
disabledbooleanfalse - When true, dims this option and prevents it from being selected.
-
selectedbooleanfalse
See Also
- Combobox Searchable dropdown with type-ahead filtering.
- Multi Select Multi-value select with tag chips, inline search filtering, and keyboard navigation.
- Radio Group Single-select option group with arrow-key navigation and ARIA radiogroup semantics. Ideal for pricing tiers, settings panels, and any context where exactly one choice must be made from a visible set of options.