Skip to content
All Taste components

Forms

Radio cards

Plan choices as cards, where the whole card is the click target and one is picked.

selected · unselected · hover · recommended badge

Choose a plan
import { Check } from "lucide-react";

export type Plan = { id: string; name: string; price: string; features: string[]; recommended?: boolean };

export function PlanRadioCards({ plans, value, onChange, name = "plan" }:
  { plans: Plan[]; value: string; onChange: (id: string) => void; name?: string }) {
  return (
    <fieldset className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3">
      <legend className="sr-only">Choose a plan</legend>
      {plans.map((p) => (
        <label
          key={p.id}
          className="relative grid cursor-pointer content-start gap-0.5 rounded-lg border border-border bg-bg p-4 transition hover:border-border-strong has-[:checked]:border-primary has-[:checked]:bg-primary-soft/40 has-[:checked]:ring-1 has-[:checked]:ring-inset has-[:checked]:ring-primary has-[:focus-visible]:outline has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-ring"
        >
          <input
            type="radio"
            name={name}
            value={p.id}
            checked={value === p.id}
            onChange={() => onChange(p.id)}
            className="absolute right-4 top-4 size-[18px] cursor-pointer appearance-none rounded-full border-[1.5px] border-border-strong bg-bg transition checked:border-[6px] checked:border-primary focus-visible:outline-none"
          />
          <span className="flex flex-wrap items-center gap-2 pr-7 font-medium text-fg">
            {p.name}
            {p.recommended && <span className="rounded-full bg-primary-soft px-2 py-0.5 text-xs font-medium text-primary-soft-fg">Recommended</span>}
          </span>
          <span className="mt-1.5 text-[22px] font-medium tracking-tight tabular-nums text-fg">
            {p.price} <span className="text-[13px] font-normal tracking-normal text-fg-subtle">/ month</span>
          </span>
          <ul className="mt-3 grid gap-1.5 border-t border-border pt-3 text-[13px] text-fg-muted">
            {p.features.map((f) => (
              <li key={f} className="flex items-center gap-2"><Check className="size-3.5 shrink-0 text-fg-subtle" aria-hidden />{f}</li>
            ))}
          </ul>
        </label>
      ))}
    </fieldset>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.