Skip to content
Primitives

Select

Dropdown select with a hard-shadow popover surface.

● LIVE
npx shadcn@latest add https://okibi.cndr.dev/r/select.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/select.json

Install the required dependencies:

npm install @radix-ui/react-select lucide-react

Copy 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; owns value / onValueChange.
  • SelectTrigger — the field button (size='sm' | 'default'); renders the chevron.
  • SelectValue — shows the current selection or the placeholder.
  • 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

Sizes

The trigger takes size="sm" for dense toolbars alongside the default size.

● LIVE

Disabled

A disabled trigger locks the control; individual SelectItems can be disabled too.

● LIVE

Invalid

Set aria-invalid on the trigger to flip the keyline to the destructive color for inline errors.

● LIVE

SEC // clearance level required.

API Reference

SelectTrigger

PropTypeDefaultDescription
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 faint destructive/10 fill.
  • 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 CheckIcon via ItemIndicator; disabled items use data-[disabled] and are skipped.
  • Label the trigger through a Form field or an explicit aria-labelSelectValue placeholder text is not an accessible name.
  • Open / close is CSS-only data-[state] animation; disabled:opacity-60 with pointer events off.

Guidelines

Do

  • Label the trigger through a Form field or aria-label, and set a clear SelectValue placeholder.
  • Group long lists with SelectGroup + SelectLabel, separated by SelectSeparator.
  • Drive it controlled with value + onValueChange when the choice lives in app state.

Don't

  • Don't use a select for two or three options that fit inline — use a RadioGroup or ToggleGroup.
  • 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.

On this page