Skip to content

Inputs

Select

Dropdown selector with keyboard navigation, a label, hint and error state.

<script>
  import { Select } from "@codeatlasdev/ui";
  let value = $state("br");
</script>

<Select label="Country" options={[{ value: "br", label: "Brazil" }]} bind:value />

For long lists that need filtering, use Combobox. Without a label, pass aria-label (or aria-labelledby) to name the trigger: the accessible name is that text followed by the current value, so it still contains the visible text. With neither, the placeholder names it. Arrow keys, Enter and Space open the list from the trigger, and Escape closes it. Pass name to submit the selected value with a native form (a hidden input; nothing is sent while no option is selected or the field is disabled). required blocks native form submission while nothing is selected. Text inside inputs stays at 16px or more on touch screens, so iOS Safari never zooms on focus.

#040

Basic

Controlled select with a pre-selected value.

Selected: svelte

Reference #040 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Select } from '@codeatlasdev/ui';

  let framework = $state('svelte');

  const frameworks = [
    { value: 'svelte', label: 'Svelte 5' },
    { value: 'react', label: 'React 19' },
    { value: 'vue', label: 'Vue 3' },
    { value: 'solid', label: 'SolidJS' },
    { value: 'astro', label: 'Astro' },
  ];
</script>

<div class="col">
  <Select label="Framework" options={frameworks} bind:value={framework} />
  <p class="out">Selected: <strong>{framework}</strong></p>
</div>

<style>
  .col {
    display: flex;
    flex-direction: column;
    gap: var(--ca-space-3);
    width: 100%;
    max-width: 320px;
  }

  .out {
    margin: 0;
    font-size: var(--ca-font-size-body2);
    color: var(--ca-text-secondary);
  }

  .out strong {
    color: var(--ca-brand-text);
    font-family: var(--ca-font-mono);
  }
</style>
#042

Placeholder

No initial value: the placeholder shows until the user picks an option.

Reference #042 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Select } from '@codeatlasdev/ui';

  let country = $state('');

  const countries = [
    { value: 'br', label: 'Brazil' },
    { value: 'us', label: 'United States' },
    { value: 'de', label: 'Germany' },
    { value: 'jp', label: 'Japan' },
    { value: 'au', label: 'Australia' },
  ];
</script>

<div class="col">
  <Select label="Country" placeholder="Choose a country…" options={countries} bind:value={country} />
</div>

<style>
  .col {
    width: 100%;
    max-width: 320px;
  }
</style>
#041

Empty-value option

An option with value '' (such as 'All projects') is a real selection: the trigger shows its label, not the placeholder. Use it for filters instead of a sentinel value.

Filter value: ""

Reference #041 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Select } from '@codeatlasdev/ui';

  let project = $state('');

  const projects = [
    { value: '', label: 'All projects' },
    { value: 'web', label: 'Web app' },
    { value: 'api', label: 'API' },
    { value: 'docs', label: 'Docs' },
  ];
</script>

<div class="col">
  <Select label="Project" placeholder="Choose a project…" options={projects} bind:value={project} />
  <p class="out">Filter value: <code>{JSON.stringify(project)}</code></p>
</div>

<style>
  .col {
    width: 100%;
    max-width: 320px;
  }
  .out {
    margin: var(--ca-space-2) 0 0;
    font-size: var(--ca-font-size-caption);
    color: var(--ca-text-secondary);
  }
</style>
#043

States

Disabled options, description, error and a disabled select.

Enterprise is disabled.

Reference #043 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { Select } from '@codeatlasdev/ui';

  let size = $state('md');
  let plan = $state('free');

  const sizes = [
    { value: 'sm', label: 'Small' },
    { value: 'md', label: 'Medium' },
    { value: 'lg', label: 'Large' },
  ];

  const plans = [
    { value: 'free', label: 'Free' },
    { value: 'pro', label: 'Pro' },
    { value: 'enterprise', label: 'Enterprise', disabled: true },
  ];
</script>

<div class="col">
  <Select label="Plan" description="Enterprise is disabled." options={plans} bind:value={plan} />
  <Select label="Region" options={sizes} error="Pick a region to continue." />
  <Select label="Size" options={sizes} bind:value={size} disabled />
</div>

<style>
  .col {
    display: flex;
    flex-direction: column;
    gap: var(--ca-space-4);
    width: 100%;
    max-width: 320px;
  }
</style>

Props

PropTypeDefaultDescription
valuestring–Bindable selected value. `undefined` means no selection and shows the placeholder; `""` is a valid value when an option declares it.
optionsArray<{ value: string; label: string; disabled?: boolean }>–Option items.
placeholderstring'Select…' (localized)Text shown when `value` is `undefined` or matches no option. The default comes from the active i18n catalog.
labelstring–Visible label above the trigger.
descriptionstring–Hint shown below the trigger.
aria-labelstring–Accessible name when there is no visible `label`. The trigger is named `aria-label` + current value; the list is named `aria-label`.
aria-labelledbystring–Id of an element that names the select when there is no visible `label` (followed by the current value). Ignored when `label` is set.
errorstring–Error message. Puts the select in the error state.
namestring–Native name, so the selected value is in FormData on a plain form submit.
requiredbooleanfalseMarks the field as required.
disabledbooleanfalseDisables the entire select.
size'sm' | 'md''md'Trigger height.
classstring–Extra classes on the root.
onchange(value: string) => void–Called when the selection changes.