- Components
Select
A custom dropdown with a keyboard-complete listbox, type-ahead and form value.
npx shadcn add @klarhimmel/select @klarhimmel/inputWhen to use
Use for
Choosing one of five to fifty options, with search by typing the first letters.
Not for
Two to five options. Use Radio group. Long lists people can name. Use Combobox.
Pairs with
Field, forms.
Preview
- Seaside suite
- Garden room
- Loft
- Barn cottage
- Attic room
Nothing chosen yet.
select.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Field } from '@/components/klarhimmel/ui/input';
import { Select } from '@/components/klarhimmel/ui/select';
export default function Example() {
return (
<Theme theme="brand">
<Field label="Room">
{(p) => (
<Select
{...p}
options={[
{ value: 'sea', label: 'Seaside suite' },
{ value: 'garden', label: 'Garden room' },
{ value: 'loft', label: 'Loft' },
]}
placeholder="Choose a room"
/>
)}
</Field>
</Theme>
);
}Install
Import
import { Select } from '@/components/klarhimmel/ui/select';- Lands in
- components/klarhimmel/ui/select.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
- Render in
- Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.
Examples
States
- Svenska
- English
- Deutsch
Choose a language to continue.
- SEK
Set by your country.
import { Field } from '@/components/klarhimmel/ui/input'; import { Select } from '@/components/klarhimmel/ui/select'; const options = [ { value: 'sv', label: 'Svenska' }, { value: 'en', label: 'English' }, { value: 'de', label: 'Deutsch' }, ]; export default function SelectStates() { return ( <div className="grid w-[min(40rem,calc(100cqw-3rem))] min-h-60 content-start gap-6 @md:grid-cols-2"> <Field label="Language" error="Choose a language to continue.">{(p) => <Select {...p} options={options} placeholder="Choose…" />}</Field> <Field label="Currency" hint="Set by your country.">{(p) => <Select {...p} options={[{ value: 'sek', label: 'SEK' }]} defaultValue="sek" disabled />}</Field> </div> ); }Form
import { useState } from 'react'; import { Button } from '@/components/klarhimmel/ui/button'; import { Field } from '@/components/klarhimmel/ui/input'; import { Select } from '@/components/klarhimmel/ui/select'; const options = [{ value: 'early', label: 'Early, 07:00' }, { value: 'normal', label: 'Normal, 09:00' }, { value: 'late', label: 'Late, 11:00' }]; export default function SelectForm() { const [sent, setSent] = useState(''); return ( <form className="flex min-h-72 w-[min(24rem,calc(100cqw-3rem))] flex-col items-start gap-4" onSubmit={(e) => { e.preventDefault(); setSent(String(new FormData(e.currentTarget).get('breakfast') ?? '')); }}> <Field label="Breakfast time" className="w-full">{(p) => <Select {...p} name="breakfast" options={options} defaultValue="normal" />}</Field> <Button type="submit">Save</Button> <p role="status" className="text-kh-small text-kh-muted">{sent ? `The form sent breakfast=${sent}.` : 'Not sent yet.'}</p> </form> ); }
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
options | { value: string; label: string; disabled?: boolean }[] | - | Required. |
value / defaultValue / onValueChange | string / (value: string) => void | - | Controlled or uncontrolled state. |
placeholder | string | 'Choose…' | Shown while nothing is chosen. |
name | string | - | Submits the value with a form (a hidden input). |
invalid / disabled | boolean | false | Error styling; switches it off. |
aria-label / aria-labelledby | string | - | Needed when no Field or <label htmlFor> names it. |
className | string | - | Extra classes, merged after the component's own. |
Accessibility
| Key | Does |
|---|---|
Enter / Space / Down / Up | Opens the list on the chosen option |
Down / Up | Moves the highlight (stops at the ends) |
Home / End | First or last option |
Page Down / Page Up | Ten options at a time |
Letters | Jump to the option that starts with them; repeat a letter to cycle |
Enter / Space | Chooses the highlighted option and closes |
Alt+Up | Chooses and closes |
Tab | Chooses the highlighted option and moves on |
Escape | Closes without changing |
ARIA select-only combobox. A click outside only closes the list; Tab commits the highlighted option. Inside a small scrolling panel prefer NativeSelect.
Accessibility