Skip to content
NewService Map: 30 components for hosts, ports and routes
<PxChoice />ReactFormsForm controlFree

Option cards

Radio group rendered as option cards; the selection outline is made of pixels that fly to the newly selected card, then keep circling.

View as Markdown
<PxChoice />

Tryuse the arrow keys.

Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

Loading <PxChoice />
Props
defaultValueremounts
labellive
disabledlive
React
<PxChoice
  label="Plan"
  options={[
    {
      value: "free",
      label: "Free",
      description: "For trying things out",
      price: "€0"
    },
    {
      value: "pro",
      label: "Pro",
      description: "For daily work",
      price: "€20/mo"
    },
    {
      value: "team",
      label: "Team",
      description: "Shared projects",
      price: "€30/user"
    }
  ]}
  defaultValue="pro"
/>

Installation

pnpm dlx shadcn@latest add @lumesec/px-choice
First timeRegister the @lumesec registry once, or install by URL+

Adds @lumesec to your components.json:

pnpm dlx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json

Or skip the setup and install by URL:

npx shadcn@latest add https://elements.lumesec.ai/r/px-choice.json

Usage

px-choice-example.tsx
import { PxChoice } from "@/components/lumesec/px-choice";

export function Example() {
  return (
    <PxChoice
      name="plan"
      label="Plan"
      defaultValue="pro"
      options={[
        { value: "free", label: "Free", description: "For trying things out", price: "€0" },
        { value: "pro", label: "Pro", description: "For daily work", price: "€20/mo" },
        { value: "team", label: "Team", description: "Shared projects", price: "€30/user" },
      ]}
    />
  );
}

Behaviour

A radio group as cards. The selection outline is made of pixels that fly from the old card to the new one, then keep circling slowly.

API reference

Props

Also accepts every prop of <div> (React.ComponentProps<"div">), spread onto the root element.

optionsTypereadonly PxChoiceOption[]

The cards: { value?: string; label: string; description?: string; price?: string }. value defaults to label; price is shown in monospace.

valueTypestring

Controlled selected value. Use with onValueChange.

defaultValueTypestringDefaultfirst option

Initially selected value when uncontrolled. A form reset restores it.

labelTypeReact.ReactNode

Label above the cards; names the radio group.

nameTypestring

Form field name; the selected value submits under it.

disabledTypebooleanDefaultfalse

Dims the group and disables the cards.

onValueChangeType(value: string) => void

Called when a different card is selected.

Ref

ref points at the root HTMLDivElement.

Keyboard

KeysAction
ArrowRight / ArrowDownSelect and focus the next card, wrapping at the end.
ArrowLeft / ArrowUpSelect and focus the previous card, wrapping at the start.

Accessibility

  • Cards are native buttons with role="radio" and aria-checked inside a role="radiogroup" labelled by label.
  • Roving tabindex: only the selected card is in the tab order.
  • The pixel outline and check are drawn on an aria-hidden canvas; the selection is exposed through aria-checked.
  • With name, a hidden input submits the selected card's value. There is no validation. Form reset restores defaultValue (or the first option) and calls onValueChange when that changes the value.
  • Reduced motion: the outline jumps to the selected card and stops circling.

Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from --lumesec. See Theming.

This component reads --foreground, --muted-foreground, --card, --border, --lumesec and --lumesec-shine.

Notes

  • Without reduced motion the outline animates continuously while the component is on screen.
  • Cards are laid out in an auto-fit grid with a 120 px minimum width.

Was this page helpful?