Skip to content

Card

Rounded container surface for grouping related content.

Card content
<Card {...args} style={{width: 360}}>
<Text type="body">Card content</Text>
</Card>
default
section
transparent
muted
<div
style={{display: 'grid', gap: 16, gridTemplateColumns: 'repeat(4, 1fr)'}}>
{(['default', 'section', 'transparent', 'muted'] as const).map(
variant => (
<Card key={variant} padding={4} variant={variant}>
<Text type="body">{variant}</Text>
</Card>
),
)}
</div>
blue
cyan
gray
green
orange
pink
purple
red
teal
yellow
<div
style={{display: 'grid', gap: 16, gridTemplateColumns: 'repeat(5, 1fr)'}}>
{(
[
'blue',
'cyan',
'gray',
'green',
'orange',
'pink',
'purple',
'red',
'teal',
'yellow',
] as const
).map(color => (
<Card color={color} key={color} padding={4}>
<Text type="body">{color}</Text>
</Card>
))}
</div>
padding={0}
padding={1}
padding={2}
padding={3}
padding={4}
padding={6}
padding={8}
padding={10}
() => {
const steps: SpacingToken[] = [0, 1, 2, 3, 4, 6, 8, 10];
return (
<div style={{display: 'flex', flexWrap: 'wrap', gap: 16}}>
{steps.map(step => (
<Card key={step} padding={step}>
<div
className={css({
bg: 'surface.blue',
borderRadius: 'md',
p: '2',
})}>
<Text type="supporting">{`padding={${step}}`}</Text>
</div>
</Card>
))}
</div>
);
}
A section card has no border or border-radius, making it suitable for use inside a larger container.
Another section
Section with color
<div className={containerStyle} style={{width: 400}}>
<Card padding={4} variant="section">
<Text type="body">
A section card has no border or border-radius, making it suitable for
use inside a larger container.
</Text>
</Card>
<Divider />
<Card padding={4} variant="section">
<Text type="body">Another section</Text>
</Card>
<Divider />
<Card color="blue" padding={4} variant="section">
<Text type="body">Section with color</Text>
</Card>
</div>
Content above the dividerThe divider stretches to the card edges using the --card-padding custom property.
<Card padding={4} style={{width: 360}}>
<VStack gap={3}>
<Text type="body">Content above the divider</Text>
<Divider isFullBleed />
<Text type="body">
The divider stretches to the card edges using the{' '}
<code>--card-padding</code> custom property.
</Text>
</VStack>
</Card>
Outer card
Nested blue
Nested green
Nested muted card
<Card padding={4} style={{width: 480}}>
<VStack gap={3}>
<Text type="label">Outer card</Text>
<div style={{display: 'grid', gap: 12, gridTemplateColumns: '1fr 1fr'}}>
<Card color="blue" padding={3}>
<Text type="supporting">Nested blue</Text>
</Card>
<Card color="green" padding={3}>
<Text type="supporting">Nested green</Text>
</Card>
</div>
<Card padding={3} variant="muted">
<Text type="supporting">Nested muted card</Text>
</Card>
</VStack>
</Card>
Featured project
Active
A short description of the project with enough detail to understand what it does at a glance.
<Card padding={4} style={{width: 360}}>
<VStack gap={3}>
<HStack align="center" gap={3} justify="between">
<HStack align="center" gap={2}>
<Icon color="accent" icon={Star} />
<Text type="label">Featured project</Text>
</HStack>
<Badge color="success" label="Active" />
</HStack>
<Text color="secondary" type="body">
A short description of the project with enough detail to understand
what it does at a glance.
</Text>
<Divider isFullBleed />
<HStack gap={2} justify="end">
<Button label="Details" size="sm" variant="ghost" />
<Button label="Open" size="sm" variant="primary" />
</HStack>
</VStack>
</Card>
ProfileEdit your personal info
NotificationsConfigure alerts
SettingsApp preferences
() => {
const items = [
{icon: User, title: 'Profile', description: 'Edit your personal info'},
{icon: Bell, title: 'Notifications', description: 'Configure alerts'},
{icon: Settings, title: 'Settings', description: 'App preferences'},
];
return (
<VStack gap={2} style={{width: 400}}>
{items.map(item => (
<Card key={item.title} padding={3}>
<HStack align="center" gap={3}>
<Icon icon={item.icon} />
<VStack gap={0}>
<Text type="label">{item.title}</Text>
<Text color="secondary" type="supporting">
{item.description}
</Text>
</VStack>
</HStack>
</Card>
))}
</VStack>
);
}

