Card
Hero example
When to use
Reach for Card whenever you need an elevated, opaque container for a block of content — a dashboard tile, a settings panel, an inline domain object that isn't one of the specialised InlineCards. Card is the default container: every page in the dashboard composes from Card and Section, and most surfaces in apps/web use it as well.
Don't reach for Card when the surface needs to feel translucent or refractive — that's Glass. Don't use it for full-screen overlays — that's Sheet. For domain objects with specific data shapes (filings, grants, board consents), use the matching InlineCard variant — Card is the generic fallback.
Anatomy
A single <div class="card"> with no internal structure. The padding prop applies via inline style and accepts any CSS length (number → px). All HTMLAttributes<HTMLDivElement> pass through via spread.
The visual identity comes from .card in @matter/components/styles.css:
background: var(--bg-elev)border: 1px solid var(--border-soft)border-radius: var(--radius-lg)
There are no slots — Card is a fully transparent container.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| padding | number | string | 32 | — |
| className | string | — | — |
| style | React.CSSProperties | — | — |
| children | React.ReactNode | — | — |
Also accepts Omit<React.HTMLAttributes<HTMLDivElement>, "children">.
States
Card itself has no states — it's a static container. If you need hover or active states, wrap Card in an interactive element or apply :hover styles via the consumer's CSS using a class composition pattern (e.g. <Card className="card-clickable">).
Density
In compact, the default padding drops from 32 to 24 (if not overridden). The radius and border stay constant.
Themes
Tokens consumed
Background reads --bg-elev. Border reads --border-soft. Radius reads --radius-lg.
Accessibility
- Keyboard interactions. None — Card is non-interactive. Children carry their own contracts.
- ARIA roles and properties. Generic container, no role override. If Card represents a named region with a heading, wrap the heading in an
<h2>and setaria-labelledbyon the Card pointing to that heading's id — or render the surrounding element as a<section>and let it carry the semantics. - Focus order. No internal focus.
- Screen-reader expectations. Children announce in DOM order. No prefix or suffix from the Card itself.
- Reduced motion. N/A — no motion.
- Forced colors. Background →
Canvas; border →ButtonBorder. Radius preserved. - WIG rules. No specific WIG implications — Card is passive. Consumers wrap interactive children with the right WIG contracts.
Do / Don't
padding={48} for hero-level cards and padding={16} for inline summary cards. The default 32 is the canonical middle ground.role="region" and aria-labelledby to Card when it represents a named section. Headings inside need a heading element.<section> substitute without setting role. Semantic HTML beats generic divs.Recipes
This component appears in every recipe under /recipes. Card is the default container.
Code example
import { Card } from "@matter/components";
export function RecentActivityCard({ events }: { events: AuditEvent[] }) {
return (
<Card aria-labelledby="recent-activity-h2">
<h2 id="recent-activity-h2">Recent activity</h2>
<ul>
{events.map((event) => (
<li key={event.id}>{event.summary}</li>
))}
</ul>
</Card>
);
}Source
packages/components/src/CardEndpointBadge
Translucent pill labelling an HTTP endpoint — verb (method-colored) and path (mono). Omit the path for verb-only mode (the V1 VerbPill role).
Sheet
Full-bleed overlay — scrim blurs the page behind, centered glass card holds focused content. Escape closes; backdrop click closes. Use for endpoint detail, action confirmation, contextual zoom. For full Radix accessibility (focus trap, escape stack), use Dialog from @matter/components/headless instead.