A .carousel displays a scrollable collection of visual items. The
default is Material 3's multi-browse layout: one large, one medium, and one
small item adapt as the active item changes. AutoInit() starts
every carousel except no-autoinit.
Give the container an accessible name and every direct item the
carousel-item class. Keep item text brief. At compact widths, use
no more than two lines of text and no more than three text-heavy items on
screen.
<div class="carousel" aria-label="Featured landscapes">
<a class="carousel-item" href="mountain-lake.html">
<img src="images/mountain-lake.jpg" alt="Mountain lake" />
<span class="carousel-item-content">Mountain lake</span>
</a>
<a class="carousel-item" href="forest-path.html">
<img src="images/forest-path.jpg" alt="Forest path" />
<span class="carousel-item-content">Forest path</span>
</a>
<a class="carousel-item" href="rocky-coastline.html">
<img src="images/rocky-coastline.jpg" alt="Rocky coastline" />
<span class="carousel-item-content">Rocky coastline</span>
</a>
</div>
<div class="mt-1">
<a class="button text" href="all-landscapes.html">Show all</a>
</div>
The text treatment is an opaque surface /
on-surface bounding shape, so its contrast does not depend on the
image. The component uses native scrolling, 8dp gaps, 16dp inline padding, 8dp
block padding, and 28dp item corners.
Layouts
Choose one layout for the content. Multi-browse is the default and is best for
many simple visuals. uncontained suits text-heavy or highly
customized items. hero spotlights one visual.
full-screen is an immersive vertical feed.
Uncontained
Uncontained items keep one width and can stop anywhere. Add
snap when snapping is preferable. Add
multi-aspect only when the source media genuinely has different
aspect ratios; widths stay between 9:16 and 16:9.
<div class="carousel uncontained" aria-label="Travel stories">
<a class="carousel-item" href="lake.html"
><img src="lake.jpg" alt="Mountain lake"
/></a>
<a class="carousel-item" href="forest.html"
><img src="forest.jpg" alt="Forest path"
/></a>
</div>
<div class="carousel uncontained multi-aspect" aria-label="Mixed-format photos">
<a
class="carousel-item"
style="--md-comp-carousel-item-aspect-ratio: 9 / 16"
href="portrait.html"
>…</a
>
<a
class="carousel-item"
style="--md-comp-carousel-item-aspect-ratio: 1 / 1"
href="square.html"
>…</a
>
<a
class="carousel-item"
style="--md-comp-carousel-item-aspect-ratio: 16 / 9"
href="landscape.html"
>…</a
>
</div>
Hero
Hero shows one large item and a 40–56dp preview of the next item. Add
center-aligned to center the large item and preview both
neighbors. Hero always snap-scrolls.
<div class="carousel hero" aria-label="Featured destinations">…</div>
<div class="carousel hero center-aligned" aria-label="Featured destinations">
…
</div>
Full-screen
Full-screen is vertical, edge-to-edge, and always snap-scrolls one item at a time in portrait compact and medium layouts. In landscape or at expanded widths, the same markup automatically adapts to a horizontal hero.
<div class="carousel full-screen" aria-label="Featured stories">
<article class="carousel-item" tabindex="0">…</article>
<article class="carousel-item" tabindex="0">…</article>
</div>
Behavior and accessibility
- Multi-browse, hero, and full-screen snap to their layouts.
- Uncontained uses free scrolling unless
snapis added. - Images receive a subtle parallax offset while scrolling.
- At medium, large, and extra-large container widths, two, three, or four large items fit before the medium and small items.
- Mouse users can drag the track with a grab cursor. Trackpads keep native two-axis page and carousel scrolling.
- Focus starts on the first item, never on the container.
- Left and right arrows move through horizontal items. Up and down arrows move through a full-screen carousel and otherwise leave the carousel.
- Home and End move directly to the first and last items.
On a vertically scrolling page, place a Show all action 4dp below every horizontal carousel. It should open a normal vertically scrolling view of the same items. If the carousel has a heading, a 48dp arrow action may sit beside that heading instead. Do not overlay previous/next controls on the carousel or place them beside its edges.
The component supplies a region, carousel and slide role
descriptions, item position labels, and indicator labels when authors do not
provide them.
Localize generated strings with the i18n option. Layout changes
follow the carousel's rendered width, including when a pane or side navigation
resizes without a window resize. A full-screen carousel automatically becomes
a horizontal hero in landscape or at an expanded width. Reduced-motion mode
removes parallax and size morphing, uses equal item widths, and disables
smooth scrolling.
An interval makes the carousel advance on its own, and content
that moves by itself has to be stoppable. So the pause contract is not
optional and there is no option that turns it off: an interval always pauses
while the pointer is over the carousel, while focus is inside it, and while
the tab is in the background, and
prefers-reduced-motion: reduce suppresses auto-advance
altogether. An explicit noWrap stops it after one pass instead
of looping. Arrow keys and set() stop at the ends either way
— a scroll track has ends, and auto-advance is the one caller that can
loop back past them.
Tokens
| Token | Default |
|---|---|
--md-comp-carousel-height |
240px compact; 320px medium; 360px expanded |
--md-comp-carousel-shape |
28px |
--md-comp-carousel-pressed-shape |
20px |
--md-comp-carousel-gap |
8px |
--md-comp-carousel-inline-padding |
16px |
--md-comp-carousel-block-padding |
8px |
--md-comp-carousel-large-item-width |
Responsive, capped by layout |
--md-comp-carousel-medium-item-width |
Responsive |
--md-comp-carousel-small-item-min-width |
40px |
--md-comp-carousel-small-item-max-width |
56px |
--md-comp-carousel-uncontained-item-width |
min(78%, 320px) |
--md-comp-carousel-item-aspect-ratio |
Per-item, multi-aspect only |
--md-comp-carousel-indicator-allowance |
40px, fixed height only |
The older --carousel-height author hook is still read.
Interactive state layer, focus indicator, and disabled opacity tokens are also
available in the compiled component source.
Initialization
document.addEventListener('DOMContentLoaded', function() {
const elems = document.querySelectorAll('.carousel');
const instances = Expressive.Carousel.init(elems);
});
Expressive.AutoInit(document.body, {
Carousel: {
i18n: { carousel: 'Galería', item: 'Elemento', of: 'de' }
}
});
Options
| Name | Type | Default | Description |
|---|---|---|---|
duration |
Number | 200 |
Milliseconds allowed for a programmatic scroll to land, and the part of
each auto-advance rest that is not interval.
|
fullWidth |
Boolean | false |
Add the full-width compatibility layout used by swipeable tabs. Prefer an M3 layout class for new carousels. |
indicators |
Boolean | false |
Show legacy paging dots. M3 recommends a nearby Show all path instead of overlay controls. |
noWrap |
Boolean | false |
Stop auto-advance at the last item instead of looping back to the first.
Arrow keys and set() stop at the ends either way — a
scroll track has ends.
|
interval |
Number | 0 |
Milliseconds to rest between automatic advances, on top of
duration — the gap follows each transition rather than
containing it, so a full cycle takes duration + interval.
0 leaves auto-advance off, and setting it turns on the pause
contract below. Each rest is armed by the move before it rather than on a
fixed phase, so a dropped tick costs a whole rest rather than leaving
whatever is left of one.
|
height |
Number | null |
Fixed track height in pixels. null sizes the carousel from its
content. A fixed height gives the indicators their own row below the track
instead of laying them over the media.
|
onCycleTo |
Function | null |
Called when the active item changes. Receives the item and whether the move was a drag. |
i18n |
Object | { carousel: 'Carousel', item: 'Item', of: 'of', indicators: 'Slides', slide: 'Slide' } |
Generated accessible label strings. indicators names the
indicator row and slide prefixes each dot, giving
“Slide 1”. Partial objects merge with the defaults. |
Methods
const instance = Expressive.Carousel.getInstance(elem);
instance.next();
instance.prev();
instance.set(3); // zero-based item index
instance.pause(); // stop auto-advance
instance.start(); // resume it
instance.destroy();
next and prev accept an optional item count.
set accepts an optional one-shot callback.
pause and start stop and resume auto-advance, and
do nothing without an interval. destroy removes
generated labels, size roles, indicators, listeners, the scroll-track
wrapper, and the auto-advance timer.