Card
v0.4.0A surface container with configurable padding and an optional header (icon + title + an `action` slot).
A surface container with configurable padding and an optional header (icon + title + an action slot).
Usage
The action slot is freeform — pass a close button, a menu, multiple buttons, or nothing. If icon, title, and action are all omitted, no header is rendered and children render directly in the card (unchanged from a plain Card). When a header is present, padding applies to the body instead of the whole card, so the header can span edge-to-edge with its own fixed padding.
Heading level
title renders inside an h1–h6 tag (not a span) so it takes its correct place in the page's heading outline — set via headingLevel, which defaults to 3. The default assumes the common case: a page's own h1, sections within it titled h2, and a Card nested inside one of those sections landing at h3. Override it when a card sits at a different depth — e.g. headingLevel={2} for a card used directly under a page's h1 with no intervening section:
Root element
The root renders as a <section>, labelled by title via aria-labelledby, whenever a title is given — an HTML5 <section> should have an accessible heading, and title is exactly that. With no title (a bare surface, or a header made up of just icon/action), the root stays a plain <div>, since an unlabelled <section> isn't a meaningful landmark.
Interactive (clickable) cards
Pass onSelect to turn the body into a real <button> — hover/focus affordance plus a centered, stacked content layout — for tile/action-card patterns like a component picker grid:
This is the same job MUI's CardActionArea does, folded into Card itself rather than a separate component — children must stay to phrasing content (text, spans, icons), since a native <button> can't contain block-level elements like <div>. onSelect is deliberately distinct from onClick: onClick is the plain pass-through to the root element (e.g. to stop click propagation on a whole panel), while onSelect specifically drives the interactive-body behavior described here.
Overriding surface styling
background/border/border-radius read from --card-background/--card-border/--card-radius custom properties (falling back to the design tokens above). A consumer embedding a Card for a different context — a docked panel with no elevation, a modal with a larger radius — can set these on its own wrapping class instead of fighting CSS-module specificity:
1.my-docked-panel {2 --card-border: none;3 --card-radius: 0;4}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
padding | none | sm | md | lg | none | Internal padding — applies to the body when a header is present |
icon | ReactNode | — | Optional icon rendered before the title |
title | ReactNode | — | Optional header title |
headingLevel | 1 | 2 | 3 | 4 | 5 | 6 | 3 | Which heading tag wraps title |
action | ReactNode | — | Optional content rendered at the end of the header |
onSelect | MouseEventHandler<HTMLButtonElement> | — | Makes the body a clickable <button> with hover/focus affordance and a centered stack layout |
scrollableBody | boolean | false | Keeps the header fixed and scrolls only the body; for cards with a bounded height |
children | ReactNode | — | Card content |
className | string | — | Additional class names |
Compositions
Open all 8 →Properties
Card props
| Prop | Type | Default | Description |
|---|---|---|---|
action | ReactNode | — | Optional content rendered at the end of the header (buttons, menus, a close control, etc.) |
children | ReactNode | — | Card body content |
className | string | — | Additional class names to merge with the component root element |
headingLevel | 5 | 1 | 2 | 3 | 4 | 6 | 3 | Which heading tag (`h1`–`h6`) wraps `title`, so the card's title takes its correct place in the page's heading outline. Defaults to `3`, matching a card nested one level below a page's `h1` title and a `section`'s `h2` — override per-instance when a card sits at a different depth. |
icon | ReactNode | — | Optional icon rendered before the title in the header |
onSelect | MouseEventHandler<HTMLButtonElement> | — | Makes the body a real `<button>` — hover/focus affordance plus a centered, stacked content layout — for tile/action-card patterns (a picker grid, for example) instead of a static surface. Distinct from `onClick` (a plain pass-through to the root element, e.g. for stopping click propagation). Omit for a plain, non-interactive card. |
padding | "sm" | "md" | "lg" | "none" | none | Controls internal padding; defaults to none so consumers opt in. Applies to the body when a header is present. |
scrollableBody | boolean | false | Keeps the header fixed and scrolls only the body when content exceeds the card's height. Only meaningful when the card has a bounded height (a modal or drawer panel, for example). |
title | ReactNode | — | Optional header title; omit along with `icon`/`action` to render no header |
Also accepts the native attributes of its root element (className, aria-*, event handlers, …).