Skip to content

Card

Groups related content and actions in a titled container.

Library
What’s this?
Contacts (3)
Acme Corporation
Ada Lovelace, Grace Hopper and Alan Turing work at this account.

Installation

pnpm dlx shadcn@latest add @wisty/card

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • To group content and actions about one thing under a title, like the contacts on an account or a summary of open cases.
  • For a related list on a record page: a title with a count, a New button in the header and a View all link in the footer, as the Related list example shows.
  • For dashboard tiles in a grid, where each card stands on its own.
  • For a sub-section inside a card. Give the inner card size="sm", as in Nested.

Don't use it

  • For a record or page header with the record name, key fields and page actions. Use a plain <header>.
  • To switch between peer panels of equal weight. Use Tabs.
  • For page layout. Use plain <section> or <div> elements with grid or flex, and put cards inside them.
  • As one big click target. Put a link in the title or a button in the header action instead.
  • For a third level of nesting. Inside a small card, use a list, a heading or a divider.

Usage

import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
Card
├── CardHeader
│   ├── CardTitle
│   ├── CardDescription
│   └── CardAction
├── CardContent
└── CardFooter

Every part is optional. CardAction places itself at the end of the header, beside the title and description.

Examples

Nested

Two cards with `size="sm"` inside another, which drop their border and shadow.

Opportunity details
Next step
Due today
Send the revised quote to procurement.
Competitors
Globex, Initech

With icon

The contact object's icon before the title, an `Icon` inside `CardTitle` with `flex items-center gap-3`, and New in `CardAction`.

Contacts (3)
Ada Lovelace, Grace Hopper and Alan Turing work at this account.

Empty and loading

A card with no tasks shows `Empty` in `CardContent`, and a card still loading its events shows a `Spinner` in its place.

Tasks
No new tasks
Events

API reference

SLDS mapping

  • Card is slds-card. A card inside a card drops its border and shadow, as .slds-card .slds-card does.
  • CardHeader is slds-card__header, CardTitle is slds-card__header-title, and CardAction is the slds-no-flex slot at the end of the header.
  • CardDescription has no SLDS counterpart. It uses SLDS's meta text (slds-text-body_small).
  • CardContent is slds-card__body slds-card__body_inner, and CardFooter is slds-card__footer.

Known gaps:

  • No header icon slot. Put the object icon inside CardTitle, as With icon shows.
  • No linked title style (slds-card__header-link). Put an <a> inside CardTitle and add hover:underline.
  • No margin between stacked sibling cards. Set a gap on the container.
  • No slds-card_boundary. To bring back a nested card's border, pass in-data-[slot=card]:border-border.
  • Every part renders a div, where SLDS uses <article>, <header> and <footer>.
PropValueSLDSNotes
sizedefaultslds-card
sizesmslds-card slds-cardThe nested tier: one radius step down, smaller and heavier title.

Accessibility

  • CardTitle isn't a heading. When the page outline needs one, put it inside: <CardTitle><h2>Contacts</h2></CardTitle>. The title style doesn't change.
  • For a major section of the page, wrap the card in a <section> whose aria-labelledby points at the title's id. Don't do it for every card: too many regions make landmark navigation noisy.
  • CardDescription's muted text is 4.6:1 on white in Lightning Blue. On a gray fill like bg-muted it drops below 4.5:1, so keep it on the card surface.

Responsive behavior

  • Card fills its container. Constrain it with the container or a class like max-w-md.
  • Titles wrap. Add truncate to CardTitle for a single-line title.
  • CardHeader is a named container (@container/card-header), so header content can respond to the card's width. It also adds nothing to the card's intrinsic width: in a flex row or a w-fit layout, give the card a width.
  • The card clips overflow. Wrap wide content, like a table, in a div with overflow-x-auto, keep the default padding around focusable content so focus rings aren't cut off, and render menus and popovers in a portal (Popover and Dropdown Menu do by default).

Rules

  • Keep CardAction inside CardHeader. Its grid placement only works there.
  • Don't add radius, border, shadow or title size classes. The tokens set them per theme, and a class pins one look into every theme.
  • Nest one level at most, with size="sm" on the inner card only.
  • A Cosmos card has no visible border or shadow, so put it on the gray page (bg-page). On a white surface, pass border-border.
  • The footer centers its content. Pass justify-end for form actions, or border-t-0 to drop the divider.
  • When View all navigates, render a link: <a href="/contacts" className={buttonVariants({ variant: "link" })}>View all</a>.
  • For a table of records in a card, use the Related list block. Table draws its own border, so a Table under px-0 puts that border on the card's edges. For other full-bleed content, pass px-0 to CardContent, and in a small card also group-data-[size=sm]/card:px-0.