Cards
Group related facts into one readable surface on a page or panel.
Quick usage
Import the shared component; never copy its markup into a feature. The Web TSX tab beside the live stage has a complete, working example.
import ContentCard from '@/shared/components/ContentCard';Live stage and code
The live component and its controls on the left, the code on the right; change a control and the code follows: Web TSX (the component in a working example), Tailwind and HTML (the markup it renders, with its data attributes), React Native, SwiftUI and Jetpack Compose. The header theme switch drives every stage.
Props
Read from the component's source when the docs build: names and defaults from its signature, types and descriptions from its JSDoc. A blank type means the prop has no JSDoc yet.
| Prop | Type | Default | Description |
|---|---|---|---|
header | React.ReactNode | - | — Row content (title/actions). Rendered inside `.contentCardHeader` (flex, space-between). |
footer | React.ReactNode | - | — Optional footer row rendered inside `.contentCardFooter`. |
children | - | - | - |
bodyClassName | string | - | — Optional body class; defaults to padded scrollable `.contentCardBody` (or `.contentCardBodyUnbounded` when `bounded={false}`). Use `contentCardBodyNested` from the same CSS module when a child owns scrolling (virtualized lists, sticky subheaders) — only meaningful in bounded mode. |
className | - | - | - |
compact | boolean | - | — Denser header/footer padding and radius (data-dense panels like Materials, tool panels). |
tightHeader | boolean | - | — Extra-tight header padding (single-row toolbars). Use with `compact`. |
bounded | boolean | true | — When `false`, card does not participate in a flex-height chain; content is not clipped and the body does not scroll. |
Source: frontend/src/shared/components/ContentCard/ContentCard.js
DOM contract
What the rendered element carries: the data attributes its styles key on, its roles and ARIA, its focus style, and whether its stylesheet draws the 44px hit boundary. Also read from the source.
| Data attributes | data-ds |
| Roles | Native element semantics only |
| ARIA | None set by the component |
| Focus | No :focus-visible rule of its own |
| 44px hit boundary | Not a pressable root |
Accessibility and touch
A pressable card is one target edge to edge.
Cards is passive: it reports a condition and is not pressed, so it has no hit boundary. If a design makes it pressable, it gains the 44 by 44 boundary and is marked /* ds-chip: why */.
Rules and constraints
Use it for
- A record summary, a metric, a section of a details page.
Avoid
- Never a card inside a card inside a card: density steps inward, it does not nest boxes.
- Never a drop shadow on a resting card: depth is lightness.
Constraints
- --bg-elevated, a 1px --border, --radius-surface (6px).
- Standard card padding 16px; a nested node card 12px by 8px.