Select
One choice from a list that opens below the field. A long value wraps and is never truncated.
Usage
import {
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@cadence-clinical/ui";
const clinics = [
{ value: "general", label: "General clinic" },
{ value: "review", label: "Review clinic" },
];
<Label htmlFor="clinic">Clinic</Label>
<Select items={clinics} onValueChange={setClinic}>
<SelectTrigger id="clinic">
<SelectValue placeholder="Choose a clinic" />
</SelectTrigger>
<SelectContent>
{clinics.map((clinic) => (
<SelectItem key={clinic.value} value={clinic.value}>
{clinic.label}
</SelectItem>
))}
</SelectContent>
</Select>Select is built on the Base UI Select and accepts all of its props. It follows the shadcn Select and keeps its part names. SelectGroup, SelectLabel and SelectSeparator divide a long list.
Pass items to Select, so the field shows the chosen option's label and not its value.
A select holds a choice. For a list of actions, use a Dropdown menu. Use a select when there are more options than fit on the screen at once. For a few, a Radio group shows them all and takes one action fewer.
Name the field
Point a Label's htmlFor at the SelectTrigger, or give the trigger an aria-label.
A select has two things a screen reader announces: the field and the list that opens. Base UI names the field and leaves the list unnamed. Here the list takes the field's name when it opens, so you name the field once. To name the list differently, pass aria-label or aria-labelledby to SelectContent.
Sizes
SelectTrigger takes size, with the same three steps as a Button: sm, md and lg. Height and text step together and follow the density.
What Cadence changes
- Nothing is truncated. The shadcn select clamps the chosen value to one line and clips an option that is wider than the list. Here the field has a minimum height and grows when its value wraps, and a long option wraps in the list, even when it has nowhere to break.
- The list opens below the field, so the field and its label stay in view. Set
alignItemWithTriggeronSelectContentfor the Base UI default, which lays the list over the field. - The list opens at once. A select is opened often and from the keyboard, where an animation only delays.
- An option is as high as a control, so it is a 44px target when the density is comfortable.
- The list is named. It takes the field's name when it opens. Base UI leaves it unnamed, which axe reports as a failure.
- Read-only keeps full contrast, and only disabled is faded. The boundary and focus ring are never faded.
Accessibility
- The chosen option carries a tick as well as its highlight.
- Enter, Space or an arrow key opens the list. The arrow keys move through it, typing jumps to a match, Enter chooses and Escape closes. Focus returns to the field.
Props
| Prop | On | Type | Default |
|---|---|---|---|
items | Select | { value, label }[] | |
size | SelectTrigger | "sm" | "md" | "lg" | "md" |
aria-label | SelectContent | string | The field's name |
side | SelectContent | As in Base UI's Positioner | "bottom" |
align | SelectContent | As in Base UI's Positioner | "start" |
alignItemWithTrigger | SelectContent | boolean | false |