A select is a headless component that wraps the native HTML <select> element, providing a dropdown menu for choosing one option from a list. It is commonly used in forms for selecting a country, category, status, or any other value from a predefined set of choices.
The consumer provides <option> elements as children, giving full control over the available choices. The component uses aria-label for accessible naming and supports two-way binding on the selected value.
<script lang="ts">
import Select from "./Select.svelte";
let country = $state("");
</script>
<Select label="Country" bind:value={country}>
<option value="">Select...</option>
<option value="us">United States</option>
<option value="gb">United Kingdom</option>
</Select>
Show Svelte source
// In your Svelte component:
import Select from "lily-design-system-svelte-headless/components/Select/Select.svelte";
<Select>
<!-- content -->
</Select>
Usage
<Field label="Sort by">
<Select label="Sort by">
<Option value="recently-published" selected>Recently published</Option>
<Option value="recently-updated">Recently updated</Option>
<Option value="most-views">Most views</Option>
</Select>
</Field>
<Field label="Choose your region">
<Hint>This can be different to what you selected before</Hint>
<Select label="Choose your region">
<Option value="">Choose region</Option>
<Option value="east">East of England</Option>
<Option value="london">London</Option>
<Option value="midlands">Midlands</Option>
<Option value="northwest">North West</Option>
<Option value="southeast">South East</Option>
</Select>
</Field>
Props
label: string (required) -- accessible name applied via aria-label
value: string (default: "") -- bindable string representing the currently selected value
required: boolean (default: false) -- whether the select is required for form submission
disabled: boolean (default: false) -- whether the select is disabled
children: slot (required) -- <option> elements to render inside the select
...restProps: unknown -- additional attributes spread onto the select element
Keyboard Interactions
Tab: Moves focus to and from the select element (native browser behavior)
Space/Enter: Opens the dropdown list when focused (native browser behavior)
Arrow Up/Down: Navigates through options (native browser behavior)
Escape: Closes the dropdown list (native browser behavior)
Home/End: Jumps to first/last option (native browser behavior)
Type-ahead: Typing characters jumps to matching options (native browser behavior)
ARIA
aria-label={label} -- provides an accessible name for the select element since there is no visible <label> element
The <select> element implicitly has role="combobox" or role="listbox" depending on the browser, with built-in accessible semantics
When to Use
Use as a last resort when RadioGroup is not practical due to a very long list of options
Use for settings where a sensible default can be pre-selected, such as a sort order or display preference
Use when screen space is limited and a compact dropdown is needed
Use with a descriptive label and optional Hint to help users understand the available choices
When Not to Use
Do not use when there are fewer than 7 options -- use RadioGroup with RadioInput instead, which is easier to use
Do not use the multiple selection attribute -- use CheckboxGroup with CheckboxInput for multi-select
Do not pre-select an option for question-based inputs -- this can bias user responses
Do not use when users need to search or filter options -- use Combobox or SelectWithExtras instead
Headless
The Select headless component provides a native <select> element with aria-label for accessible naming and two-way value binding. Consumer-provided <option> children define the available choices. The browser handles all keyboard navigation and dropdown behavior. The consumer provides all visual styling.
Styles
The consumer provides all CSS styling. The component renders with a .select class for targeting. No default styles are included — this is a fully headless component.
Testing
Verify the component renders a <select> element with class select
Verify aria-label={label} -- provides an accessible name for the select element since there is no visible <label>` element
Verify The <select> element implicitly has role="combobox" or role="listbox" depending on the browser, with built-in accessible semantics
Verify keyboard interactions work correctly
Verify pass-through attributes are applied
Advice
Designers: Include a placeholder option (e.g., "Select a country...") as the first item to guide users, and avoid overly long option lists that are hard to scan.
Developers: Set the placeholder option's value to an empty string and combine with the required prop to enforce selection, ensuring form validation works correctly.
Implementation Notes
Renders a single <select> element containing consumer-provided <option> children
Uses aria-label for accessible naming instead of a visible <label> element
Supports two-way binding on the value prop
Uses slot for the children prop to render <option> elements
Spreads restProps onto the select element for consumer extensibility
Related components
select-with-extras — a select dropdown with additional features like search or groups
option — an option element within a select dropdown
combobox — a text input combined with a dropdown list for filtering options
theme-select — a select dropdown for choosing a theme