Card
The generic surface primitive, composed from typed slots for regions, text, and summary figures, and optionally pressable.
Implementation previewEvery example. In one place.
Explore every named example, including states, compositions, and theme variants.
Example source
packages/design-system/src/components/cards/Card/Card.stories.tsxExport PlaygroundImplementation preview Local Storybook build 0.2.0-preview.1. Source specification status does not approve this runtime.
Stories + source JSON ↗Local specification source status: preview. Implementation preview pending sign-off.
The generic surface primitive, composed from typed slots for regions, text, and summary figures, and optionally pressable.
Implementation API 10 entry points · source-derived preview
Read the actual callable API and preserve its composition constraints. Each union branch is a complete alternative; props marked forbidden cannot be supplied.
import { Card } from '@cloakedinc/design-system';CardCardProps
packages/design-system/src/components/cards/Card/CardRoot/CardRoot.tsx:11
| Prop | Type | Presence | Notes |
|---|---|---|---|
surface | CardSurface | undefined | Optional | Surface fill. Defaults to `default`; `highlight` swaps in the banner container color.Source default: 'default' |
radius | CardRadius | undefined | Optional | Corner radius for the card surface. Defaults to `lg`.Source default: 'lg' |
layout | CardLayout | undefined | Optional | Direction slots flow. Defaults to `stack`; `split` sits them side by side with an 8px gap.Source default: 'stack' |
padding | CardPadding | undefined | Optional | Padding on the surface itself. Defaults to `none` because the slots pad themselves —
`default` adds 16px that stacks on top of any padded slot.Source default: 'none' |
children | React.ReactNode | Required | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
onPress | (() => void) | undefined | Optional | Providing this renders the root as a pressable `<button>` rather than a `<div>`. |
ariaLabel | string | undefined | Optional | Accessible label for pressable cards when visible copy is not sufficient. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`light`).Source default: true |
testId | string | undefined | Optional | `data-testid` on the root element. |
silhouette | boolean | undefined | Optional | Marks the card as a silhouette measurement target. The attribute is presence-only, so `false` is normalized away rather than rendered: `data-silhouette="false"` would still match the `[data-silhouette]` query and opt the card in. |
Card.HeaderCardZoneProps
packages/design-system/src/components/cards/Card/CardSlots/CardSlots.tsx:12
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
layout | CardSlotLayout | undefined | Optional | Flow direction inside the slot. Defaults to `stack`. |
padding | CardSlotPadding | undefined | Optional | Defaults to `default` (16px). `compact` and `spacious` change only the vertical padding. |
gap | CardSlotGap | undefined | Optional | Space between the slot's own children. Defaults to `sm` (8px). |
align | CardAlign | undefined | Optional | Cross-axis alignment. Unset applies no class and leaves the flex default. |
justify | CardJustify | undefined | Optional | Main-axis distribution; `between` maps to `space-between`. Unset leaves the flex default. |
divider | "top" | "bottom" | undefined | Optional | Draws a `--cds-divider` rule on that edge of the slot. |
grow | boolean | undefined | Optional | Lets the slot absorb leftover space. `Card.Body` already does this without the prop. |
Card.BodyCardZoneProps
packages/design-system/src/components/cards/Card/CardSlots/CardSlots.tsx:47
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
layout | CardSlotLayout | undefined | Optional | Flow direction inside the slot. Defaults to `stack`. |
padding | CardSlotPadding | undefined | Optional | Defaults to `default` (16px). `compact` and `spacious` change only the vertical padding. |
gap | CardSlotGap | undefined | Optional | Space between the slot's own children. Defaults to `sm` (8px). |
align | CardAlign | undefined | Optional | Cross-axis alignment. Unset applies no class and leaves the flex default. |
justify | CardJustify | undefined | Optional | Main-axis distribution; `between` maps to `space-between`. Unset leaves the flex default. |
divider | "top" | "bottom" | undefined | Optional | Draws a `--cds-divider` rule on that edge of the slot. |
grow | boolean | undefined | Optional | Lets the slot absorb leftover space. `Card.Body` already does this without the prop. |
Card.FooterCardFooterProps
packages/design-system/src/components/cards/Card/CardSlots/CardSlots.tsx:82
| Prop | Type | Presence | Notes |
|---|---|---|---|
onPress | (() => void) | undefined | Optional | Providing this renders the footer as a pressable `<button>` rather than a `<div>`. |
ariaLabel | string | undefined | Optional | Accessible label for the pressable footer when visible copy is not sufficient. |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for successful presses. Defaults to `true` (`light`).Source default: true |
testId | string | undefined | Optional | `data-testid` on the footer element. |
children | React.ReactNode | Required | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
layout | CardSlotLayout | undefined | Optional | Flow direction inside the slot. Defaults to `stack`. |
padding | CardSlotPadding | undefined | Optional | Defaults to `default` (16px). `compact` and `spacious` change only the vertical padding. |
gap | CardSlotGap | undefined | Optional | Space between the slot's own children. Defaults to `sm` (8px). |
align | CardAlign | undefined | Optional | Cross-axis alignment. Unset applies no class and leaves the flex default.Source default: 'center' |
justify | CardJustify | undefined | Optional | Main-axis distribution; `between` maps to `space-between`. Unset leaves the flex default.Source default: 'center' |
divider | "top" | "bottom" | undefined | Optional | Draws a `--cds-divider` rule on that edge of the slot. |
grow | boolean | undefined | Optional | Lets the slot absorb leftover space. `Card.Body` already does this without the prop. |
Card.StackCardStackProps
packages/design-system/src/components/cards/Card/CardSlots/CardSlots.tsx:132
| Prop | Type | Presence | Notes |
|---|---|---|---|
as | "div" | "span" | undefined | Optional | Element to render. Defaults to `div`; use `span` where only phrasing content is allowed.Source default: 'div' |
children | React.ReactNode | Required | |
className | string | undefined | Optional | |
style | React.CSSProperties | undefined | Optional | |
layout | CardSlotLayout | undefined | Optional | Flow direction inside the slot. Defaults to `stack`. |
padding | CardSlotPadding | undefined | Optional | Defaults to `default` (16px). `compact` and `spacious` change only the vertical padding. |
gap | CardSlotGap | undefined | Optional | Space between the slot's own children. Defaults to `sm` (8px). |
align | CardAlign | undefined | Optional | Cross-axis alignment. Unset applies no class and leaves the flex default. |
justify | CardJustify | undefined | Optional | Main-axis distribution; `between` maps to `space-between`. Unset leaves the flex default. |
divider | "top" | "bottom" | undefined | Optional | Draws a `--cds-divider` rule on that edge of the slot. |
grow | boolean | undefined | Optional | Lets the slot absorb leftover space. `Card.Body` already does this without the prop. |
Card.TitleCardTextSlotProps
packages/design-system/src/components/cards/Card/CardTextSlots/CardTextSlots.tsx:10
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required |
Card.DescriptionCardTextSlotProps
packages/design-system/src/components/cards/Card/CardTextSlots/CardTextSlots.tsx:21
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required |
Card.MetricCardMetricProps
packages/design-system/src/components/cards/Card/CardMetric/CardMetric.tsx:11
| Prop | Type | Presence | Notes |
|---|---|---|---|
value | React.ReactNode | Required | The figure, rendered at `display-2` in the card's text color. |
unit | React.ReactNode | Optional | Suffix beside the value, at body size and bottom-aligned. Nothing renders when omitted. |
Card.DeltaCardDeltaProps
packages/design-system/src/components/cards/Card/CardDelta/CardDelta.tsx:20
| Prop | Type | Presence | Notes |
|---|---|---|---|
value | React.ReactNode | Required | Change text, rendered as-is — include any sign or unit you want shown. |
direction | "up" | "down" | undefined | Optional | Which way the figure moved. Sets the marker, and the tone unless `tone` overrides it.
Defaults to `up` and is never inferred from `value`.Source default: 'up' |
tone | CardDeltaTone | undefined | Optional | Overrides the tone `direction` implies, for a figure whose sentiment runs against its arrow — spam calls falling is `down` and `success`. Without it, `up` is success and `down` is danger. |
marker | CardDeltaMarker | undefined | Optional | Marker shape. Defaults to `caret`.Source default: 'caret' |
className | string | undefined | Optional | |
ariaLabel | string | undefined | Optional | Accessible name for the pill, replacing its visible text. Pass the unabbreviated figure when `value` is compacted, so `1.8k this week` is not what a screen reader has to interpret. |
testId | string | undefined | Optional | `data-testid` on the pill wrapper; the caret and the text carry none. |
Card.StatusIndicatorStatusIndicatorProps
packages/design-system/src/components/status/StatusIndicator/StatusIndicator.tsx:25
| Prop | Type | Presence | Notes |
|---|---|---|---|
children | React.ReactNode | Required | |
tone | "neutral" | "info" | "success" | "warning" | "danger" | undefined | Optional | Semantic status color for the dot; maps to the `--cds-status-*` tokens. Defaults to `neutral`.Source default: 'neutral' |
className | string | undefined | Optional | |
testId | string | undefined | Optional | Optional test selector. |
Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source.
Overview
The generic surface primitive. Compose it from typed slots — Card.Header / Card.Body /
Card.Footer, Card.Title / Card.Description, Card.Metric / Card.Delta — rather than raw
markup. surface encodes emphasis. For a selectable option, use SelectionCard.
Use when
surface is the semantic decision: a plain container versus a tinted prompt.
Slots own their own padding, so the root usually keeps padding="none". layout="split" lays the
top-level slots in a row instead of a column; it is available but not currently used in app code —
stack is the norm.
Composition
Header (title + description) over a body list — the canonical slot arrangement.
The summary-card pattern pairs Card.Metric + Card.Delta in a header with a body stat list and a
footer link.
Pressable
Pass onPress to turn the whole card into a tap target, or Card.Footer onPress for a pressable
footer only.
Usage by variant
| Choice | Use when | Avoid when |
|---|---|---|
Surface: default | The standard container for grouping content, metrics, and lists. Nearly every card in the app is default. | A moment you want to actively steer the user toward — that earns highlight. |
Surface: highlight | A tinted surface that steers users toward a short next step — a setup prompt or inline CTA (ProfileSetupCard). | Ordinary content. Rare by design — at most one per screen so the highlight keeps its pull. |
Component specification
$schema
../../../specs/component-spec.schema.jsonParts
- Id
- root
- Label
- Root
- Description
- Surface shell carrying the surface, radius, layout, and padding.
- Required
- true
- Id
- header
- Label
- Card.Header
- Description
- Top region slot, usually title and status.
- Id
- body
- Label
- Card.Body
- Description
- Main region slot for content and lists.
- Id
- footer
- Label
- Card.Footer
- Description
- Bottom region slot, optionally pressable.
- Id
- stack
- Label
- Card.Stack
- Description
- Ad-hoc grouping slot within a region.
- Id
- title
- Label
- Card.Title
- Description
- Region heading; wraps to as many lines as the copy needs.
- Id
- description
- Label
- Card.Description
- Description
- Supporting region text.
- Id
- metric
- Label
- Card.Metric
- Description
- Large metric value with optional unit.
- Id
- delta
- Label
- Card.Delta
- Description
- Change label: a caret or trend marker with the movement, toned success, warning, danger, or neutral. Tone follows the direction unless the figure's sentiment runs against its arrow.
Allowed Design Choices
- Variants
- Container
- Label
- Container
- Description
- The single generic surface; content comes from slots.
- Sizes
- Surfaces
- Default
- Label
- Default
- Description
- Raised surface background.
- Highlight
- Label
- Highlight
- Description
- Tinted background for prompts and CTAs.
- Radii
- Xs
- Label
- Extra small
- Sm
- Label
- Small
- Md
- Label
- Medium
- Lg
- Label
- Large
- Description
- The default corner radius.
- Xl
- Label
- Extra large
- Paddings
- None
- Label
- None
- Description
- No outer padding; slots own their padding.
- Default
- Label
- Default
- Description
- 16-unit outer padding on the shell.
- Layouts
- Stack
- Label
- Stack
- Description
- Top-level slots flow in a column.
- Split
- Label
- Split
- Description
- Top-level slots flow in a row.
Variants
- Id
- container
- Label
- Container
- Description
- Generic slotted surface.
- Platforms
- ios
- android
- web
Sizes
None specifiedMeasurements
- Id
- surface-radius
- Label
- Default corner radius
- Kind
- exact
- Token
- --cds-radius-lg
- Value
- 20
- Unit
- px
- Applies To
- root
- Id
- split-gap
- Label
- Split layout gap
- Kind
- exact
- Token
- --cds-space-8
- Value
- 8
- Unit
- px
- Applies To
- root
- Id
- default-padding
- Label
- Default outer padding
- Kind
- exact
- Token
- --cds-space-16
- Value
- 16
- Unit
- px
- Applies To
- root
Layout
- Alignment
- Slots stack in a column by default, or lay out in a row under the split layout.
- Flow
- The shell hosts typed slots; slots own their own padding, so the shell usually keeps padding none.
- Notes
- Use SelectionCard when the card is a selectable option rather than a container.
Typography And Color
- Typography
- Label
- Title
- Description
- Card.Title uses the body ramp emphasis.
- Token
- --cds-type-body-size
- Colors
- Label
- Default surface
- Description
- The default surface uses the raised background.
- Token
- --cds-bg-surface
- Label
- Highlight surface
- Description
- The highlight surface uses the banner container tint.
- Token
- --cds-banner-container
- Label
- Text
- Description
- Card content uses primary text.
- Token
- --cds-text-primary
- Label
- Delta label
- Description
- Card.Delta's marker and label mix 60% of the tone's solid colour with 40% primary text for readable contrast on the unchanged subtle fill. Success is shown; danger, warning and neutral follow the same calculation. This local adaptation remains preview pending human sign-off.
- Token
- --cds-status-success-solid
- Label
- Delta fill
- Description
- The delta pill sits on the matching subtle tint for its tone.
- Token
- --cds-status-success-subtle
States
- Id
- default
- Label
- Default
- Description
- Static container surface.
- Id
- highlight
- Label
- Highlight
- Description
- Tinted prompt surface.
- Id
- pressable
- Label
- Pressable
- Description
- onPress makes the whole card a tap target with tap feedback.
Behavior
- Label
- Slot composition
- Description
- Compose regions and figures from typed slots rather than raw markup.
- Label
- Pressable
- Description
- onPress makes the card a button; a pressable footer targets just the footer. The two are mutually exclusive — a pressable footer inside a pressable card would nest interactive controls.
- Label
- Silhouette measurement
- Description
- silhouette marks the card as a measurement target so a page can record its settled geometry for a loading placeholder. Presence-only and non-visual: it renders a data attribute and changes nothing about the card itself.
Accessibility
- Label
- Pressable name
- Description
- Give a pressable card an accessible label when visible copy is insufficient.
- Label
- Structure
- Description
- Slots keep a predictable region order for assistive tech.
Content Adaptation
- Label
- Region padding
- Description
- Slots carry their own padding tokens so the shell stays padding-free.
- Label
- Split content
- Description
- The split layout pairs a metric with supporting content in a row.
Motion Haptics
- Motion
- Label
- Press feedback
- Description
- Pressable cards apply tap feedback on press.
- Haptics
- Label
- Press haptic
- Description
- Pressable cards request a light haptic on a successful press.