Card
Group related content and actions on a surface.
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/cardComplete the one-time setup first. Registry JSON lists source files and dependencies.
For this page’s examples, also install:
pnpm dlx shadcn@4.18.0 add @siteplane/avatar @siteplane/button @siteplane/empty-state @siteplane/file-type-icon @siteplane/form @siteplane/input @siteplane/label @siteplane/progress @siteplane/select @siteplane/separator @siteplane/switchUsage
import { Card, CardPanel } from "@/siteplane/components/card";
<Card>
<CardPanel>Content</CardPanel>
</Card>;Composition
CardFrame (optional)
├── CardFrameHeader
│ ├── CardFrameTitle
│ ├── CardFrameDescription
│ └── CardFrameAction
├── Card
│ ├── CardHeader
│ │ ├── CardMedia (optional)
│ │ ├── CardTitle
│ │ │ └── CardTitleMeta (optional)
│ │ ├── CardDescription
│ │ └── CardAction
│ ├── Separator (optional)
│ ├── CardPanel (CardContent)
│ ├── Separator (optional)
│ └── CardFooter
├── CardFrameBleed (optional)
└── CardFrameFooterHeader action
Place a link or button in CardAction. Beside a title and a description, it sits at the top right of the header. Beside a title alone, it centers on the title's line and keeps the header as tall as a header without an action: a button taller than the line, up to the xl size, reaches into the header's padding instead of making the header taller. Cards side by side therefore keep one header height, with or without an action.
Header media and title context
Put an avatar or an icon in CardMedia as the first part of CardHeader, to show who or what the card is about. It sits before the title and the description, 12 px from them. A bare icon is 20 px in the accent color and centers on the first line of the title; an avatar taller than that line starts at its top. Beside a title alone, the media, the title and the action center on each other.
Set tone="neutral" on CardMedia to draw a bare icon in the secondary text color, --sp-rw-fg-secondary, instead of the accent. A neutral icon suits a module icon that leaves color to states. An avatar or an image keeps its own colors.
<CardMedia tone="neutral">
<ChartColumnIcon aria-hidden="true" />
</CardMedia>Put context that belongs to the title, such as a period, a count or a short status, in CardTitleMeta inside CardTitle, after the title's text. It shares the title's line in the quieter text of CardDescription, and moves below the title when the line is full. Rendered as a heading with render={<h2 />}, the title's text and its context form the heading together.
Divided header and footer
Place a Separator after CardHeader or before CardFooter to divide them from the panel. The divided header keeps as much space below its text as above it, 20 px, and the divided footer keeps 16 px above and below its actions. The panel keeps its own padding on the other side of each line. A border-b on CardHeader or a border-t on CardFooter divides them the same way.
- PO-2041Oct 14$1,240.00
- PO-2033Oct 7$980.50
- PO-2027Sep 30$1,102.25
Density
Set density="compact" to pad the parts of a card by 16 px instead of 20 px. Without the prop, a card takes the density of the SiteplaneUIProvider around it, so a compact provider makes every card inside it compact, and a card's own density wins over the provider's. The space between the parts stays the same.
<Card density="compact">Inset
Every part of a card is padded by the card inset, 20 px by default and 16 px in the compact density: the header, the panel and the footer, the sides of the tinted header and of the divided bands, and the space above and below a divided header. The space between the parts stays the same: 12 px below a header, 16 px above a footer and the 16 px of a divided footer.
Three tokens set the inset in place of the density:
--sp-rw-card-insetsets all four sides.--sp-rw-card-inset-blocksets the space above and below.--sp-rw-card-inset-inlinesets the sides.
--sp-rw-card-inset-block and --sp-rw-card-inset-inline take the value of --sp-rw-card-inset until you set them. Set the tokens after the base import to change every card, as Theming shows, or on one card with its className:
<Card className="[--sp-rw-card-inset:--spacing(6)]">For less space above and below than at the sides, set the block token alone:
<Card className="[--sp-rw-card-inset-block:--spacing(3)]">The cards joined into a CardFrame take the frame's inset at their sides instead, --card-frame-inset, so their text lines up with the frame's header.
Joined frame
Use CardFrame for a titled section with a frame-level action, and place Card directly inside it. The frame draws the outer edge and clips the card at it, so the card runs edge to edge with square corners and adds only the hairline where it begins. CardFrameBleed extends custom content, such as a table, to the outer edge. The frame's header and footer, the parts of a joined card and the outer columns of a table inside CardFrameBleed share one horizontal inset.
Variants
The variant prop changes only the surface treatment; the parts inside stay the same. The sections below group the eight variants by where a card sits and what it shows.
Raised, header and subtle
raised is the default: the card color with the edge and a soft shadow, for a card on the page. Set variant="header" for the raised Card with a tinted header and a thin divider. Compose the same CardHeader, CardTitle and CardPanel parts; the Card owns the header background, border, spacing and its compact 14 px semibold title in both themes. Set variant="subtle" for a flatter surface in the page tone without a shadow.
<Card variant="header">
<CardHeader>
<CardTitle>Views in 24 hours</CardTitle>
</CardHeader>
<CardPanel>2,000 views</CardPanel>
</Card>2,000
12% more than the day before.
Inset and elevated
Both are for a surface that sits on a surface of the same color. Set variant="inset" for a surface that sits inside another surface, such as a file row inside a Card. It uses the page tone, is pressed in with a soft inner top shadow and the native hairline, and has no elevation, so it never floats white on white. Set variant="elevated" for a surface that must lift off a surface of the same color, such as a plan to choose on a white page: it keeps the raised fill and adds the hairline with a soft all-round shadow that also shows the top edge, biased slightly downward.
$0 / month
$20 / month
Message and error
Set variant="message" for a bordered message surface without elevation. It uses the normal 12 px surface radius with a smaller 4 px top-start corner (top-left in left-to-right layouts). Set variant="error" for the raised Card of an item that failed, such as an upload that did not finish. The Card adds the destructive contour of an invalid field and a faint tint. State the failure in text or with an icon as well; the color never carries it alone.
logo-dark.png did not upload
The file is larger than the 5 MB limit.
Glass
Set variant="glass" for a card that sits on a photo, a video or a color surface. In place of the card color, edge and shadow, the card shows a tinted, blurred view of what lies behind it, with a light rim. Its parts and spacing stay the same. Glass shows every component with a glass variant.
The gradient only makes the glass visible. It is not part of the component.
Edge and background
A raised card draws one edge: a hairline contour with a soft shadow, strong enough to read on a white page and on another card. Cards, frames and panels share it, so each boundary has a single hairline. The header and error cards keep this edge, and inset and elevated cards draw the same hairline with a shadow of their own. A subtle card has no edge, a message card draws a border in its place, and a glass card shows a light rim. No prop or provider option changes the strength of the edge.
On the page canvas
2,000
On the card color
2,000
Cards read best on a background a little darker than the card, such as the page canvas (bg-background): the edge and the soft shadow then separate the card clearly, especially on high-density displays.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
Card. | raised | subtle | header | message | error | inset | elevated | glass | raised | Surface treatment; header adds a divided header, message adds a contour and a smaller top-start corner, error adds a destructive contour and tint, inset presses a nested surface into its parent, elevated lifts it with an all-round shadow, glass shows a tinted, blurred view of what lies behind the card. |
Card. | default | compact | provider / default | Inset of the parts: 20 px, or 16 px compact. The inset tokens below take its place. |
--sp-rw-card-inset | CSS length | unset: 20 px, 16 px compact | Padding of the header, panel and footer, the sides of the bands and the space above and below a divided header. Set it on :root after the base import, or on one card with className="[--sp-rw-card-inset:--spacing(6)]". |
--sp-rw-card-inset-block | CSS length | --sp-rw-card-inset | The space above and below: the padding of the header, panel and footer and the space above and below a divided header. |
--sp-rw-card-inset-inline | CSS length | --sp-rw-card-inset | The sides of the header, panel and footer and of the bands. The cards joined into a CardFrame take --card-frame-inset instead. |
Card | CSS length | 1.5rem | Horizontal inset of CardFrameHeader, CardFrameFooter and the outer columns of a table inside CardFrameBleed. Set it on CardFrame, for example with className="[--card-frame-inset:--spacing(4)]". |
Card | div props | — | Leading avatar or icon of CardHeader, before the title and description. A bare icon is 20 px in the accent color. |
Card | accent | neutral | accent | Color of a bare icon: the accent, or the secondary text color, --sp-rw-fg-secondary. |
Card | span props | — | Context inside CardTitle, after its text, in the quieter description text. Moves below the title when the line is full. |
All parts. | ReactElement | render function | div | Compose native semantics onto each part. |
All parts. | string | unset | Additional classes. |
All parts. | ReactNode | unset | Content. |
All parts accept native div attributes and refs through Base UI
composition; CardTitleMeta
accepts native span attributes. CardHeader lays out CardMedia, CardTitle,
CardDescription and CardAction; CardPanel supplies flexible padded
content; CardContent is the exported alias for CardPanel; CardFooter holds
lower content. CardFrame, CardFrameHeader, CardFrameTitle,
CardFrameDescription, CardFrameAction, CardFrameFooter and
CardFrameBleed supply the corresponding outer composition. Only Card owns
variant and density. No part adds data loading, selection, click behavior or
focus handling.
Usage notes
Title parts render divs by default. Use render={<h2 />} if the title belongs in the document outline. Keep interactive controls separate instead of turning a card containing buttons into another nested button.
A long title wraps. To keep it on one line, put a Truncate inside CardTitle. The title then ends in an ellipsis before the CardAction and shows in full in a tooltip on hover. A Truncate with lines={2} inside CardDescription keeps the first two lines of a description.
Tables inside a frame
Tables inside a direct child Card or CardFrameBleed align their outer columns with the frame header and footer. Set --card-frame-inset on CardFrame to adjust that shared inset; a table-specific --table-edge-padding can override it.