Card
Flexible content container with composable subcomponents and four locked-down presets (control, summary, display, display-row).
Card is the canonical content-grouping container. Five 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).preset="display"— the malleable vertical cell of a card-grid Section, per ADR-018.preset="display-row"— the horizontal card-grid cell / section row (image left, content right).
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— Surface-primary fill, no border, no shadow (flat since brik-bds#1146; for a shadow useraised).raised— Surface-primary fill, no border, with a cast--box-shadow-mddrop shadow. The shadow-casting counterpart to the now-flatelevated; use for a focal/lone card or a grid cell that needs a lifted, contained read.borderless— Transparent fill, no border, no shadow. Use for cards sitting on a colored surface, where the border ring reads as visual noise.
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>Leading media
The media prop turns the default card into a horizontal "media object" — an Avatar or a square 1:1 Image on the left, with children stacked to the right. Pass exactly one of avatar / image. Both size to the shared Avatar scale (sm 32px, md 40px, lg 48px, xl 64px). The avatar falls back to initials from name and can carry a presence status; the image takes fit (contain for logos, cover for photos).
<Card media={{ avatar: { name: 'Jordan Lee', status: 'online', size: 'lg' } }}>
<CardTitle as="h4">Jordan Lee</CardTitle>
<CardDescription>jordan.lee@brikdesigns.com</CardDescription>
</Card>
<Card media={{ image: { src: '/brik-logo.svg', alt: 'Brik Designs logo', fit: 'contain', size: 'lg' } }}>
<CardTitle as="h4">Brik Designs</CardTitle>
<CardDescription>Design system · Enterprise plan</CardDescription>
</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).
preset="control" replaces the standalone <CardControl> component, which is deprecated per ADR-004 (#657). New code should use the preset; see the CardControl migration guide.
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.
Display preset
preset="display" is the malleable cell of a card-grid Section, not a standalone card — per ADR-018, a display card only exists inside a grid that owns the columns. Every affordance is optional and prop-toggled so one cell serves any content type — service, blog post, customer story, property listing, team bio, support plan.
<Card
preset="display"
tag={<ServiceTag line="brand" />}
image={<Frame ratio="16-9"><img src="/service.jpg" alt="" /></Frame>}
badge={<Badge>Featured</Badge>}
title="Brand identity"
description="Logo, type, and color systems built to scale."
action={<Button href="/services/branding" variant="outline" size="sm">Learn more</Button>}
/>title renders as titleAs (default h3) — set it to keep the document outline correct under a grid-section heading; the visual size is token-driven and unchanged by the level. For a cell on a colored (service-tinted) grid, variant="borderless" (transparent), variant="elevated" (fill + shadow), or variant="raised" (fill + cast --box-shadow-md shadow) swaps the surface treatment, and tint applies a pale service-line wash. badge renders only when image is also set.
Display-row preset
preset="display-row" is the horizontal card-grid cell / section row — image on the left, content (tag, title, description, action) on the right. Use for single-row sections where a vertical layout wastes horizontal space (Related Customer Story, Recommended Add-On, featured plan). Collapses to a vertical stack at ≤ 640px.
<Card
preset="display-row"
tag={<Tag>Case study</Tag>}
image={<Frame ratio="4-3"><img src="/story.jpg" alt="" /></Frame>}
imageWidth="standard"
title="How Northwind tripled conversions"
description="A ground-up rebrand and site rebuild."
action={<Button href="/stories/northwind" variant="outline" size="sm">Read the story</Button>}
/>imageWidth sets the left column: narrow (25%), standard (35%, default), or wide (50%) — or pass any CSS length/percentage string ("40%", "320px"). The optional extras slot renders structured content (bullet lists, feature pills) between description and action.
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' | 'raised' | 'borderless' | 'outlined' |
padding | 'none' | 'sm' | 'md' | 'lg' | 'md' |
interactive | boolean | false |
href | string | — |
media | CardMedia (exactly one of avatar / image / logo) | — |
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 | CardSummaryTextLink | — |
Display preset
| Prop | Type | Default |
|---|---|---|
preset | 'display' (required) | — |
title | string (required) | — |
titleAs | 'h2' | 'h3' | 'h4' | 'h3' |
description | string | — |
variant | 'borderless' | 'elevated' | 'raised' | — |
tint | CardTint (service line) | — |
image | ReactNode | — |
tag | ReactNode | — |
badge | ReactNode (needs image) | — |
action | ReactNode | — |
href | string | — |
Display-row preset
| Prop | Type | Default |
|---|---|---|
preset | 'display-row' (required) | — |
title | string (required) | — |
titleAs | 'h2' | 'h3' | 'h4' | 'h3' |
description | string | — |
tint | CardTint (service line) | — |
image | ReactNode | — |
imageWidth | CardDisplayRowImageWidth (narrow / standard / wide, or any CSS length) | 'standard' |
tag | ReactNode | — |
extras | ReactNode | — |
action | ReactNode | — |
href | string | — |
All modes accept standard <div> HTML attributes (excluding title, which is repurposed in the control preset).
Related
- CardControl — deprecated; see Card presets
- CardList — list-of-cards layout
- Testimonial — testimonial component (Storybook; renamed from CardTestimonial)
- CollapsibleCard — Card + Accordion mash-up
- PricingCard — pricing tier card
- Divider — for non-card section separators
- FieldGrid — equal-column wrapper for stat tiles
- Storybook playground