Card
Holds one thing together, like a goal with its details and a button.
Examples
Plus plan
No monthly fee and 1.5% back on every purchase.
Get $20 back
Invite a friend. You both get $20 after their first payment.
Styles
Vacation fund
$290 of $480
surfacedefault
Vacation fund
$290 of $480
outline
Vacation fund
$290 of $480
raised
Vacation fund
$290 of $480
glow
Interactive
Vacation fund
$290 of $480
falsedefault
true
States
Savings
rest
hover
focus
active
Usage
Rules
9 rules
- Must
- Use outline or raised for a card on a grey area. Because the default card is the same #f6f5f2 as bg-surface, so it loses its edge there.
- Must
- Light one glow card per view and keep the rest surface or outline. Because two lit cards split the eye, and the glow stops meaning the one thing to pick.
- Never
- Write a gradient on a card; use variant="glow". Because a hand-made gradient misses the glow's hover power-up and its forced-colors fallback.
- Should
- Give a card one main action in the footer and keep the rest ghost.
- Never
- Nest a Card in a Card; use spacing or a divider. Because an inner card is the same grey as the outer one, so it adds padding and a radius with no edge.
- Should
- Title a card with the thing, in sentence case, not a sentence.
- Must
- Make a whole-card press one control with interactive and href; put no button or link inside it. Because a button nested in the card's button is invalid HTML, and its press also opens the card.
- Should
- Take the width from the grid column, not a fixed w-*, which breaks on a phone.
- Must
- Render no Card with nothing in it; it has no empty state. Because an empty card still draws a grey 16px-radius block with 20px padding, a blank tile in the grid.
Accessibility
4 checks
- A plain card is a div and takes no focus; Tab goes to the first control inside.
- An interactive card is a link or a native button; Enter and Space run it and a ring shows.
- CardTitle is an h3; change the level to fit the page outline.
- Never put a button or link inside an interactive card.
Edge cases
5 edge cases
- Long title and link
- Text wraps at spaces and hyphens; a string with neither overflows. Add break-words where user text is shown.
- Narrow to 120px
- The 20px padding leaves 80px for text.
- Right to left
- Padding is symmetric and text aligns to the start.
- Forced colors
- Glow gets a 1px CanvasText border; surface and raised have no edge. Use outline where the boundary matters.
- Loading
- No loading state. Reserve the height while content loads.
API
3 props
variant'surface' | 'outline' | 'raised' | 'glow'default'surface'interactivebooleandefaultfalseclassNamestring
Tokens
27 tokens
--surface- #f6f5f2
--surface-2- #efeeea
--page- #ffffff
--raised- #ffffff
--line- #e6e4df
--radius-panel- 16px
--shadow-card- 0 1px 2px rgb(var(--shadow-ink) / 0.06), 0 1px 1px rgb(var(--shadow-ink) / 0.04)
--accent- #e4f222
--on-accent- #0f0f0d
--glow-accent-from- #dbec1c
--glow-accent-to- #f6ff7c
--glow-accent-edge- rgb(255 255 255 / 0.7)
--glow-accent-bloom- rgb(255 255 255 / 0.28)
--glow-accent-cast- 112 120 20
--glow-accent-from-lit- #e4f42c
--glow-accent-to-lit- #faff9c
--glow-accent-from-press- #c8d915
--glow-accent-to-press- #e6f24a
--fg- #0f0f0d
--fg-2- #4a4943
--text-sm- 14px
--text-lg- 18px
--weight-semibold- 600
--leading-tight- 1.2
--dur-instant- 80ms
--dur-base- 200ms
--ease-out- cubic-bezier(0.16, 1, 0.3, 1)