/// Inputs
CardInput
Selectable option cards for plan / tier / layout pickers — radio single-choice or checkbox multi-choice, selection inverting to an accent ring.
import { CardInput } from "@protocore/pds";Single choice
Wrap CardInput.Options in a CardInput.Group. The default type="single" gives radio semantics — one value, arrow-key navigation — and the selected card takes the accent ring.
Multiple choice
Set type="multiple" for checkbox semantics: the group owns a string[] and each card toggles independently.
Standalone toggle
A lone CardInput.Option (no Group) behaves as a checkbox card, controllable via checked / onCheckedChange — good for a single opt-in.
When to use it
Use CardInput when each option carries enough content — a title, a description, a price — that a plain radio or checkbox row would feel cramped, and seeing the options side by side aids the choice. For terse, equal-weight options use RadioGroup / CheckboxGroup; for a compact view-mode toggle use SegmentedControl.
Usage
Do
- Give the group an accessible name via `aria-label` or a wrapping `Field`.
- Keep each card's title and description parallel across options.
- Set a `name` on the group when the value participates in a native form.
Don't
- Don't nest other interactive controls inside a card — the whole card is the control.
- Don't use single-choice cards where more than one can apply — set `type="multiple"`.
- Don't rely on color alone; the selected card also shows a checked marker.
Accessibility
| Keys | Action |
|---|---|
| Tab | Move focus into the group (single: the selected card). |
| Arrow keys | Move and select between cards in single mode. |
| Space | Toggle the focused card in multiple mode. |
- Each card is a `<label>` wrapping a real radio/checkbox, so selection is native and screen-reader-correct.
- The group is a `role="radiogroup"` (single) or `role="group"` (multiple).
- Focus draws the standard accent ring on the card surface via the hidden input's `:focus-visible`.
CardInput.Group props
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | — | Accessible name for the group. |
className | string | — | |
defaultValue | string | string[] | — | Initial selection when uncontrolled. |
disabled | boolean | — | Disable every card in the group. |
name | string | — | Shared `name` for the underlying inputs — required for radio grouping in single mode. |
onValueChange | ((value: string | string[]) => void) | — | Fires with the next selection: a string in single mode, string[] in multiple. |
orientation | enum | horizontal | Layout of the cards. Default `"horizontal"` (wrapping row). |
style | CSSProperties | — | |
type | enum | single | `"single"` = radio semantics (one value), `"multiple"` = checkbox (array). Default `"single"`. |
value | string | string[] | — | Controlled selection — a string (single) or string[] (multiple). |
CardInput.Option props
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Controlled checked state when used standalone (no Group). |
children | ReactNode | — | Extra content below the description. |
className | string | — | |
defaultChecked | boolean | — | Initial checked state when used standalone and uncontrolled. |
description | ReactNode | — | Secondary line under the title (muted). |
disabled | boolean | — | Disable just this card. |
icon | ReactNode | — | Optional leading icon slot. |
onCheckedChange | ((checked: boolean) => void) | — | Fires with the next checked state when used standalone. |
style | CSSProperties | — | |
title | ReactNode | — | Primary label (sans, ink). |
value | string | — | This option's value — required inside a `CardInput.Group`. |