Radio Cardsv0.1.13Documentation Under Review
A deprecated radio group component with card-style visual presentation. Use RadioGroup component for new implementations with better accessibility and simpler API.
Deprecated Component
This component is deprecated and should not be used in new projects. Please use the RadioGroup component instead with custom card styling, which provides better accessibility, simpler API, and improved functionality.
Installation
Install the RadioCards component from the Catalyst component library:
npm install @pmi/catalyst-radio-cardsOverview
The RadioCards component provides a card-style visual presentation for radio button groups, built on Radix UI's RadioGroup primitive. Each option is displayed as a selectable card with title and optional label text.
Migration Required: This component is deprecated. For new implementations, use RadioGroup with custom card-style CSS, which provides: - Simpler API with fewer required components - Better accessibility with automatic ARIA attributes - More flexible styling options - Improved keyboard navigation - Better state management
Key Features
- Card-Style Selection: Visual cards instead of traditional radio buttons
- Four Size Variants: xs, sm, md, lg for different contexts
- Danger State: Visual warning for validation errors
- Flexible Layout: Title and label can be arranged flexibly
- Radix UI Foundation: Built on accessible RadioGroup primitive
- Disabled State: Visual indication with opacity and pointer-events
- Controlled State: Value and onValueChange for form integration
Examples
Basic Usage
Radio cards with title and label above.
Label Below Title
Flexible content ordering with label below title.
Title Only
Compact cards with only title text.
Size Variants
Four size options for different UI contexts.
Large
Medium (Default)
Small
Extra Small
Danger State
Use danger prop for validation errors or warning states.
Disabled State
Disabled state prevents user interaction.
API Reference
RadioCardsRoot
The root container managing radio group state and context.
Prop
Type
RadioCardsItem
Individual selectable card item within the radio group.
Prop
Type
RadioCardsTitle
Title text displayed prominently on the card (typically price or main value).
Prop
Type
RadioCardsLabel
Label text for additional context (typically plan name or option description).
Prop
Type
Size Specifications
The RadioCards component provides four size variants with responsive padding and typography:
Prop
Type
Border Width Compensation
When a card is selected, the border width increases from --border-xs to --border-md. To prevent layout shift, the padding is reduced:
- Normal: Full padding with thin border
- Selected: Reduced padding (compensates for thicker border)
- Net Result: Card dimensions remain constant
Styling Details
State-Based Styling
The component uses data attributes for state-based styling:
// Size context from RadioCardsRoot
<RadioCardsRoot data-size="md" data-danger="false">
{/* Items respond to parent context */}
<RadioCardsItem data-state="checked">{/* Title and Label change colors based on state */}</RadioCardsItem>
</RadioCardsRoot>Color Transitions
Colors change based on state:
- Default: Primary text color with dark border
- Hover: White text with filled background
- Selected: Thicker border, maintained colors
- Danger: Red text and border, white on hover
- Disabled: Reduced opacity, no pointer events
Typography Scale
Title sizes scale with card size:
- xs/sm:
text-header-2xs - md:
text-header-xs - lg:
text-header-md
Label always uses text-body-sm regardless of card size.
Design Tokens
The RadioCards component uses Catalyst design tokens:
-
Colors:
- Text:
--text-primary,--text-white,--text-danger - Border:
--border-off-black-dark,--border-danger - Fill:
--fill-off-black,--fill-danger
- Text:
-
Spacing:
- Padding:
--scale-8,--scale-12,--scale-16,--scale-20,--scale-24 - Gap:
--scale-2,--scale-4,--scale-8,--scale-12
- Padding:
-
Border:
- Width:
--border-xs(unchecked),--border-md(checked) - Radius:
--rounded-xs
- Width:
-
Effects:
- Opacity:
--opacity-disabled - Outline:
--border-sm(focus ring) - Transitions:
transition-colors
- Opacity:
Accessibility
Limited Accessibility: This component has basic accessibility via Radix UI. Use RadioGroup for enhanced accessibility features.
Keyboard Navigation
- Tab: Move focus between radio cards
- Arrow Keys: Navigate between cards in the group
- Space: Select focused card
- Enter: Select focused card (if applicable)
ARIA Attributes
Provided automatically by Radix UI RadioGroup:
role="radiogroup"on rootrole="radio"on itemsaria-checkedstate on itemsaria-disabledwhen disabled
WCAG 2.1 AA Compliance
- Contrast Ratios: Text and borders meet 4.5:1 minimum
- Touch Targets: All sizes meet minimums (xs: 32px+, others: 36px+)
- Focus Indicators: Visible outline on focus
- Keyboard Navigation: Full keyboard support via Radix UI
- Disabled State: Opacity and pointer-events for clear indication
Best Practices
// ✓ Always provide value prop for controlled state
<RadioCardsRoot value={selected} onValueChange={setSelected}>
<RadioCardsItem value="option-1">
<RadioCardsTitle>Option 1</RadioCardsTitle>
</RadioCardsItem>
</RadioCardsRoot>
// ✓ Use unique values for each item
<RadioCardsItem value="unique-1">...</RadioCardsItem>
<RadioCardsItem value="unique-2">...</RadioCardsItem>
// ✓ Provide meaningful titles and labels
<RadioCardsItem value="pro">
<RadioCardsLabel>Professional Plan</RadioCardsLabel>
<RadioCardsTitle>$29/month</RadioCardsTitle>
</RadioCardsItem>Common Patterns
Pricing Selection
import { useState } from 'react';
function PricingSelector() {
const [plan, setPlan] = useState('basic');
return (
<RadioCardsRoot value={plan} onValueChange={setPlan} size="lg">
<RadioCardsItem value="basic">
<RadioCardsLabel>Basic</RadioCardsLabel>
<RadioCardsTitle>$9/mo</RadioCardsTitle>
</RadioCardsItem>
<RadioCardsItem value="pro">
<RadioCardsLabel>Pro</RadioCardsLabel>
<RadioCardsTitle>$29/mo</RadioCardsTitle>
</RadioCardsItem>
<RadioCardsItem value="enterprise">
<RadioCardsLabel>Enterprise</RadioCardsLabel>
<RadioCardsTitle>Custom</RadioCardsTitle>
</RadioCardsItem>
</RadioCardsRoot>
);
}Donation Amount
<RadioCardsRoot value={amount} onValueChange={setAmount} className="flex gap-[var(--scale-8)]">
{[25, 50, 100, 250].map((value) => (
<RadioCardsItem key={value} value={String(value)} className="flex-1">
<RadioCardsTitle>${value}</RadioCardsTitle>
</RadioCardsItem>
))}
</RadioCardsRoot>Shipping Options
<RadioCardsRoot value={shipping} onValueChange={setShipping} size="md">
<RadioCardsItem value="standard">
<RadioCardsTitle>Standard</RadioCardsTitle>
<RadioCardsLabel>5-7 business days</RadioCardsLabel>
</RadioCardsItem>
<RadioCardsItem value="express">
<RadioCardsTitle>Express</RadioCardsTitle>
<RadioCardsLabel>2-3 business days</RadioCardsLabel>
</RadioCardsItem>
<RadioCardsItem value="overnight">
<RadioCardsTitle>Overnight</RadioCardsTitle>
<RadioCardsLabel>Next day delivery</RadioCardsLabel>
</RadioCardsItem>
</RadioCardsRoot>Form Validation
<form onSubmit={handleSubmit}>
<RadioCardsRoot required danger={hasError} value={selection} onValueChange={setSelection}>
<RadioCardsItem value="option-1">
<RadioCardsTitle>Option 1</RadioCardsTitle>
</RadioCardsItem>
<RadioCardsItem value="option-2">
<RadioCardsTitle>Option 2</RadioCardsTitle>
</RadioCardsItem>
</RadioCardsRoot>
{hasError && <span className="text-sm text-red-600">Please select an option</span>}
</form>Migration Guide
Migrating to RadioGroup
Replace this deprecated component with RadioGroup and custom card styling for better maintainability:
Before (RadioCards - Deprecated)
import { RadioCardsRoot, RadioCardsItem, RadioCardsTitle, RadioCardsLabel } from '@pmi/catalyst-radio-cards';
<RadioCardsRoot value={value} onValueChange={setValue}>
<RadioCardsItem value="option-1">
<RadioCardsLabel>Plan Name</RadioCardsLabel>
<RadioCardsTitle>$100</RadioCardsTitle>
</RadioCardsItem>
<RadioCardsItem value="option-2">
<RadioCardsLabel>Plan Name</RadioCardsLabel>
<RadioCardsTitle>$200</RadioCardsTitle>
</RadioCardsItem>
</RadioCardsRoot>;After (RadioGroup with Custom Styling - Recommended)
import { RadioGroup } from '@pmi/catalyst-radio-group';
<RadioGroup value={value} onValueChange={setValue} className="flex gap-3">
<RadioGroup.Item value="option-1" className="flex flex-col p-4 border-2 rounded-lg hover:bg-gray-50">
<RadioGroup.Indicator className="sr-only" />
<span className="text-sm text-gray-600">Plan Name</span>
<span className="text-2xl font-bold">$100</span>
</RadioGroup.Item>
<RadioGroup.Item value="option-2" className="flex flex-col p-4 border-2 rounded-lg hover:bg-gray-50">
<RadioGroup.Indicator className="sr-only" />
<span className="text-sm text-gray-600">Plan Name</span>
<span className="text-2xl font-bold">$200</span>
</RadioGroup.Item>
</RadioGroup>;Troubleshooting
Cards Not Changing State
Ensure value and onValueChange are properly connected:
// ✓ Correct - controlled state
const [value, setValue] = useState('')
<RadioCardsRoot value={value} onValueChange={setValue}>
...
</RadioCardsRoot>
// ✗ Incorrect - missing onValueChange
<RadioCardsRoot value={value}>
...
</RadioCardsRoot>Layout Shift on Selection
This is expected behavior due to border width change. The padding compensation may not be perfect for all custom styles.
// Normal: 2px border, full padding
// Selected: 4px border, reduced padding
// Net result: Card size stays same (mostly)Hover Colors Not Showing
Ensure you're not overriding the group-hover/item: classes:
// ✓ Correct - preserves hover behavior
<RadioCardsItem className="custom-class">
...
</RadioCardsItem>
// ⚠ Warning - may override hover colors
<RadioCardsTitle className="text-blue-500">
...
</RadioCardsTitle>Deprecation Notice
Component Deprecated
This component is deprecated and will be removed in a future major version.
Migration Path:
- Use RadioGroup with custom card-style CSS
- Simpler component structure (fewer nested components)
- More flexible styling options
- Better accessibility with automatic ARIA
- Easier to maintain and customize
Timeline:
- Current: Deprecated but functional
- Support: Maintained for bug fixes only
- Removal: Planned for next major version
Please migrate to RadioGroup with custom styling at your earliest convenience.
Related Components
- RadioGroup - Recommended replacement with custom styling
- Radio - Deprecated basic radio input
- Checkbox - For multiple selections
- Card - For general card layouts