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.

Upgrade to Pro
Unlock every theme and component.
Billed annually. Cancel anytime from settings.

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add card

Usage#

usage.tsxTSX
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>  )}

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.

Upgrade to Pro
Unlock every theme and component.
Billed annually. Cancel anytime from settings.
Playground
Props

A contour with a hairline shadow, a bare contour, or a filled floating panel.

The padding tier for every part.

Parts

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.

Actions along the bottom.

card.tsxTSX
<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>

Anatomy#

Upgrade to Pro
Unlock every theme and component.
Billed annually. Cancel anytime from settings.

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.

default
variant="default"
The quick brown fox jumps over the lazy dog.
outline
variant="outline"
The quick brown fox jumps over the lazy dog.
elevated
variant="elevated"
The quick brown fox jumps over the lazy dog.

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.

compact
Default.
Shared via React Context.
comfortable
Spacious alternative.
Shared via React Context.

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.

A living room with a green sofa and warm lightBest seller
Interior kit
48 rooms, 6 moods, every one editable.
Mountain retreat
Media can close the card too.
Sunlit valley between green mountains

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.

Income vs expenses
Net of refunds, in USD.
$48,200
Income vs expenses
Net of refunds, in USD.
$48,200

FAQ#

Namespaced statics like Card.Header do not survive the React Server Components server to client boundary, so Koala exports each part by name. Import and compose them as `<Card><CardHeader>` and so on.