motif svelteArc

Select ported

A compact choice field with a keyboard friendly menu.

A labelled choice field whose shown value rolls in the direction of the list. Built on Bits UI Select for accessible keyboard navigation and typeahead.

Live specimen

Loading demo…

Guidance

When to use

  • A short fixed list where typing is not needed, such as region or sort order.
  • Form fields that should submit natively through the hidden input name.
  • Dropdown selectors requiring clean keyboard navigation and typeahead.

When not to use

  • Use combobox for long or searchable lists.
  • Use multi-select when several values can be chosen.
  • Use segmented-control for two to four choices that should stay visible.

Installation

pnpm dlx shadcn-svelte@latest add @arcui/select

Usage

<script lang="ts">
  import { Select } from '$lib/components/select';

  let region = $state('eu');

  const options = [
    { value: 'us', label: 'United States' },
    { value: 'eu', label: 'Europe' },
    { value: 'ap', label: 'Asia Pacific', disabled: true },
    { value: 'sa', label: 'South America' }
  ];
</script>

<Select
  label="Region"
  bind:value={region}
  {options}
  description="Select the primary deployment region for your cluster."
/>

Variants & Examples

Controlled with Description

Two-way binding via bind:value with helper text linked through aria-describedby.

<Select
  label="Region"
  bind:value={selectedRegion}
  options={regionOptions}
  description="Linked helper text for assistive technology."
/>

Disabled and Disabled Options

Supports disabling individual options or the entire select trigger.

<Select
  label="Restricted Field"
  value="us"
  disabled
  options={regionOptions}
/>

API Reference

PropTypeDefaultDescription
labelstring–Visible label for the trigger, linked via for attribute.
optionsSelectOption[]–Array of { value: string; label: string; disabled?: boolean } items in list order.
valuestringundefinedControlled selected value; supports two-way bind:value.
defaultValuestring''Initial value when uncontrolled.
onValueChange(value: string) => void–Callback fired when an option is selected.
openbooleanundefinedControlled open state of the dropdown; supports two-way bind:open.
onOpenChange(open: boolean) => void–Callback fired when dropdown opens or closes.
placeholderstring'Select an option'Placeholder text displayed when no value is selected.
descriptionstring–Helper copy rendered below the trigger, linked through aria-describedby.
disabledbooleanfalseWhether the select is disabled.
namestring–Form submission input name.
requiredbooleanfalseWhether selection is required for form validation.
idstring–Trigger ID; auto-generated via $props.id() when omitted.
classstring–Additional CSS class applied to the trigger element.

Keyboard Interactions

KeyAction
Enter / Space / ArrowDownOpens the list from the trigger.
ArrowUp / ArrowDownMoves highlight between enabled items in the list.
Enter / SpaceSelects the highlighted item and closes the dropdown.
EscapeCloses the dropdown without changing the selected value.
Type a letterJumps directly to the next item starting with that letter (typeahead).

Accessibility

  • Built on Bits UI Select: provides native role="combobox" on trigger and role="listbox" on content.
  • Trigger is linked to label with for/id, and to description through aria-describedby.
  • Real value renders in a visually hidden input for form submission; animated copy is aria-hidden.
  • Focus is properly trapped and returned to trigger on close.

Motion

Phase 1 still-state: Selected value renders in trigger valueText container. Content animates in and out with CSS keyframes (select-in / select-out). Chevron rotates 180deg when open. Phase 2 wires directional value roll (later option rises from below, earlier drops from above with soft blur filter). Reduced motion swaps value with instant crossfade.

Notes for AI

  • Pick for short fixed lists where typing is not needed.
  • Two-way binding is available via bind:value={selected}.
  • Use options array with value and label properties; disabled flag is optional per item.

Source of truth: registry/components/select/select.tsx