Card

Draws one frame around everything about one topic, and can fold its body away.

Examples

An eval run with its kind

Refund policy eval
Run
Running

Scoring support-agent on 142 of 200 tickets.

A summary with an action

Spend this period
Resets on Oct 1
$1,284 of $2,000

A tool call input

Sizes

  • Tickets
    Last 24 hours
    112 resolved, 9 escalated.

    sm

  • Tickets
    Last 24 hours
    112 resolved, 9 escalated.

    defaultdefault

  • Tickets
    Last 24 hours
    112 resolved, 9 escalated.

    lg

Collapsible

  • Tickets
    Last 24 hours
    112 resolved, 9 escalated.

    falsedefault

  • true

States

  • folded

  • 3 arguments

    open

  • hover

  • focus

  • disabled

Usage

When to use it
Use
A page has several topics and each needs its own box: spend, usage, invoices.
Use
Detail most people skip but some need: a tool call's input, a trace step's attributes (collapsible).
Use
A settings page groups fields; fold the groups so the page fits and keep them deep-linkable.
Not for
A list of similar things, one per row. Use Item.
Not for
Show and hide one region with no box around it. Use Collapsible.
Not for
A message about trouble that the person must see. Use Alert.
Not for
A summary or a decision: a few peer rows, one chart, a short read-only fact set, a card that asks Resume or Roll back. Use Panel.

Rules

10 rules
Must
Use a Card for a working surface: a big table or stream, a trace, a log, code, a form or settings group with fields the person edits, the detail of one object; anything that scrolls, filters, streams or is edited. Use a Panel for a summary or a decision. Because a working surface needs the white frame's edge and the rule under its header to show where it ends, while a glanceable widget reads best as a quiet grey group.
Never
A widget is a Panel and a working surface is a Card; never a Card inside a Panel or a Panel inside a Card. Because a white card in a grey panel is a box in a box; side by side, the grey widget and the white table tell the person which one to glance at and which one to work in.
Never
Put a Card inside a Card; a card is one frame, and rows inside it are split by rule-row, never by a second box. Because a box in a box stacks two edges until no one can tell which group a line closes.
Must
Put actions in CardAction beside CardTrigger, never inside it. Because CardTrigger is a button, and a button inside it is invalid HTML that also folds the card on every press.
Must
Add a CardMeta that says what is inside to every collapsible card, such as "3 arguments". Because folded, the header is all that shows, and a title alone does not say whether the body is worth opening.
Should
Name the topic in 1 to 4 words, sentence case. Because a collapsible card truncates its title to one line.
Never
Add a border or shadow to a Card. Because the card already draws its one edge, and a shadow makes it read as an overlay above the page.
Must
Give a card that holds a form or group of controls role="group" and aria-labelledby its title. Because Card renders a plain div, so a screen reader tabbing into its fields never hears what they belong to.
Should
Link to a page instead of folding a body taller than 480px open. Because a tall body pushes the cards below it off the screen when it opens.
Never
Put a button inside a Card rendered as a link. Because a button inside a link is invalid HTML, and a press on it also follows the link.

Accessibility

5 checks
A collapsible trigger is a button with aria-expanded and aria-controls, named by title and meta.
A folded body uses hidden="until-found", so find-in-page and fragment links open it.
Enter or Space toggles the trigger; Tab reaches the trigger, then each action.
Use role="group" with aria-labelledby on a card that holds controls.
Disabled trigger is keyed off aria-disabled; search still opens the body.

Edge cases

11 edge cases
Very long title
Wraps in a static card; truncates in a collapsible header and keeps the caret and actions.
One-word title
The header keeps 40px.
Empty body
Disable the trigger when there is nothing to show.
Wide child
overflow-hidden clips it; a table inside scrolls itself.
Right to left
The caret and the actions move to the left edge.
Zoom 200%
The header grows with the text; the body follows its content.
Toggled ten times in a second
Ends in the last state; the body cuts, so nothing lags.
Inside a form
The trigger is type="button" and never submits.
Request failed inside
Show an Alert inside; the card does not change.
Forced colors
The frame line survives as CanvasText; the kind badge keeps its text.
Reduced motion
Nothing changes: the body already cuts.

API

5 props
Card size
'sm' | 'default' | 'lg' default 'default'
Card kind
Kind
Card collapsible
boolean default false
Card render
ReactElement
CardTrigger disabled
boolean default false

Tokens

16 tokens
--surface-page
var(--paper)
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--line-row
oklch(var(--ink-c) / 0.08)
--accent-mark
var(--ink)
--radius-frame
10px
--space-3
12px
--space-4
16px
--space-6
24px
--surface-hover
oklch(var(--ink-c) / 0.05)
--line-focus
2px
--dur-fast
120ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)
--type-label
400 14px/16px var(--font-sans)
--type-label-sm
400 12px/16px var(--font-sans)
--type-body-sm
380 13px/18px var(--font-sans)
--text-3
oklch(var(--ink-c) / 0.66)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.