Card
Flexible content container with composable subcomponents and two locked-down presets (control + summary).
Card is the canonical content-grouping container. Three rendering modes:
- Default — flexible container, compose with
CardTitle/CardDescription/CardFootersubcomponents. preset="control"— locked-down settings/control layout (badge + title + description + action). Replaces the legacyCardControlcomponent per ADR-004.preset="summary"— compact metric/stat layout (label + large value + optional link). Replaces the deprecatedCardSummarycomponent.
Use it for
- Service grids, feature cards, and content blocks (default mode)
- Settings panels with title + description + toggle/action (
preset="control") - Dashboard stat tiles ($48K Q1 revenue, 142 active users) (
preset="summary") - Any grouped-content surface where consistent border/padding/shadow matter
For a horizontal stack of card-like rows, use FieldGrid wrapping Cards. For loading-state placeholders, use Skeleton variant="rectangular".
Import
import { Card, CardTitle, CardDescription, CardFooter } from '@brikdesigns/bds';Default mode
Compose freely with the Title / Description / Footer subcomponents.
<Card variant="outlined" padding="md">
<CardTitle>Title</CardTitle>
<CardDescription>Description text goes here.</CardDescription>
<CardFooter>
<Button variant="primary" size="sm">Action</Button>
</CardFooter>
</Card>Variants
outlined(default) — Subtle secondary border.brand— Primary-color border for emphasis.elevated— Shadow for visual prominence.
Padding
none, sm, md (default), lg. Match content density.
Interactive + link
interactive adds hover affordance. href renders as an <a> so the whole card becomes a link.
<Card variant="elevated" interactive>
Hover me
</Card>
<Card href="/articles/foo" interactive>
Whole card is a link
</Card>Control preset
preset="control" is the canonical settings-row card. Locked layout: leading badge + (title + description) on the left, action slot on the right.
<Card
preset="control"
badge={<Badge status="positive">On</Badge>}
title="Email notifications"
description="Send a weekly digest to your inbox."
action={<Button variant="outline" size="sm">Configure</Button>}
/>actionAlign controls vertical alignment of the action slot — center (default) or top (anchors to the upper-right when descriptions are tall).
Both <Card preset="control"> and standalone <CardControl> ship as first-class APIs in v0.39.0+. The 2026-Q2 audit recommended folding CardControl into Card; the user reversed that decision 2026-04-24 to preserve optionality at the call site. Pick whichever reads cleaner in context. See the audit doc.
Summary preset
preset="summary" is the compact stat tile. Label on top, large numeric value below, optional text link.
<Card
preset="summary"
label="Q1 revenue"
value={48250.75}
type="price"
textLink={{ label: 'Details', href: '/revenue' }}
/>Number formatting
type="numeric"(default) — locale-formatted integer (1,234).type="price"— USD currency ($1,234.50).- String values render verbatim regardless of
type.
The standalone CardSummary component is @deprecated and slated for deletion in a future major version. Migrate to Card preset="summary" — same prop names, same number formatting, same layout. See ADR-004.
When not to use
- Don't use Card for inline content blocks. Cards imply discrete grouping; for paragraphs and section dividers, use prose + Divider.
- Don't use Card for tables. Use a table component when rows share columns. Cards are for self-contained groupings.
- Don't reach for the default mode when
preset="control"orpreset="summary"fits. The presets carry locked-down semantics that propagate consistently across screens.
Accessibility
- Default Card renders a
<div>; withhref, it renders an<a>with full anchor semantics (right-click, keyboard, screen-reader link role). interactivewithouthrefaddsrole="button"only whenonClickis also passed; otherwise it's a non-interactive hover affordance.- Title becomes an
<h3>semantic by default in the Title subcomponent — adjust the surrounding heading hierarchy accordingly.
API
Default mode
| Prop | Type | Default |
|---|---|---|
variant | 'outlined' | 'brand' | 'elevated' | 'outlined' |
padding | 'none' | 'sm' | 'md' | 'lg' | 'md' |
interactive | boolean | false |
href | string | — |
children | ReactNode (required) | — |
Control preset
| Prop | Type | Default |
|---|---|---|
preset | 'control' (required) | — |
title | string (required) | — |
description | string | — |
badge | ReactNode | — |
action | ReactNode | — |
actionAlign | 'center' | 'top' | 'center' |
Summary preset
| Prop | Type | Default |
|---|---|---|
preset | 'summary' (required) | — |
label | string (required) | — |
value | string | number (required) | — |
type | 'numeric' | 'price' | 'numeric' |
textLink | { label: string; href: string } | — |
All modes accept standard <div> HTML attributes (excluding title, which is repurposed in the control preset).
Related
- CardControl — sibling settings-card component (still in Storybook)
- CardList — list-of-cards composition (Storybook)
- CardTestimonial — testimonial layout (Storybook)
- CollapsibleCard — Card + Accordion mash-up (Storybook)
- PricingCard — pricing tier card (Storybook)
- Divider — for non-card section separators
- FieldGrid — equal-column wrapper for stat tiles
- Storybook playground