An <article> is an elevated card. Any heading is the
headline, <p class="subhead"> is the optional
subhead, <p class="supporting-text"> is supporting
copy, direct <img>, <picture>, or
<figure> is media, and direct .actions
is the action row. Include only the slots the content needs.
There is no card-content, card-title, or
card-action class.
Tokens follow the
M3 card spec.
The elevated container is surface-container-low with
12dp corners. The default sits at elevation 1; an interactive card
rises to 2 on hover. The
headline is title-medium / on-surface, the
subhead is title-small / on-surface, and
supporting text is body-medium /
on-surface-variant. Inset is 16dp.
Weekend in the mountains
Three-day itinerary
Explore trails, overlooks, and places to rest along the way.
<article>
<header>
<h3>Weekend in the mountains</h3>
<p class="subhead">Three-day itinerary</p>
</header>
<p class="supporting-text">Explore trails and overlooks.</p>
<img src="images/mountains.jpg" alt="Mountain valley beneath a cloudy sky">
<div class="actions">
<button type="button" class="text">Share</button>
<button type="button" class="tonal">View trip</button>
</div>
</article>
Variants
Default is elevated. filled uses
surface-container-highest at rest (no shadow).
outlined (or border) draws a 1dp
outline-variant stroke over surface.
Elevated
Surface-container-low, elevation 1.
Filled
Surface-container-highest, no elevation.
Outlined
Surface plus a 1dp outline.
<article>…</article>
<article class="filled">…</article>
<article class="outlined">…</article>
Primary action and states
A static card has no hover treatment. To make the card an entry
point, wrap its primary content in a direct
<a class="primary-action">. That gives the card M3
hover, focus, pressed, and focus-indicator states. A directly
actionable card has no other links or buttons. When the content
needs several actions, leave the card static and put the controls in
.actions. Toggle dragged or
picked-up during reordering; that state keeps its dragged
elevation and 16% state layer even if the primary action remains
hovered or pressed. For a disabled destination, remove
href and set both
aria-disabled="true" and tabindex="-1" on
the primary action.
Upcoming reservation
Open the reservation details.
<article>
<a class="primary-action" href="/reservation/42">
<h3>Upcoming reservation</h3>
<p>Open the reservation details.</p>
</a>
</article>
Collections
card-collection creates a responsive grid with no more
than 8dp between cards. Cards keep their variant-specific resting
elevation: level 1 for elevated, level 0 for filled and outlined.
Add picked-up or dragged only while a card is
being moved. Add list, staggered,
mosaic, or carousel uncontained for the other collection
layouts. Sorting and filtering controls stay outside the collection.
Responsive grid
The default uses auto-fit columns. Change the minimum card width or replace the complete grid track token when the content needs a different column strategy.
Pho
$12
Burrito Bowl
$9
Quinoa Salad
$10
List
list keeps one card per row and 8dp spacing without
changing each card's variant-specific resting elevation.
Pho
Rice noodles, herbs, and aromatic broth · $12
Quinoa Salad
Roasted vegetables, herbs, and lemon dressing · $10
Staggered grid
staggered packs intrinsic card heights
into masonry-like columns. Source order flows down each column.
Pho
$12
Combo #2
Two entrées, three sides, and two drinks · $28
Pumpkin Soup
$8
Combo #1
Entrée, side, and drink · $14
Mosaic grid
mosaic uses dense grid placement. Set
column and row spans on individual cards from application data.
Pho
$12
Combo: Entrée, side, and drink
$14
Combo #2: 2 entrées, 3 sides, and 2 drinks
$28
Quinoa Salad
$10
Carousel
Add carousel uncontained. Add carousel-item
to every card; AutoInit() supplies the M3 scroll track.
Pho
Rice noodles, herbs, and aromatic broth · $12
Quinoa Salad
Roasted vegetables, herbs, and lemon dressing · $10
Combo #1
Entrée, side, and drink · $14
<!-- Responsive grid (the default). Override the track token as needed. -->
<section class="card-collection" aria-label="Dinner menu">
<article class="outlined">…</article>
<article class="outlined">…</article>
</section>
<!-- List -->
<section class="card-collection list" aria-label="Dinner menu">
<article class="outlined"><h3>Pho</h3><p>$12</p></article>
<article class="outlined"><h3>Quinoa Salad</h3><p>$10</p></article>
</section>
<!-- Intrinsic-height staggered grid -->
<section class="card-collection staggered" aria-label="Dinner menu">
<article class="outlined"><h3>Pho</h3><p>$12</p></article>
<article class="outlined"><h3>Combo #2</h3><p>Two entrées, three sides, and two drinks · $28</p></article>
</section>
<!-- Dense mosaic. Set spans per card from application data. -->
<section class="card-collection mosaic" aria-label="Dinner menu">
<article style="--md-comp-card-collection-row-span: 3">…</article>
<article style="--md-comp-card-collection-column-span: 2; --md-comp-card-collection-row-span: 4">…</article>
</section>
<!-- M3 uncontained carousel. AutoInit starts the Carousel component. -->
<section class="card-collection carousel uncontained" aria-label="Dinner menu">
<article class="outlined carousel-item"><h3>Pho</h3><p>$12</p></article>
<article class="outlined carousel-item"><h3>Quinoa Salad</h3><p>$10</p></article>
</section>
Media
A direct <img> is full-bleed across the top and its
media surface has rounded corners matching the card.
Wrap it in a <figure> if you want a caption on
the image. The <figcaption> is an opaque, rounded
bounding shape using the paired surface and
on-surface roles, so image colors cannot reduce the
contrast of its text or icons. Normal text must retain at least
4.5:1 contrast; large text and meaningful icons require at least
3:1. Recheck those ratios if you override either color token.
I am a very simple card. I am good at containing small bits of information.
<article>
<figure>
<img src="images/sample-1.jpg" alt="">
<figcaption>
<span class="material-symbols" aria-hidden="true">landscape</span>
<span>Card title</span>
</figcaption>
</figure>
<p>I am a very simple card.</p>
<div class="actions">
<button type="button" class="text">Action</button>
</div>
</article>
Horizontal
The same card can use two orientations without changing its content
order. Add horizontal to move the media to the start and
stack the headline, supporting text, and action beside it. A direct
.primary-action link can wrap those same slots when the
whole horizontal card is one destination. Without a size helper, its
height follows content with a 240px minimum; small,
medium, and large provide fixed expanded
heights. Below 600px, both forms return to the vertical layout and
fixed heights reset to content.
Performances at The Hideout
Watch exclusive live performances at The Hideout every Saturday starting at 7pm.
Performances at The Hideout
Watch exclusive live performances at The Hideout every Saturday starting at 7pm.
<!-- Vertical -->
<article class="outlined">
<img src="images/the-hideout.jpg" alt="Musician playing guitar during a live performance">
<h3>Performances at The Hideout</h3>
<p>Watch exclusive live performances at The Hideout every Saturday starting at 7pm.</p>
<div class="actions">
<button type="button" class="tonal">Get tickets</button>
</div>
</article>
<!-- Horizontal: only the orientation class changes. -->
<article class="outlined horizontal">
<img src="images/the-hideout.jpg" alt="Musician playing guitar during a live performance">
<h3>Performances at The Hideout</h3>
<p>Watch exclusive live performances at The Hideout every Saturday starting at 7pm.</p>
<div class="actions">
<button type="button" class="tonal">Get tickets</button>
</div>
</article>
<!-- Directly actionable horizontal card. -->
<article class="outlined horizontal">
<a class="primary-action" href="/performances/the-hideout">
<img src="images/the-hideout.jpg" alt="Musician playing guitar during a live performance">
<h3>Performances at The Hideout</h3>
<p>Watch exclusive live performances every Saturday.</p>
</a>
</article>
Reveal
An <aside> expands in normal flow below the persistent
media, headline, and subhead. Place a
.card-reveal-trigger button over the media;
the same button opens and closes the details.
Cards.Init() (and AutoInit() when the card
contains both that button and one identified direct <aside>)
owns aria-expanded and applies the closed-panel styles
only after the disclosure contract is accepted. The trigger must be
enabled when Cards initializes; rejected, disabled-only, unidentified,
or multi-panel disclosures leave every panel visible, available, and
untouched. The native button handles Enter and Space. Escape closes
an open panel and returns focus to its trigger. A trigger belongs to
its closest <article>; nested cards, including cards
inside an outer reveal panel, keep their own disclosure ownership and
do not initialize or toggle the outer card. The reveal grows the
card instead of covering or internally scrolling it.
This replaces legacy .activator markup and heading-based
close controls. Use the same native button to open and close the panel.
Ana Russo
Sibling
<article class="filled">
<figure>
<img src="images/ana-russo.jpg" alt="Portrait of Ana Russo">
<button type="button"
class="card-reveal-trigger"
aria-label="Toggle contact details"
aria-controls="ana-contact"></button>
</figure>
<header class="card-reveal-summary">
<h3>Ana Russo</h3>
<p class="subhead">Sibling</p>
</header>
<aside id="ana-contact">
<address class="reveal-actions">
<a class="reveal-action" href="tel:+16505551234">
<span class="material-symbols" aria-hidden="true">call</span>
<span>(650) 555-1234</span>
</a>
<a class="reveal-action" href="mailto:hey@anarusso.com">
<span class="material-symbols" aria-hidden="true">mail</span>
<span>hey@anarusso.com</span>
</a>
</address>
</aside>
</article>
Expanding card
An expanding card performs a shared-container transition from a
compact feed item into a full-screen modal detail surface. Use an
<article class="expanding-card"> with a direct
<dialog class="expanding-card-dialog">. Keep the
same hero image in both states so the media appears to grow with the
container. ExpandingCard.Init() and
AutoInit() wire up the modal, measured clip origin,
back action, focus return, Escape, and reduced motion.
Override --md-comp-expanding-card-motion-duration to change
container timing. Closing waits for the actual clip transition, including
cancellation; zero duration and reduced motion close immediately.
Reopening or destroying the instance invalidates pending close cleanup.
The onClose callback runs when closing starts.
Listen to Glass Souls
From your recent favorites
<article class="outlined expanding-card">
<figure>
<img src="images/glass-souls.jpg" alt="Pastel balloons floating above flowers">
<button type="button" class="expanding-card-trigger"
aria-label="Open Glass Souls album" aria-haspopup="dialog"></button>
</figure>
<header class="expanding-card-summary">
<h3>Listen to Glass Souls</h3>
<p class="subhead">From your recent favorites</p>
</header>
<dialog id="glass-souls-card" class="expanding-card-dialog"
aria-labelledby="glass-souls-title">
<button type="button" class="expanding-card-close" aria-label="Back">
<span class="material-symbols" aria-hidden="true">arrow_back</span>
</button>
<figure class="expanding-card-hero">
<img src="images/glass-souls.jpg" alt="Pastel balloons floating above flowers">
</figure>
<div class="expanding-card-content">
<header class="expanding-card-detail-header">
<h2 id="glass-souls-title">Glass Souls’ Biggest Hits</h2>
<div class="expanding-card-actions">
<button type="button" class="expanding-card-favorite" aria-label="Favorite album">
<span class="material-symbols" aria-hidden="true">favorite</span>
</button>
<button type="button" class="expanding-card-play" aria-label="Play album">
<span class="material-symbols" aria-hidden="true">play_arrow</span>
</button>
</div>
</header>
<div class="expanding-card-track">
<strong>Fragile</strong><small>Glass Souls</small>
<time datetime="PT3M34S">3:34</time>
</div>
</div>
</dialog>
</article>
Sizes
small, medium, and large
lock the height at 300px, 400px, and 500px so a row of cards lines
up. Media takes the top 60%; the action row sticks to the bottom.
These sizes are not in the M3 spec — they are optional layout
helpers.
Small
The small card is 300px tall.
<article class="small">…</article>
<article class="medium">…</article>
<article class="large">…</article>