Settings

Sidebar
Main content area
<Card style={{height: 360, width: 600}}>
<Layout
content={
<LayoutContent>
<Text type="body">Main content area</Text>
</LayoutContent>
}
footer={
<LayoutFooter
primaryButton={<Button label="Save" variant="primary" />}
secondaryButton={<Button label="Cancel" variant="ghost" />}
/>
}
header={<LayoutHeader title="Settings" />}
start={
<LayoutPanel width={160}>
<Text type="body">Sidebar</Text>
</LayoutPanel>
}
/>
</Card>
Content to import
Documents24 PDF and text files
Recommended
Images128 photos and illustrations
Archives6 compressed archives
function ImportSelection(): React.JSX.Element {
const [value, setValue] = useState(['documents']);
const items = [
{
description: '24 PDF and text files',
icon: FileText,
label: 'Documents',
value: 'documents',
},
{
description: '128 photos and illustrations',
icon: Image,
label: 'Images',
value: 'images',
},
{
description: '6 compressed archives',
icon: FileArchive,
label: 'Archives',
value: 'archives',
},
];
return (
<CheckboxGroup
className={groupClassName}
label="Content to import"
onChange={setValue}
value={value}>
{items.map(item => (
<CheckboxCard
key={item.value}
label={item.label}
padding={4}
value={item.value}>
<HStack align="center" gap={3} justify="between">
<HStack align="center" gap={2}>
<Icon color="secondary" icon={item.icon} />
<VStack gap={0}>
<Text type="label">{item.label}</Text>
<Text color="secondary" type="supporting">
{item.description}
</Text>
</VStack>
</HStack>
{item.value === 'documents' ? (
<Badge color="blue" label="Recommended" />
) : null}
</HStack>
</CheckboxCard>
))}
</CheckboxGroup>
);
}
<ImportSelection />
Selection states
Selected option
Disabled option
<CheckboxGroup
className={groupClassName}
label="Selection states"
onChange={() => {}}
value={['selected']}>
<CheckboxCard label="Selected option" padding={4} value="selected">
<Text type="label">Selected option</Text>
</CheckboxCard>
<CheckboxCard
isDisabled
label="Disabled option"
padding={4}
value="disabled">
<Text type="label">Disabled option</Text>
</CheckboxCard>
</CheckboxGroup>
Account settingsManage your profile and preferences
<ClickableCard
className={cardClassName}
label="Open account settings"
onClick={() => {}}
padding={4}
>
{( <HStack align="center" gap={3} justify="between"> <HStack align="center" gap={2}> <Icon icon={Settings} /> <VStack gap={0}> <Text type="label">Account settings</Text> <Text color="secondary" type="supporting"> Manage your profile and preferences </Text> </VStack> </HStack> <Icon color="secondary" icon={ChevronRight} /> </HStack> )}
</ClickableCard>
Account settingsManage your profile and preferences
<ClickableCard
className={cardClassName}
label="Open account settings"
onClick={undefined}
padding={4}
href="/settings/account"
>
{( <HStack align="center" gap={3} justify="between"> <HStack align="center" gap={2}> <Icon icon={Settings} /> <VStack gap={0}> <Text type="label">Account settings</Text> <Text color="secondary" type="supporting"> Manage your profile and preferences </Text> </VStack> </HStack> <Icon color="secondary" icon={ChevronRight} /> </HStack> )}
</ClickableCard>
Quarterly reportOpen the report without capturing its independent actions.
<ClickableCard {...args}>
<VStack gap={3}>
<VStack gap={0}>
<Text type="label">Quarterly report</Text>
<Text color="secondary" type="supporting">
Open the report without capturing its independent actions.
</Text>
</VStack>
<HStack gap={2}>
<Button label="Archive report" onClick={() => {}} size="sm" />
</HStack>
</VStack>
</ClickableCard>
Account settingsManage your profile and preferences
<ClickableCard
className={cardClassName}
label="Open account settings"
onClick={() => {}}
padding={4}
disabledReason="Account settings are unavailable"
isDisabled
>
{( <HStack align="center" gap={3} justify="between"> <HStack align="center" gap={2}> <Icon icon={Settings} /> <VStack gap={0}> <Text type="label">Account settings</Text> <Text color="secondary" type="supporting"> Manage your profile and preferences </Text> </VStack> </HStack> <Icon color="secondary" icon={ChevronRight} /> </HStack> )}
</ClickableCard>
Account settingsManage your profile and preferences
<ClickableCard
className={cardClassName}
label="Open account settings"
onClick={() => {}}
padding={4}
isReadOnly
>
{( <HStack align="center" gap={3} justify="between"> <HStack align="center" gap={2}> <Icon icon={Settings} /> <VStack gap={0}> <Text type="label">Account settings</Text> <Text color="secondary" type="supporting"> Manage your profile and preferences </Text> </VStack> </HStack> <Icon color="secondary" icon={ChevronRight} /> </HStack> )}
</ClickableCard>
Plan
FreeFor personal projects
ProFor growing teams
Popular
EnterpriseFor organizations at scale
function PlanSelection({isReadOnly = false}: {isReadOnly?: boolean}) {
const [value, setValue] = useState('pro');
const plans = [
{description: 'For personal projects', label: 'Free', value: 'free'},
{description: 'For growing teams', label: 'Pro', value: 'pro'},
{
description: 'For organizations at scale',
label: 'Enterprise',
value: 'enterprise',
},
];
return (
<RadioGroup
className={groupClassName}
isReadOnly={isReadOnly}
label="Plan"
onChange={setValue}
value={value}>
{plans.map(plan => (
<RadioCard
key={plan.value}
label={plan.label}
padding={4}
value={plan.value}>
<HStack align="center" gap={3} justify="between">
<VStack gap={0}>
<Text type="label">{plan.label}</Text>
<Text color="secondary" type="supporting">
{plan.description}
</Text>
</VStack>
{plan.value === 'pro' ? (
<Badge color="blue" label="Popular" />
) : null}
</HStack>
</RadioCard>
))}
</RadioGroup>
);
}
<PlanSelection />
Plan
FreeFor personal projects
ProFor growing teams
Popular
EnterpriseFor organizations at scale
function PlanSelection({isReadOnly = false}: {isReadOnly?: boolean}) {
const [value, setValue] = useState('pro');
const plans = [
{description: 'For personal projects', label: 'Free', value: 'free'},
{description: 'For growing teams', label: 'Pro', value: 'pro'},
{
description: 'For organizations at scale',
label: 'Enterprise',
value: 'enterprise',
},
];
return (
<RadioGroup
className={groupClassName}
isReadOnly={isReadOnly}
label="Plan"
onChange={setValue}
value={value}>
{plans.map(plan => (
<RadioCard
key={plan.value}
label={plan.label}
padding={4}
value={plan.value}>
<HStack align="center" gap={3} justify="between">
<VStack gap={0}>
<Text type="label">{plan.label}</Text>
<Text color="secondary" type="supporting">
{plan.description}
</Text>
</VStack>
{plan.value === 'pro' ? (
<Badge color="blue" label="Popular" />
) : null}
</HStack>
</RadioCard>
))}
</RadioGroup>
);
}
<PlanSelection isReadOnly />
Plan
Free plan
Pro plan
<RadioGroup
className={groupClassName}
label="Plan"
onChange={() => {}}
value="free">
<RadioCard label="Free" padding={4} value="free">
<Text type="label">Free plan</Text>
</RadioCard>
<RadioCard isDisabled label="Pro" padding={4} value="pro">
<Text type="label">Pro plan</Text>
</RadioCard>
</RadioGroup>

