Skip to content

Card

A <Card> is a bordered, rounded surface that visually groups related content. The Attio-flavored rule: cards group, they don’t decorate. A card without a clear semantic boundary is just a box.

import from "@na/ui/components/card" ▶ Open in Storybook packages/ui/src/components/card.tsx

When to use

  • A discrete piece of content with its own header + body (a settings group, an entity tile, a stat).
  • Grid items that need uniform shape — wrap in CardGrid.

Don’t use Card for: page-level layout (Cards inside Cards is a code smell), every section header on a page (use a heading + spacing), or a list-row container (lists should be flat per Attio’s pattern).

Default

Acme Corp
5 deals · $128K pipeline

Recent activity: 3 emails, 1 call, 2 deal-stage changes this week.

<Card>
<CardHeader>
<CardTitle>Acme Corp</CardTitle>
<CardDescription>5 deals · $128K pipeline</CardDescription>
</CardHeader>
<CardContent>
<p>Recent activity: 3 emails, 1 call, 2 deal-stage changes this week.</p>
</CardContent>
<CardFooter className="border-t pt-4">
<Button size="sm" variant="outline">Open</Button>
</CardFooter>
</Card>

With CardAction

<CardAction> slots into the header’s right-aligned column.

Beta integration
Connected · syncing every 15 minutes
Beta
<Card>
<CardHeader>
<CardTitle>Beta integration</CardTitle>
<CardDescription>Connected · syncing every 15 minutes</CardDescription>
<CardAction>
<Badge variant="secondary">Beta</Badge>
</CardAction>
</CardHeader>
</Card>

CardGrid

Wraps cards in a responsive grid (1 / 2 / 3 / 4 columns). Single column on mobile, multi-column at md and up.

Total
128
Active
96
Archived
32
<CardGrid columns={3}>
<Card></Card>
<Card></Card>
<Card></Card>
</CardGrid>

API

Card

Prop Type Default Description
className string Forwarded. Use only for spacing/sizing — not for color.
...rest HTMLAttributes<HTMLDivElement> All native div attributes.

CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction

All are slot wrappers. They accept className and forward all native div attributes. CardAction is auto-aligned to the right column of CardHeader.

CardGrid

Prop Type Default Description
columns 2 | 3 | 4 3 Desktop column count. 4 collapses to 2 columns at md and 4 at lg.
children * ReactNode Cards (or any equally-sized item).
className string Forwarded.

Design guidelines

✓ Do

  • Use Card for entities and settings groups. Title at top, action top-right, content below.
  • Match all cards on a page in shape. Mixed paddings/heights look broken.
  • Put the divider above CardFooter via `className="border-t pt-4"` only when the footer changes the meaning of the card (action vs. metadata).

✗ Don't

  • Wrap a list table in a Card. Tables are flat per the spec — see Attio Layout.
  • Nest cards. If two cards belong together, the outer one should not be a card.
  • Use a card to add a "shadow" for emphasis. The card is structure, not decoration.

Accessibility

  • CardTitle defaults to a <div> — slot it under a section heading rather than relying on it as the heading itself for screen readers.
  • Cards aren’t focusable by default. Make them clickable by wrapping in <a> only when the whole card is the link target — otherwise use buttons inside.
  • StatRow — Compact stat with delta.
  • ContentSection — When you want a titled body section without a card border.
  • PropertyList — Key/value entity layout for inside cards.

▶ Open Card stories in Storybook