Card
A surface that groups related content. The reference multi-part component: one tv recipe with slots, shared variants flowing to every part through React Context.
Installation#
Usage#
import {
Card, CardHeader, CardTitle,
CardDescription, CardContent, CardFooter,
} from "@/components/ui/card"
export function Example() {
return (
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Supporting text.</CardDescription>
</CardHeader>
<CardContent>Body content.</CardContent>
<CardFooter>Footer</CardFooter>
</Card>
)
}Anatomy#
import {
Card,
CardMedia,
CardHeader,
CardTitle,
CardDescription,
CardAction,
CardContent,
CardFooter,
} from "@/components/ui/card"
<Card>
<CardMedia />
<CardHeader>
<CardTitle />
<CardDescription />
<CardAction />
</CardHeader>
<CardContent />
<CardFooter />
</Card>Playground#
Card is the other shape: two props, and everything else is composition. Turn a Part off and a whole element leaves the snippet, rather than a show-something prop appearing. Drop them all and the card still holds together.
A contour with a hairline shadow, a bare contour, or a filled floating panel.
The padding tier for every part.
A picture inset from the card's edges.
Holds the title, description and action.
A muted line under the title.
A control on the header's trailing edge.
The body.
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle>Upgrade to Pro</CardTitle>
<CardDescription>Unlock every theme and component.</CardDescription>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
Billed annually. Cancel anytime from settings.
</CardContent>
<CardFooter className="gap-2">
<Button variant="ghost">Learn more</Button>
<Button>Upgrade</Button>
</CardFooter>
</Card>Variants#
default and outline sit in the page flow, so they paint no fill: they are contours that take whatever they sit on, the page, a muted band or a dialog. default adds a hairline shadow, outline is the bare border. elevated is the one that floats: it drops the border, leans on shadow, and is the only variant with a fill.
Density#
density flows from the root through context to every part’s padding and the title scale. compact is the Koala default (16px padding & gaps, 1rem title); comfortable is the spacious alternative for marketing layouts. Set it per-card or for a whole subtree with DensityProvider - see Density.
Media#
CardMedia holds a photo, video or screenshot inset from the card’s edges, never bled to them. The inset is 8px on every side, tighter than the copy’s padding, so the picture reads as framed. Its radius is concentric with the card’s: 20px outside, minus 8px of inset, is 12px inside. Badges laid over the picture anchor to the frame, not the card. The frame is aspect-video by default; pass another ratio in className.
Interactive#
When the whole card is the way in (a task on a board, a template in a gallery, a customer in a grid), render it as the link or the button itself: pass asChild around an <a> or a <button>, and interactive to add the pointer, a hover and a focus ring. The hover follows the variant: default lifts and strengthens its edge, outline strengthens its edge with a whisper of lift, elevated rises further. The edge matters on the dark themes, where a shadow alone barely reads. Only the box-shadow moves, and nothing scales under the press. Keep other controls out of an interactive card: a link or button nested in another is invalid, so a card with its own actions stays a plain card with a link in its title.
Action on a narrow card#
A wide action (a period switch, a select) leaves the title a sliver of room on a phone-width card. Pass wrap="narrow" on CardAction and it drops under the title and description once the card header is narrower than 24rem, then returns to the top-right when there is room. It is measured on the header with a container query, so it follows the card, not the viewport: a narrow card on a wide screen wraps too, and a wide card on a tablet does not. It is opt-in because an icon button reads best beside the title at any width. Both cards below hold the same header.
API reference#
Card
The root, a flex column whose density flows to every part. Forwards all native <div> props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "outline" | "elevated" | "default" | A hairline-shadow contour, a bare border, or a filled card on a large shadow (the only one with a fill). |
| density | "comfortable" | "compact" | "compact" | Padding and gaps (16px or 24px) and the title size. Falls back to the nearest DensityProvider. |
| interactive | boolean | false | Pointer, a hover lift that follows the variant, and a focus ring, for a card that is itself a link or button. |
| asChild | boolean | false | Renders the card onto its single child (Radix Slot), such as an <a>. |
| CSS variable | Description |
|---|---|
| --surface | Set to var(--card) by elevated only, so controls inside blend with its fill. |
CardHeader
A grid for the title, description and an optional action, which it seats top-right. Forwards all native <div> props.
CardTitle
The title, semibold. Forwards all native <div> props.
CardDescription
Muted supporting text; truncate or line-clamp on it win over its pretty wrapping. Forwards all native <div> props.
CardAction
The header's top-right slot, for a button or control. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| wrap | "never" | "narrow" | "never" | narrow drops the action under the copy once the header is under 24rem (a container query). The card then needs its width from the layout. |
| Data attribute | Description |
|---|---|
| data-wrap | "narrow" when wrap="narrow". |
CardContent
The body, padded to the density. Forwards all native <div> props.
CardFooter
A row for actions, padded to the density. Forwards all native <div> props.
CardMedia
An inset 16:9 picture frame with a concentric radius; an <img> or <video> inside fills it. Override the ratio with className. Forwards all native <div> props.