Carousel

A carousel shows one thing several ways in the space of one: tall cards side by side, one in view and the next cut at the edge, or one slide at a time across the whole width. The slides snap sideways, one to a stop, under a finger, a trackpad or the keyboard, and a row of page dots one square below says which slide you are on and takes you to any other. Previous and next buttons can stand beside the dots.

With slide-width="auto" it is a strip: cards of their own widths, tall ones and wide ones, a few in view and the next cut at the edge, each stopping at the strip’s start. A strip can run on past its column to the window’s edge while its first card keeps to the column, loop without an end, and, when asked, move on by itself.

Everything a carousel holds is in the page and reachable without it: never put the only copy of something important in a slide.

Three observations

Text slides keep the same measure while their lengths differ.

Slides twelve squares wide

Swipe or scroll sideways, or use the buttons and the dots. Moving to another slide fires change.

One slide at a time

Without a slide width each slide is the carousel's own width. The last dot marks an end.

<ds-carousel label="Colours" slide-width="12" buttons>
<figure>…</figure>
<figure>…</figure>
<figure>…</figure>
</ds-carousel>

Each element inside is a slide. Name the carousel for what the slides show with label, a translation key or, with literal, the words as written.

WidthSlides
No slide-widthThe carousel’s own width, one at a time.
slide-width="12"Twelve squares each, as many in view as fit; the next one is cut at the edge.
slide-width="auto"Each slide as wide as it is: a strip of cards to browse.

gap sets the squares between slides, one by default and 0 for none. A slide is as tall as the tallest one.

carousel.addEventListener("change", event => {
console.log(event.detail.index); // the slide a person moved to
});
carousel.current = 2; // goes there, smoothly unless the reader asks for less motion

change fires when a person moves to another slide: by swiping, scrolling, a dot or a button. Setting current, or calling goTo(), next() or previous(), moves the carousel without firing it.

A strip of cards of their own width

slide-width="auto" gives each card its own width, several in view. Swipe or scroll sideways, or use the buttons; each card stops at the start.

Without an end, moving on by itself

loop holds the cards three times over; autoplay moves on every five seconds, holds while you point, touch or focus, and always has a pause button.

<ds-carousel label="Work" slide-width="auto" no-dots bleed="end" buttons>
<ds-card href="/lists" tone="sharp-blue" width="14" height="22" media="center">…</ds-card>
<ds-card href="/portal" tone="ink" width="28" height="22" media="bleed">…</ds-card>
<ds-card href="/money" tone="apple-green" width="14" height="22" media="center">…</ds-card>
</ds-carousel>
OptionWhat it does
bleednone (the default) keeps the strip in its box. end runs it on to the window’s end edge, the first card still on the box’s start, so nothing stands before the column. both runs it to both edges; cards slide out past the column’s start.
loopHolds the slides three times over, so the strip has no end either way. The copies are hidden from assistive technology and never focused. A looping carousel draws no dots.
autoplayMoves on a slide at a time by itself: empty for every five seconds, or a number of milliseconds (at least 1000). Without loop it goes back to the first slide after the last. See Autoplay.
freeSlides freely, without stopping at slides.

bleed, loop and autoplay are read when the element first draws; put a new element in its place to change them.

  • The element is a region described as a carousel and named by label. Each slide is a group described as a slide and labelled “2 of 5”, unless the page labels it.
  • The dots are a group of buttons, the current one marked aria-current; the arrow keys, Home and End move between them. The buttons are named “Previous slide” and “Next slide” and stop at the ends.
  • When the slide in view changes, a hidden live region says “3 of 5” politely.
  • The track is an ordinary scroller: swiping and trackpads move it, and so do the arrow keys where the browser lets a scroller take focus.
  • With reduced motion, a dot or a button jumps to its slide at once and the dots do not grow.
  • Previous and next point back and on along the reading direction, right to left too.

All the words are the shared words, in every language.

A carousel moves on by itself only when asked to, with autoplay. Then it always shows a pause button (named “Pause”, then “Play”), holds while the pointer is over it, a finger is on it or the keyboard is in it, holds while it is out of view or the page is hidden, and never moves for a reader who asks for reduced motion. That meets WCAG 2.2.2 (Pause, Stop, Hide). Use it for a strip that shows off, never for one that holds something a person must read or act on: a slide that leaves while someone reads it costs more than it shows.

AttributeTypeDefaultWhat it does
labelstringThe shared word “Carousel”The region’s name.
literalbooleanfalseShow label as written, never looked up.
currentnumber0The slide in view, from 0.
buttonsbooleanfalsePrevious and next buttons beside the dots.
no-dotsbooleanfalseLeave the dots out.
endbooleanfalseDraw the last dot as an end marker.
slide-widthnumber or autoThe carousel’s widthEach slide’s width in whole squares; auto for slides of their own width.
gapnumber1Whole squares between slides.
bleednone, end, bothnoneHow far a strip runs past its box.
loopbooleanfalseHold the slides three times over, without an end.
autoplayempty or millisecondsOffMove on by itself, with a pause button.
freebooleanfalseSlide freely, without stopping at slides.
EventDetailWhat it means
change{ index, previous }A person moved to another slide.
PartWhat it is
trackThe scroller holding the slides.
controlsThe row of buttons and dots.
previous, next, pauseThe buttons.
dotsThe page dots.

Slides are the carousel’s width or whole squares wide, a whole number of squares apart. The controls stand one square below the track, a row one square high starting at its edge: each button in a one-square cell (the icon button is one control high), each dot one square. A carousel of one slide has no controls. Snapping is mandatory, one slide to a stop.

--ds-carousel-slide and --ds-carousel-gap hold the widths, for a product that sets them for a narrower screen, such as the column less two squares so the next slide shows at the edge.

import { Carousel } from "@0001-labs/design-system-1/DS1/react/carousel";
<Carousel label={t("Colours")} slideWidth={12} buttons onChange={index => setColour(index)}>
{colours.map(colour => <Card key={colour.key} colour={colour} />)}
</Carousel>

Carousel renders ds-carousel with the slides in the light DOM and the region and its name in the markup, so the server’s HTML is already a named region. label is shown as given. dots={false} leaves the dots out; slideWidth="auto", bleed, loop, autoplay and free make it a strip. Register ds-carousel in the browser before hydration.