Rounded container surface for grouping related content.

PropTypeDefaultDescription
color"red" | "orange" | "yellow" | "green" | "teal" | "cyan" | "blue" | "purple" | "pink" | "gray"Decorative surface color. When set, overrides the variant's default background with the corresponding surface color token.
data-testidstringTest ID applied to the root element.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 100Inner padding step.
variant"section" | "default" | "transparent" | "muted"'default'Visual style variant. - default — bordered, rounded container with bg background. - section — flat container with no border or border-radius, for use inside a larger container. - transparent — no background. - muted — subtle background, no visible border.

A rich Card option that participates in a CheckboxGroup.

PropTypeDefaultDescription
children*ReactNodeRich content rendered beside the selection indicator.
classNamestringAdditional CSS class names applied to the card root.
color"red" | "orange" | "yellow" | "green" | "teal" | "cyan" | "blue" | "purple" | "pink" | "gray"Decorative surface color. When set, overrides the variant's default background with the corresponding surface color token.
data-testidstringTest ID applied to the card root.
isDisabledbooleanfalseWhether this selection item is disabled.
label*stringAccessible name for the native checkbox or radio. It is not rendered.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 100Inner padding step.
refRef<HTMLDivElement>Ref forwarded to the card root.
styleCSSPropertiesInline styles applied to the card root.
value*stringValue represented by this selection item.
variant"section" | "default" | "transparent" | "muted"'default'Visual style variant.

