# Select

> A single-choice dropdown built on a real button and a portalled listbox panel, with full keyboard navigation, typeahead and focus that never leaves the trigger.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Forms
- Tags: select, dropdown, listbox, combobox, form, compound, accessibility
- Docs: https://fancy-ui.rama.app/docs/components/select
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/select

## Import

```svelte
<script lang="ts">
  import { Select } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { Select } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` (required) | `SelectOption[]` | — | The choices, in order — { value, label, disabled? }. |
| `value` | `string` | `""` | The selected value, bindable — "" means nothing is selected. |
| `onValueChange` | `(value: string) => void` | — | Called with the new value whenever the selection changes. |
| `placeholder` | `string` | — | Shown in the trigger while nothing is selected. |
| `disabled` | `boolean` | `false` | Blocks opening; excludes the control from form submission. |
| `required` | `boolean` | `false` | Marks the control required (aria-required). No native form-validation enforcement — see the README. |
| `invalid` | `boolean` | `false` | Drives the error border and aria-invalid. |
| `id` | `string` | — | Element id. |
| `name` | `string` | — | Native name. When set, a hidden input carries the value into the form. |
| `label` | `string` | — | Accessible name — for a control with no visible Label next to it. |
| `side` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Side of the trigger to place the panel on. Flips when it would overflow. |
| `align` | `"start" \| "center" \| "end"` | `"start"` | Alignment along the trigger's cross axis. |
| `class` | `string` | — | Additional CSS classes, merged onto the trigger. |
| `ref` | `HTMLButtonElement \| null` | `null` | Bindable reference to the trigger button. |
| `sound` | `boolean` | `false` | Plays open on opening, select on a committed choice and close on a dismissal, once the user has enabled sound |

## Examples

### Basic Usage

Three frameworks, one selected by default.

```svelte
<script lang="ts">
	import { Select } from "fancy-ui-svelte";

	const frameworks = [
		{ value: "svelte", label: "Svelte 5" },
		{ value: "react", label: "React" },
		{ value: "vue", label: "Vue" },
	];

	let framework = $state("svelte");
</script>

<Select
	sound
	options={frameworks}
	bind:value={framework}
	label="Framework"
	placeholder="Choose a framework"
	class="w-[220px]"
/>
```

### With FormField

Wrapped in FormField — required and the error message driven by context.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
	import { Select } from "fancy-ui-svelte";

	const plans = [
		{ value: "starter", label: "Starter" },
		{ value: "team", label: "Team" },
		{ value: "enterprise", label: "Enterprise" },
	];

	let plan = $state("");
</script>

<FormField label="Plan" required error={plan === "" ? "Choose a plan to continue." : undefined}>
	<Select sound options={plans} bind:value={plan} placeholder="Select a plan" class="w-[220px]" />
</FormField>
```

### Disabled Options

One option unavailable — skipped by keyboard, typeahead and pointer selection.

```svelte
<script lang="ts">
	import { Select } from "fancy-ui-svelte";

	const regions = [
		{ value: "us", label: "United States" },
		{ value: "eu", label: "Europe" },
		{ value: "ap", label: "Asia Pacific (coming soon)", disabled: true },
	];

	let region = $state("us");
</script>

<Select sound options={regions} bind:value={region} label="Region" class="w-[240px]" />
```

### Placement

The panel anchored on all four sides of the trigger.

```svelte
<script lang="ts">
	import { Select } from "fancy-ui-svelte";

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

	const sides = ["top", "right", "bottom", "left"] as const;
</script>

<div class="flex flex-wrap items-center gap-6">
	{#each sides as side (side)}
		<Select sound options={sizes} placeholder={side} {side} class="w-[140px]" />
	{/each}
</div>
```
