Radio Card
A selectable card built on the shared Radio component for rich single-choice forms.
RadioCard combines the native Radio component with a flexible card layout. Use it when a choice needs more context than a label: descriptions, icons, badges, prices, media, additional content, or validation messages. RadioCardGroup manages a set of mutually exclusive cards.
The selection control is always the shared Radio component. This keeps native radio semantics, keyboard behavior, focus treatment, and indicator sizing consistent with standalone radios.
Basic usage
import { RadioCard, RadioCardGroup } from '@mosmox/ui';
<RadioCardGroup label="Plan" name="plan" defaultValue="team">
<RadioCard value="personal" title="Personal" description="For individual projects" />
<RadioCard value="team" title="Team" description="For growing teams" badge="Popular" />
<RadioCard value="enterprise" title="Enterprise" description="For larger organizations" />
</RadioCardGroup>The group generates a native radio name when name is omitted. Only one card can be selected at a time.
Controlled and uncontrolled state
Use defaultValue for local state or value and onChange for controlled forms:
const [plan, setPlan] = useState('team');
<RadioCardGroup name="plan" value={plan} onChange={setPlan}>
<RadioCard value="personal" title="Personal" />
<RadioCard value="team" title="Team" />
</RadioCardGroup>RadioCardGroup.onChange receives (value, nativeEvent). A standalone card can use checked, defaultChecked, and onChange, whose arguments are (checked, value, nativeEvent).
Content slots
Every content slot accepts ReactNode:
icon— decorative icon before the card content.leading— custom content before the main text.badge— compact content next to the title.content— additional content below the description.footer— secondary content at the bottom of the main column.trailing— backwards-compatible alias for right-side content.rightContent— dedicated right-side slot for media, prices, status, or custom components.
<RadioCard
value="workspace"
title="Workspace"
description="A shared space for your team"
badge="Recommended"
rightContentAlign="center"
rightContent={<img alt="" src="/workspace-preview.jpg" width={72} height={48} />}
content="Unlimited projects and shared permissions"
footer="Cancel anytime"
/>rightContent takes precedence when both rightContent and the legacy trailing prop are provided. rightContentAlign accepts start, center, or end and aligns the content vertically within the card. The default is center.
Sizes, variants, and indicators
RadioCard has exactly three sizes:
| Size | Use |
|---|---|
xs | Extra-small, compact interfaces. |
sm | Small, dense forms and settings. |
default | Standard card density. |
There is no large RadioCard size. The visual variants are outline, soft, filled, and elevated.
The shared Radio indicator can be placed at start (default), end, top, or none when selected-card styling is enough.
<RadioCardGroup direction="horizontal" columns={3} size="sm" variant="soft" indicator="top">
<RadioCard value="one" title="One" />
<RadioCard value="two" title="Two" />
<RadioCard value="three" title="Three" />
</RadioCardGroup>direction controls the group layout (vertical or horizontal). columns controls the grid column count and gap accepts a number in pixels or any CSS length string.
States and validation
disabledprevents interaction and applies the disabled visual state.loadingdisables the card and displays a loading indicator.requiredapplies native required validation.- Card
errordisplays an individual validation message. - Group
errordisplays a message below the complete group.
<RadioCardGroup label="Delivery" error="Choose a delivery method." required>
<RadioCard value="standard" title="Standard" description="3–5 business days" />
<RadioCard value="express" title="Express" description="Tomorrow" loading />
<RadioCard value="pickup" title="Pick up" description="Unavailable" disabled />
</RadioCardGroup>ConfigProvider defaults
RadioCardGroup reads shared defaults from ConfigProvider.components.radioCard:
<ConfigProvider components={{ radioCard: { variant: 'elevated', size: 'sm', indicator: 'top' } }}>
<RadioCardGroup>
<RadioCard value="a" title="Option A" />
<RadioCard value="b" title="Option B" />
</RadioCardGroup>
</ConfigProvider>The provider supports size, variant, and indicator. Explicit group props override provider defaults. All visual colors use ConfigProvider design tokens, including surface, light background, control border, text, muted text, focus, primary, danger, and control radius tokens. The selected card uses the same darker control-border treatment as the open Accordion state.
Accessibility and keyboard behavior
The implementation renders native input type="radio" elements inside a semantic fieldset. A visible group label becomes the fieldset legend; use aria-label when there is no visible label. Card descriptions are connected with aria-describedby, and aria-label is available for cards whose visible content is not sufficient.
Disabled and loading cards are not interactive. Focus-visible styles identify the active card, and the browser's native radio-group keyboard behavior supports Tab and Arrow keys.
API
RadioCardGroup
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Radio cards in the group. |
label | ReactNode | — | Visible group label and fieldset legend. |
description | ReactNode | — | Supporting text below the legend. |
error | ReactNode | — | Group-level validation message. |
name | string | generated | Native radio group name. |
value | string | — | Controlled selected value. |
defaultValue | string | — | Initial selected value for uncontrolled state. |
onChange | (value, event) => void | — | Called with the selected value and native change event. |
direction | 'vertical' | 'horizontal' | vertical | Group layout direction. |
columns | number | string | — | Grid column count. |
gap | number | string | 12 | Gap between cards; numbers are pixels. |
size | 'xs' | 'sm' | 'default' | — | Shared card size. |
variant | 'outline' | 'soft' | 'filled' | 'elevated' | — | Shared card variant. |
indicator | 'start' | 'end' | 'top' | 'none' | — | Shared indicator placement. |
disabled | boolean | false | Disables every card in the group. |
required | boolean | false | Applies native required state to cards. |
className | string | — | Custom group class. |
style | CSSProperties | — | Inline styles and CSS custom properties. |
aria-label | string | — | Accessible group name when no visible label is used. |
RadioCard
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value submitted by the group. |
title | ReactNode | — | Main card heading. |
description | ReactNode | — | Supporting text. |
icon | ReactNode | — | Decorative icon before the main content. |
leading | ReactNode | — | Custom leading content. |
badge | ReactNode | — | Content next to the title. |
content | ReactNode | — | Additional main-column content. |
footer | ReactNode | — | Footer content in the main column. |
trailing | ReactNode | — | Legacy alias for rightContent. |
rightContent | ReactNode | — | Content in the right side of the card. |
rightContentAlign | 'start' | 'center' | 'end' | center | Vertical alignment of right-side content. |
name | string | — | Native radio name for standalone cards. |
checked | boolean | — | Controlled standalone checked state. |
defaultChecked | boolean | false | Initial standalone checked state. |
disabled | boolean | false | Prevents interaction. |
loading | boolean | false | Disables the card and shows a spinner. |
required | boolean | — | Native required state. |
size | 'xs' | 'sm' | 'default' | default | Card density. |
variant | 'outline' | 'soft' | 'filled' | 'elevated' | outline | Card appearance. |
indicator | 'start' | 'end' | 'top' | 'none' | start | Radio indicator placement. |
cardDirection | 'horizontal' | 'vertical' | horizontal | Card content direction mode. |
error | string | — | Individual validation message. |
onChange | (checked, value, event) => void | — | Standalone card change callback. |
className | string | — | Custom outer field class. |
style | CSSProperties | — | Inline styles on the outer field. |
aria-label | string | — | Accessible card name when visible content is insufficient. |
RadioCardRightContentAlign is exported as a type for consumers that build wrappers around the right-side slot.