A Card surface that delegates activation to a real button or link while keeping nested controls independently operable.

PropTypeDefaultDescription
color"red" | "orange" | "yellow" | "green" | "teal" | "cyan" | "blue" | "purple" | "pink" | "gray"Decorative surface color. When set, overrides the variant's default background with the corresponding surface color token.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 100Inner padding step.
variant"section" | "default" | "transparent" | "muted"'default'Visual style variant.
refRef<HTMLDivElement>Ref forwarded to the outer, non-interactive container.
children*ReactNodeNon-interactive phrasing content that becomes the action's visual target.
classNamestringAdditional CSS class names applied to the root element.
data-testidstringTest ID applied to the root element.
disabledReasonstringExplanation shown in a tooltip and exposed as the control's accessible description while disabled.
hrefstringNavigation URL. When provided, the action renders as a link and takes precedence over onClick.
isDisabledbooleanfalseWhether the action is disabled. Disabled actions remain focusable and use aria-disabled so their reason can be discovered.
isReadOnlybooleanfalseWhether to preserve the content's appearance while removing interaction.
label*stringAccessible name for the action. It is not rendered visually.
onClickMouseEventHandler<HTMLElement>Called when the action is activated. Also fires for link clicks when href is provided.
relstringLink relationship. noopener noreferrer are added for _blank targets.
styleCSSPropertiesInline styles applied to the root element.
targetstring'_self'Link browsing-context target.

A rich Card option that participates in a RadioGroup.

PropTypeDefaultDescription
children*ReactNodeRich content rendered beside the selection indicator.
classNamestringAdditional CSS class names applied to the card root.
color"red" | "orange" | "yellow" | "green" | "teal" | "cyan" | "blue" | "purple" | "pink" | "gray"Decorative surface color. When set, overrides the variant's default background with the corresponding surface color token.
data-testidstringTest ID applied to the card root.
isDisabledbooleanfalseWhether this selection item is disabled.
label*stringAccessible name for the native checkbox or radio. It is not rendered.
padding0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 100Inner padding step.
refRef<HTMLDivElement>Ref forwarded to the card root.
styleCSSPropertiesInline styles applied to the card root.
value*stringValue represented by this selection item.
variant"section" | "default" | "transparent" | "muted"'default'Visual style variant.