Primitives
Select
Dropdown select with a hard-shadow popover surface.
● LIVE
"use client";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/registry/okibi/ui/select";export default function SelectDemo() { return ( <Select defaultValue="ALPHA"> <SelectTrigger className="w-56 max-w-full"> <SelectValue placeholder="Clearance level" /> </SelectTrigger> <SelectContent> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE">Charlie</SelectItem> </SelectContent> </Select> );}npx shadcn@latest add https://okibi.cndr.dev/r/select.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/select.jsonInstall the required dependencies:
npm install @radix-ui/react-select lucide-reactCopy the source from the Code tab above into components/ui/select.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
<Select>
<SelectTrigger>
<SelectValue placeholder="Grid reference" />
</SelectTrigger>
<SelectContent>
<SelectItem value="ALPHA">Alpha</SelectItem>
<SelectItem value="BRAVO">Bravo</SelectItem>
</SelectContent>
</Select>Anatomy
<Select>
<SelectTrigger>
<SelectValue placeholder="Grid reference" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Sectors</SelectLabel>
<SelectItem value="ALPHA">Alpha</SelectItem>
<SelectItem value="BRAVO">Bravo</SelectItem>
</SelectGroup>
<SelectSeparator />
<SelectItem value="OMEGA">Omega</SelectItem>
</SelectContent>
</Select>Select— the root; ownsvalue/onValueChange.SelectTrigger— the field button (size='sm' | 'default'); renders the chevron.SelectValue— shows the current selection or theplaceholder.SelectContent— the portalled popover (auto scroll buttons when it overflows).SelectGroup+SelectLabel— an optional labelled cluster of items.SelectItem— one option; checked items show the indicator.SelectSeparator— a hairline rule between groups.
Examples
Grouped
Organize options into SelectGroups with SelectLabel headings and SelectSeparator rules; the list scrolls when it overflows.
● LIVE
"use client";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@/registry/okibi/ui/select";export default function SelectScrollableDemo() { return ( <Select> <SelectTrigger className="w-56"> <SelectValue placeholder="Grid reference" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Sectors</SelectLabel> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE">Charlie</SelectItem> <SelectItem value="DELTA">Delta</SelectItem> <SelectItem value="ECHO">Echo</SelectItem> </SelectGroup> <SelectSeparator /> <SelectGroup> <SelectLabel>Channels</SelectLabel> <SelectItem value="CH-01">CH-01</SelectItem> <SelectItem value="CH-02">CH-02</SelectItem> <SelectItem value="CH-03">CH-03</SelectItem> <SelectItem value="CH-04">CH-04</SelectItem> <SelectItem value="CH-05">CH-05</SelectItem> </SelectGroup> </SelectContent> </Select> );}Sizes
The trigger takes size="sm" for dense toolbars alongside the default size.
● LIVE
"use client";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/registry/okibi/ui/select";export default function SelectSizesDemo() { return ( <div className="flex gap-3"> <Select> <SelectTrigger size="sm"> <SelectValue placeholder="Sector" /> </SelectTrigger> <SelectContent> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE">Charlie</SelectItem> </SelectContent> </Select> <Select> <SelectTrigger> <SelectValue placeholder="Channel" /> </SelectTrigger> <SelectContent> <SelectItem value="CH-01">CH-01</SelectItem> <SelectItem value="CH-02">CH-02</SelectItem> <SelectItem value="CH-03">CH-03</SelectItem> </SelectContent> </Select> </div> );}Disabled
A disabled trigger locks the control; individual SelectItems can be disabled too.
● LIVE
"use client";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/registry/okibi/ui/select";export default function SelectDisabledDemo() { // Locked controls: a fully disabled trigger holding a committed value, plus // an enabled select whose "Charlie" option is individually disabled. return ( <div className="flex flex-col gap-3"> <Select defaultValue="ALPHA" disabled> <SelectTrigger className="w-56 max-w-full"> <SelectValue placeholder="Clearance level" /> </SelectTrigger> <SelectContent> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE">Charlie</SelectItem> </SelectContent> </Select> <Select> <SelectTrigger className="w-56 max-w-full"> <SelectValue placeholder="Clearance level" /> </SelectTrigger> <SelectContent> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE" disabled> Charlie </SelectItem> </SelectContent> </Select> </div> );}Invalid
Set aria-invalid on the trigger to flip the keyline to the destructive color for inline errors.
● LIVE
SEC // clearance level required.
"use client";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/registry/okibi/ui/select";export default function SelectInvalidDemo() { return ( <div className="w-56 max-w-full space-y-2"> <Select> <SelectTrigger aria-invalid className="w-full"> <SelectValue placeholder="Clearance level" /> </SelectTrigger> <SelectContent> <SelectItem value="ALPHA">Alpha</SelectItem> <SelectItem value="BRAVO">Bravo</SelectItem> <SelectItem value="CHARLIE">Charlie</SelectItem> </SelectContent> </Select> <p className="font-mono text-xs text-destructive-spark"> SEC // clearance level required. </p> </div> );}API Reference
SelectTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "default" | Trigger height — sm for dense toolbars. |
Accessibility
- Built on Radix
Select, giving a listbox with full keyboard support: type-ahead, arrow navigation, Enter to choose, Esc to close. - The trigger reflects
aria-invalid='true'with a destructive border plus faintdestructive/10fill. - Focus on the trigger draws the 2px signal ring as an inset
box-shadow(inset 0 0 0 2px var(--color-ring)). - The selected item is marked with a
CheckIconviaItemIndicator; disabled items usedata-[disabled]and are skipped. - Label the trigger through a
Formfield or an explicitaria-label—SelectValueplaceholder text is not an accessible name. - Open / close is CSS-only
data-[state]animation;disabled:opacity-60with pointer events off.
Guidelines
Do
- Label the trigger through a
Formfield oraria-label, and set a clearSelectValueplaceholder. - Group long lists with
SelectGroup+SelectLabel, separated bySelectSeparator. - Drive it controlled with
value+onValueChangewhen the choice lives in app state.
Don't
- Don't use a select for two or three options that fit inline — use a
RadioGrouporToggleGroup. - Don't reach for select when users need free typing or fuzzy search — use
Combobox. - Don't treat the placeholder as a label; it disappears once a value is chosen.