Card
Bordered surface for grouping related content, metadata, and actions.
Installation
The components are exported from @by/experience-system. Add the package with your package manager:
pnpm add @by/experience-systemIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.
Composition
Use the following composition to build a Card:
Card
├── CardHeader
│ ├── CardTitle
│ ├── CardDescription (optional)
│ └── CardAction (optional)
├── CardContent (optional)
└── CardFooter (optional)CardHeader uses a @container/card-header grid so CardAction aligns to the top trailing edge when present. CardTitle and CardDescription are plain div elements (not headings by default)—choose heading levels in the surrounding page if needed.
Usage
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@by/experience-system';Card and its parts are client components ('use client'). Use them inside a Client Component or a dynamic import when using the Next.js App Router.
<Card className="max-w-sm">
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Supporting text.</CardDescription>
</CardHeader>
<CardContent>Body content.</CardContent>
</Card>Examples
Overview
Basic header and content.
Form layout
CardAction in the header for a secondary control, CardFooter for primary actions.
Elevation
Shadow utilities on Card for depth (shadow-sm through shadow-xl).
Interactive patterns
Link-wrapped card, selectable cards, and RadioGroup card options (focus rings on the interactive wrapper).
Clickable card (link)
Full card is a link: interactive cards have stronger outline than regular cards, use `border-neutral-alpha-6` and `border-neutral-alpha-8` for normal and hovered states respectively.
<a> with group-hover / group-focus-visible on the card.Selectable cards
Click a card to select it; selection uses a higher elevation than idle cards.
Selectable cards have stronger outline than regular cards, use `border-neutral-alpha-6` and `border-neutral-alpha-8` for normal and hovered states respectively.
Radio cards
Use RadioGroup with Radio at the start of each card; wrap the card in Label so the whole surface toggles the option. Only one card can be selected at a time.
API Reference
Subsection titles name the exports from @by/experience-system. These are layout div components (not Radix primitives).
Card
| Data attribute | Values |
|---|---|
data-slot | card |
Also accepts standard div attributes.
CardHeader
| Data attribute | Values |
|---|---|
data-slot | card-header |
Also accepts standard div attributes.
CardTitle
| Data attribute | Values |
|---|---|
data-slot | card-title |
Also accepts standard div attributes.
CardDescription
| Data attribute | Values |
|---|---|
data-slot | card-description |
Also accepts standard div attributes.
CardAction
Trailing header actions; participates in the header grid when data-slot="card-action" is present on this node.
| Data attribute | Values |
|---|---|
data-slot | card-action |
Also accepts standard div attributes.
CardContent
| Data attribute | Values |
|---|---|
data-slot | card-content |
Also accepts standard div attributes.
CardFooter
| Data attribute | Values |
|---|---|
data-slot | card-footer |
Also accepts standard div attributes.
Accessibility
Prefer a single primary interactive element per card (one a or button wrapping the card, or explicit buttons inside). If the whole card is clickable, put the focus ring on the wrapper and avoid nested tab stops. For radio-style cards, use RadioGroup + Radio with a Label wrapping the card so the control name matches the visible title. See MDN — Cards for generic container semantics and your product pattern library for card-specific guidance.
Source in the repo: packages/experience-system/src/components/Card/Card.tsx. Agent-oriented contracts: packages/experience-system/src/components/Card/Card.instructions.md.