Card
Card is a styled container component with border, box-shadow, and border-radius that groups related content. It supports structured layouts with subcomponents or custom content.
Page navigation navigation
By default, Card renders a <div>, which has no implicit semantics. The right pattern depends on how the Card is used: as a standalone region, or as one item in a collection.
Choose the right semantics for the context
Standalone Card
When a Card stands on its own as a meaningful chunk of page content, render it as a <section> so it is exposed as a landmark.
<Card as="section">
<Card.Heading>Project Alpha</Card.Heading>
<Card.Description>Card content</Card.Description>
</Card>
When Card.Heading is present, the section's aria-labelledby is automatically wired to the heading's id. If there is no visible heading, provide your own aria-label or aria-labelledby on the Card.
A visible heading is strongly recommended on standalone Cards so that sighted and non-sighted users have an equivalent experience. Choose a Card.Heading level (h2 through h6) that fits the surrounding document outline. The default is h3.
Card in a collection
When multiple Cards are rendered together, place them inside a list. The <li> provides grouping, so each Card should remain a <div> (the default) and should not also be set to as="section".
<ul>
<li>
<Card>
<Card.Description>Card within a list</Card.Description>
<Card.Metadata>Updated 2 hours ago</Card.Metadata>
</Card>
</li>
{/* …more list items */}
</ul>
Headings must not be used inside a list-item Card. Heading elements combined with list semantics disrupt the reading order for screen reader users. Use Card.Description (or other non-heading content) for the primary text, and avoid heading elements in custom content too.
Provide context for interactive controls
Card.Action renders a single interactive control (typically an IconButton) in the top-right corner. The control's accessible name must identify which Card it acts on. "More options" is not enough when the page contains several Cards.
<Card.Action>
<IconButton icon={KebabHorizontalIcon} aria-label="More options for Project Alpha" variant="invisible" />
</Card.Action>
The same applies to any interactive content passed as Card children. When several Cards expose the same controls, include the Card's subject in each control's accessible name. Visually hidden text is a common way to do this.
Images and icons
Card.Image accepts the standard <img> attributes. Provide a meaningful alt value for informative images, and an empty string (the default) for decorative ones.
Card.Icon is decorative by default and is hidden from assistive technologies. Only set aria-label when the icon conveys information that is not already in the surrounding text.
How to test the component
Integration tests
- A standalone Card is exposed as a region landmark with an accessible name that reflects the Card's subject
- A list of Cards is rendered as a labelled
<ul>or<ol>, with each Card inside an<li>, and the Cards do not introduce additional landmarks - No heading elements are present inside list-item Cards
- When multiple Cards expose the same controls, each control's accessible name identifies which Card it acts on
- Informative
Card.Imageimages have meaningfulalttext; decorative images have an emptyalt
Component tests
Cardrenders as<div>by default, and as<section>whenas="section"- When
as="section"and aCard.Headingis present, the section'saria-labelledbyis automatically wired to the heading'sid Card.Headingrenders at the configured heading level, defaulting to<h3>Card.Iconis hidden from the accessibility tree unless anaria-labelis providedCard.Actionexposes its child control with the consumer-provided accessible name