caliper uidocs

Select menu

Free

A select whose list opens with the chosen option over the trigger; the platform picker on touch.

Installs components/ui/select-menu.tsxDepends on @radix-ui/react-popover, lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/select-menu.json

18 ms from Frankfurt

When to use #

Use it for

  • Five to fifteen options where a short code helps, like regions or currencies.
  • Desktop forms where the list should grow around what you already read.

Skip it when

  • Options need search; use a combobox.
  • Two to five options; a segmented control is faster.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/select-menu.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { Label } from "@/components/ui/label"
import { SelectMenu } from "@/components/ui/select-menu"

const regionOptions = [
  { value: "eu-central-1", label: "Frankfurt", code: "eu-central-1" },
  { value: "eu-west-1", label: "Dublin", code: "eu-west-1" },
  { value: "eu-west-2", label: "London", code: "eu-west-2" },
  { value: "us-east-1", label: "Ashburn", code: "us-east-1" },
  { value: "us-west-2", label: "Oregon", code: "us-west-2" },
  { value: "ap-northeast-1", label: "Tokyo", code: "ap-northeast-1" },
  { value: "ap-southeast-2", label: "Sydney · at capacity", code: "ap-southeast-2", disabled: true },
];

const regionHints: Record<string, string> = {
  "eu-central-1": "Closest to most of your team",
  "eu-west-1": "18 ms from Frankfurt",
  "eu-west-2": "UK data residency",
  "us-east-1": "92 ms from Frankfurt",
  "us-west-2": "148 ms from Frankfurt",
  "ap-northeast-1": "231 ms from Frankfurt",
};

export function RegionMenu() {
  const [region, setRegion] = useState("eu-west-1");
  return (
    <div className="grid w-full max-w-xs gap-1.5 py-24">
      <Label id="docs-region-menu-label">Primary region</Label>
      <SelectMenu aria-labelledby="docs-region-menu-label" name="region" options={regionOptions} value={region} onValueChange={setRegion} />
      <p className="text-xs text-muted-foreground" role="status">{regionHints[region]}</p>
    </div>
  );
}

const team = [
  { value: "mara", label: "Mara Chen" },
  { value: "noor", label: "Noor Haddad" },
  { value: "ivo", label: "Ivo Petrov" },
  { value: "sana", label: "Sana Okafor" },
];

API reference #

SelectMenu

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
options *readonly SelectMenuOption[]—
valuestring—
defaultValuestring—
onValueChange((value: string) => void)—
placeholderstring"Choose…"
namestring—Submitted with the form.
disabledboolean—
size"sm" | "md""md"sm is 32px for toolbars, filters and table rows; md follows the control height.
variant"ghost" | "field""field"ghost drops the field ring, for a select inside a composed control (a currency in a money field).
classNamestring—
refRef<HTMLButtonElement | HTMLSelectElement>—The trigger (or, on touch screens, the native select), e.g. for a dialog's first focus.

Accessibility #

It exposes combobox, listbox and option semantics, with aria-hidden, aria-haspopup, aria-expanded, aria-controls, aria-labelledby and aria-label kept in step with state.

KeyBehaviour
↓Moves to the next item or value.
↑Moves to the previous item or value.
HomeJumps to the first item or the minimum.
EndJumps to the last item or the maximum.
EnterActivates the focused control.
SpaceActivates the focused control.
TabMoves focus through the part's focusable elements.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.