Card Component
The `card` component is a responsive, self-contained card element that adapts its layout based on its container size using container queries. It features scoped
Card anatomy keeps content roles explicit
Media, header, content, and footer each own a predictable part of the card.
<article data-card>
<div data-card-media>
<img src="report-cover.jpg" alt="" />
</div>
<div data-card-body>
<header data-card-header>Design systems report</header>
<div data-card-content>
<p>Patterns, adoption, and accessibility findings.</p>
</div>
<footer data-card-footer>Updated 12 minutes ago</footer>
</div>
</article> Patterns, adoption, and accessibility findings.
More examples
Each example introduces a different part of the shipped API.
Adaptive cards respond to their container
Resize the preview: media moves beside the body when the card container reaches its layout threshold.
<article data-card data-card-layout="adaptive">
<div data-card-media><img src="cover.jpg" alt="" /></div>
<div data-card-body>
<header data-card-header>Container-aware card</header>
<div data-card-content>...</div>
<footer data-card-footer><a href="/details">Read details</a></footer>
</div>
</article> Its layout changes from stacked to horizontal based on available card width.
Overview
The card component is a responsive, self-contained surface that adapts its layout based on its container size. Its selectors stay component-scoped through explicit card host and part names, and its visual roles are controlled by semantic tokens.
Live card
Hover it, then resize the page to see its container-query layout.
<div style="container-type: inline-size; container-name: card-container;">
<card>
<card-media aria-hidden="true" style="background: linear-gradient(135deg, var(--accent), var(--secondary)); min-height: 10rem;">
</card-media>
<card-body>
<layout-cluster gap="var(--space-sm)" justify="space-between">
<badge status="primary">Featured</badge>
<small>6 min read</small>
</layout-cluster>
<card-header>Designing with container queries</card-header>
<card-content>
<p>A card can respond to the space it receives instead of the viewport.</p>
</card-content>
<card-footer>
<button type="button">Read article</button>
</card-footer>
</card-body>
</card>
</div> A card can respond to the space it receives instead of the viewport.
The card supports custom-element, class, and data-* hosts. The same structure can use <card>, .card, or [data-card]; its parts likewise support card-body, .card-body, and [data-card-body].
Key Features
- Container Queries: Layout adapts to the card’s own width, not viewport
- Explicit Styling: Selectors target only card hosts and named card parts
- Responsive Design: Switches from vertical to horizontal layout at 32rem container width
- Elevation Effects: Hover animations with shadow changes
- Flexible Content: Supports media, header, content, and footer sections
Structure
The card component consists of several semantic elements:
card-media: Image or media content (16:9 aspect ratio by default)card-body: Main content containercard-header: Card title (larger, bold text)card-content: Primary content (flexible growth)card-footer: Footer content (smaller, muted text)
Layout Behavior
Narrow Layout (< 32rem)
- Vertical stack layout
- Media at top (16:9 aspect ratio)
- Body content below
Wide Layout (≥ 32rem)
- Horizontal layout
- Media on left (150px square)
- Body content on right (60% width)
Styling Customization
The card supports several CSS custom properties for customization:
--card-background--card-color--card-border-color--card-border-width--card-radius--card-padding--card-gap--card-header-padding--card-header-padding-block-end--card-footer-padding--card-footer-padding-block-start--card-shadow--card-hover-transform--card-hover-shadow
These public variables work directly on a card or from a scoped ancestor:
.pricing-grid { --card-padding: 2rem; --card-radius: 1rem;}
.pricing-grid > card[data-featured] { --card-border-color: var(--accent);}Card internals use private computed aliases, so inherited public overrides are not replaced by the component selector.
card-header and card-footer use the card inset when they are direct children of a card. When they are nested in card-body, the body owns the inset instead, so padding is never doubled.
Usage Example
<card> <card-media> <img src="article-cover.jpg" alt="A CSS layout drawn on a whiteboard"> </card-media> <card-body> <card-header>Designing with container queries</card-header> <card-content> <p>A card can respond to the space it receives instead of the viewport.</p> </card-content> <card-footer> <button type="button">Read article</button> </card-footer> </card-body></card>Hover Effects
- Transform: Moves up 2px by default
- Elevation: Flat by default; opt in with
--card-hover-shadowand--card-hover-transform
Technical Implementation
Selector scope
:is(card, [data-card], .card) { /* card host */ }:is(card-body, [data-card-body], .card-body) { /* card body */ }Explicit host and part selectors prevent unrelated page content from inheriting card layout.
Container Queries
@container card-container (min-width: 32rem) { /* Styles applied when card is at least 32rem wide */}The card establishes itself as a container with container-type: inline-size.
CSS Nesting
Uses native CSS nesting for organized, hierarchical styles.
Browser Support
Cards follow the shared Browser Support baseline. Their narrow, stacked composition is the default; the horizontal arrangement is a container-query enhancement when enough space is available.
Accessibility
- Semantic structure with proper content hierarchy
- Hover effects respect
prefers-reduced-motion - Color contrast maintained through theme variables
- Keyboard navigation support (inherited from base styles)