Carousel

Carousel

Material 3 adaptive carousels for visual collections.

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 snap is 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.

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.