Component catalog
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
Browse the public controls by interaction purpose. Every result links to generated API facts, accessibility behavior, source, and exhaustive stories.
A single-select control that chooses one value from a discrete set of options.
Install the package and import the component from its public entry point.
pnpm add @liquidify/reactimport { Picker } from "@liquidify/react/picker"This minimal form confirms the public import. Use the curated examples for application-shaped composition and Storybook for exhaustive states.
menu — root renders the shared Popup compound primitive
wheel — root is wrapping a
segmented — root is . Each
radioGroup — root is carrying
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | undefined | — | Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean. |
defaultOpen | boolean | undefined | `false` | Uncontrolled seed for the open state of the menu popover panel. Ignored for `segmented` and `wheel` styles. Pass `defaultOpen={true}` in a Storybook story or test to pre-open the listbox panel for visual grading without a user interaction. |
defaultValue | string | undefined | `items[0].value` | Uncontrolled seed for the internal selection (ignored once {@link PickerProps.value} is supplied). |
disabled | boolean | undefined | — | When `true`, the whole control is dimmed, removed from the tab order, and all interaction is suppressed. Since the roots vary by style (some are `<div>`, some `<button>`), `data-disabled` provides the CSS hook and `aria-disabled` is set on the appropriate ARIA root. |
items * | readonly PickerItem[] | — | The selectable options, rendered in order. At least one item is required. |
labelProps | LabelProps | undefined | — | Per-instance typography override for the trigger value and the option labels, forwarded to the internal {@link Text} (ADR-0028). Token-backed axes only; the HIG default is `variant="body"` with `color="inherit"`, so each label adopts its host's colour (accent trigger, primary/secondary option, the wheel's selected colour transition). Applies only to string/number labels — an `<Icon>` / element label passes through unwrapped. |
onChange | ((next: string) => void) | undefined | — | Fires with the next selected item's `value` whenever the selection changes. Always called, in both controlled and uncontrolled modes. |
onOpenChange | ((open: boolean) => void) | undefined | — | Fires with the next open state on every open/close path of the menu popover panel — trigger click, item selection, Escape, and outside pointer-down. Ignored for `segmented` and `wheel` styles. |
open | boolean | undefined | — | Controlled open state of the menu popover panel (ADR-0015 presentation triad, with {@link PickerProps.defaultOpen} / {@link PickerProps.onOpenChange}). Passing it switches the open state to controlled mode: the panel follows this prop and every open/close request only notifies. Ignored for `segmented` and `wheel` styles. |
placement | PickerPlacement | undefined | `"top-left"` | Which trigger corner the `menu` panel anchors to — the corner picks the open side (`top-*` below the trigger, `bottom-*` above) and the trigger edge the panel aligns with (`*-left` / `*-right`), so the menu grows away from that corner (see {@link PickerPlacement}). Ignored for `segmented` and `wheel` styles. |
ref | Ref<HTMLDivElement> | undefined | — | Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`. |
value | string | undefined | — | Controlled selected value. Passing this switches Picker to controlled mode: the rendered selection always reflects this value and the consumer owns it via {@link PickerProps.onChange}. |
variant | PickerVariant | undefined | `"menu"` | The visual variant of the picker — maps to SwiftUI `pickerStyle`. |
The mapping describes naming and behavior without claiming framework equivalence.
| SwiftUI | React | Relationship | Notes |
|---|---|---|---|
`Picker(_ title:, selection: Binding<V>, content:)` | `aria-label` / `aria-labelledby` + `value?: string` + `onChange(next: string)` | direct | `title` becomes the accessible name (via `aria-label`/`aria-labelledby`); `selection: Binding<V>` splits into `value` (controlled) + `onChange`; single-select, string-keyed (ADR-0015). |
`@State sel = 1` seed | `defaultValue?: string` (default `items[0].value`) | direct | Uncontrolled seed; mirrors Segmented exactly. |
`Text(opts[i]).tag(i)` | `items: PickerItem[]` where `PickerItem = { value: string; label: ReactNode; disabled?: boolean; icon?: ReactNode }` | direct | `.tag` → `value`; `Text`/`Label` → `label`; `Label(systemImage:)` → `icon`. A string/number `label` renders through the `Text` primitive (`variant="body"`, `color="inherit"` so each option/trigger keeps its own colour authority — accent trigger, primary/secondary option, the wheel selected-colour transition); an element label passes through unwrapped (ADR-0028). |
`.font(_:)` / `.foregroundStyle(_:)` on labels | `labelProps` (`Pick<TextProps, …>`) | direct | Per-instance typography override forwarded to the internal `Text` of the trigger value **and** every option label (token-backed axes only; ADR-0028). Spread last, so an explicit value wins. Applies only to string/number labels. |
`.pickerStyle(.menu)` / `.wheel` / `.segmented` / `.radioGroup` | `variant?: PickerVariant` (`"menu" | "wheel" | "segmented" | "radioGroup"`) | direct | The whole variant axis. Default `"menu"`. `radioGroup` is the SwiftUI macOS/visionOS `.radioGroup` style, rendered web-side with the iOS checkmark-row idiom (resolved ADR-0014/0027) — a vertical native-radio group with a trailing flat token-filled checkmark indicator. |
`.radioGroup` selection idiom (`Picker(selection:)` single `Binding<Hashable>`) | reuses `value` / `defaultValue` / `onChange` (value-bearing **string** triad, NOT the boolean `checked` triad) | direct | `radioGroup` maps its single-select `Binding` to the **same string `value` triad** as `menu`/`wheel`/`segmented` and to Segmented — never Toggle's `checked`. The resolved `value` maps to the checked radio row; a controlled `value` absent from `items` renders no checked row (no raw-key echo). |
menu presentation state (no SwiftUI counterpart — UIKit/AppKit own the menu presentation) | `open?: boolean` + `defaultOpen?: boolean` (default `false`) + `onOpenChange?(open: boolean)` | direct | The `menu` panel's presentation triad (ADR-0015): `open` switches to controlled mode, `defaultOpen` seeds the uncontrolled state, `onOpenChange` fires on every open/close path (trigger click, selection, `Escape`, outside pointer-down). Delegated to the shared `Popup` primitive. Ignored by `segmented`/`wheel`. |
`.disabled(true)` | `disabled?: boolean` | direct | Whole-control disable: `data-disabled` + `aria-disabled` + `pointer-events: none` + removed from tab order. Per-item via `PickerItem.disabled`. |
`.controlSize(_:)` | — (deferred; see Sizes) | direct | No size axis in v1. |
`.tint(_:)` | — (deferred; see Out of scope) | direct | No tint axis in v1; accent is always `--lq-color-accent`. |
`.frame(height: 150)` (wheel) | n/a (component-owned geometry) | direct | iOS drum height; resolved from `--lq-picker-wheel-h`. |
`Picker(sources:selection:…)` multi-select | — | direct | Out of scope; single-select only. |
(accessible listbox fallback): ↑/↓ scroll by one row; Home/ End to first/last; Enter confirms.
--lq-color---lq-color-accent--lq-color-bg-primary--lq-color-label-primary--lq-color-label-quaternary-dark--lq-color-label-quaternary-light--lq-color-label-secondary--lq-color-separator--lq-glass---lq-hit-target-touch--lq-icon-size-small--lq-lens-progress--lq-lens-stretch--lq-lens-x--lq-picker-indicator-alpha--lq-picker-pad--lq-picker-wheel-band-fill--lq-picker-wheel-band-h--lq-picker-wheel-band-inset--lq-picker-wheel-band-radius--lq-picker-wheel-h--lq-radius-large--lq-radius-pill--lq-shadow-ambient---lq-space-