Mode Card
A selectable tile that represents one mode in a small picker — study mode, difficulty preset, theme variant, layout density. Tapping the card toggles its selected state; the active card paints the accent surface, inactive cards paint a neutral surface with a hairline border.
Customize
Size
lg
State
Content
Installation
npx shadcn@latest add https://craftbits.dev/r/mode-card.jsonUsage
import { ModeCard } from "@craft-bits/core";
import { Zap } from "lucide-react";
<ModeCard
icon={<Zap size={20} />}
title="Practice"
description="Drill the trickier sub-skills."
selected={picked === "practice"}
onClick={() => setPicked("practice")}
/>A radio-group picker:
<div role="radiogroup" aria-label="Study mode" className="flex flex-col gap-3">
{modes.map((m) => (
<ModeCard
key={m.id}
icon={<m.Icon size={20} />}
title={m.title}
description={m.description}
selected={picked === m.id}
onClick={() => setPicked(m.id)}
/>
))}
</div>A compact grid of size="sm" chips:
<div className="grid grid-cols-3 gap-2">
{densities.map((d) => (
<ModeCard
key={d.id}
size="sm"
icon={<d.Icon size={14} />}
title={d.title}
description={d.description}
selected={picked === d.id}
onClick={() => setPicked(d.id)}
/>
))}
</div>Anatomy
- Icon — top-left (
lg) or top-center (sm). InheritscurrentColorso it tracks the selected vs idle text tone. - Title — serif heading. Short labels read best (
Study,Practice,Review). - Description — small supporting line. Tone flips between
opacity-80on the accent fill andcb-fg-muted/cb-fg-subtleon the idle surface. - Surface —
bg-cb-accentwhen selected;bg-cb-bg-elevatedwith a hairlinecb-borderwhen idle.
Sizes
lg(default) — row-style card: icon on the left, title above description on the right. The "primary" picker treatment.sm— column-style chip: icon stacked above title and description. The "secondary" / grid-of-options treatment.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | required | Heading. |
description | ReactNode | — | One-line supporting text under the title. |
icon | ReactNode | — | Inline icon — lucide-react glyph or any SVG. |
selected | boolean | false | Whether this card is the currently picked option. |
size | 'lg' | 'sm' | 'lg' | Row-style or column-style. |
disabled | boolean | — | Locks the card. Drops to 40% opacity and suppresses the tap scale. |
onClick | (e) => void | — | Click handler. Caller owns the picker state. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- Renders as
<button type="button" role="radio" aria-checked={selected}>. Wrap a stack in arole="radiogroup"element with anaria-label. - The icon is
aria-hidden="true"— pair it with a meaningfultitleso the accessible name always conveys the mode. disabledsets both the nativedisabledattribute andaria-disabled="true".- Focus visibly highlights via
focus-visible:ring-cb-accent.
Credits
- Extracted from:
algoflashcards(src/platform/ui/ModeCard.tsx). The original used avariant: "primary" | "secondary"switch plus adisabledboolean and a free-floatingindexprop. The library version renamesvarianttosize, dropsindex, exposes a first-classselectedboolean instead of inferring active state from!disabled, replacesbevel-tile/bg-cardwith--cb-*semantic tokens, swapsSPRING.snappyforSPRINGS.snap, and migrates the inlinewhileTapto the canonicalTAP_SCALEconstant.