Carousels
A row of slides seen one or a few at a time: tours, photos, reviews. The browser scrolls and snaps the row - a swipe, a wheel, the arrow keys - and the buttons move a slide back or on.
Examples
carousel on the frame, a named region; carousel-inner on the row, which takes the focus; carousel-item on each slide. Buttons with command="--prev" or "--next" and aria-controls naming the carousel move a slide, round the ends. An empty carousel-indicators fills with a button per slide. The slide in place is aria-current="true".
<div class="carousel" id="carousel-basic-tours" role="region" aria-label="Tours">
<div class="carousel-inner" tabindex="0" aria-label="Tours">
<div class="carousel-item"><div class="p-24 bg-primary-subtle vstack gap-8"><strong>North Bay marine park</strong><span>Kayaks, a lagoon and a viewpoint, a day by speedboat.</span></div></div>
<div class="carousel-item"><div class="p-24 bg-success-subtle vstack gap-8"><strong>Turtle Cove snorkeling</strong><span>Two bays and a coral garden, lunch on board.</span></div></div>
<div class="carousel-item"><div class="p-24 bg-warning-subtle vstack gap-8"><strong>Sunset cruise</strong><span>A catamaran round the south coast at dusk.</span></div></div>
</div>
<div class="hstack gap-8 justify-content-between">
<button class="btn btn-icon" type="button" command="--prev" aria-controls="carousel-basic-tours" aria-label="Previous"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
<div class="carousel-indicators"></div>
<button class="btn btn-icon" type="button" command="--next" aria-controls="carousel-basic-tours" aria-label="Next"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
</div>
</div>Several at a time
Three custom properties on the carousel set the row, in the module's own stylesheet: --redz-carousel-items slides in view, --redz-carousel-gap between them and --redz-carousel-peek of the next one at the edges. carousel-center snaps a slide to the middle, carousel-auto lets each slide keep its own width.
.tour-reviews { --redz-carousel-items: 2; --redz-carousel-gap: 12px; --redz-carousel-peek: 24px; }
<div class="carousel" id="carousel-several-reviews" role="region" aria-label="Reviews">
<div class="carousel-inner" tabindex="0" aria-label="Reviews">
<div class="carousel-item"><div class="card card-body h-100">“The best day of our trip.” - Anna</div></div>
<div class="carousel-item"><div class="card card-body h-100">“Kind guides and a clean boat.” - Mark</div></div>
<div class="carousel-item"><div class="card card-body h-100">“The lagoon is worth the climb.” - Somchai</div></div>
<div class="carousel-item"><div class="card card-body h-100">“Lunch was a surprise, a good one.” - Leila</div></div>
</div>
<div class="hstack gap-8 justify-content-end">
<button class="btn btn-icon btn-sm" type="button" command="--prev" aria-controls="carousel-several-reviews" aria-label="Previous"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
<button class="btn btn-icon btn-sm" type="button" command="--next" aria-controls="carousel-several-reviews" aria-label="Next"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
</div>
</div>One at a time, fading, playing
carousel-fade stacks the slides and shows the current one. carousel-overlay lays its buttons over the slides. A button with command="--play" plays the slides on every 5 seconds while it is aria-pressed="true" - written so, it plays from the start - and pauses while the pointer rests on the carousel or the focus is in it; it never plays for a person who asks for less motion.
<div class="carousel carousel-fade" id="carousel-fade-island" role="region" aria-label="The island">
<div class="carousel-inner" tabindex="0" aria-label="The island">
<div class="carousel-item"><div class="p-32 bg-info-subtle text-center"><strong>Beaches</strong></div></div>
<div class="carousel-item"><div class="p-32 bg-success-subtle text-center"><strong>Waterfalls</strong></div></div>
<div class="carousel-item"><div class="p-32 bg-warning-subtle text-center"><strong>Temples</strong></div></div>
</div>
<div class="carousel-overlay">
<button class="btn btn-icon btn-sm" type="button" command="--prev" aria-controls="carousel-fade-island" aria-label="Previous"><i class="bi bi-chevron-left" aria-hidden="true"></i></button>
<button class="btn btn-icon btn-sm" type="button" command="--next" aria-controls="carousel-fade-island" aria-label="Next"><i class="bi bi-chevron-right" aria-hidden="true"></i></button>
</div>
<div class="hstack gap-8 justify-content-center">
<button class="btn btn-icon btn-sm btn-text" type="button" command="--play" aria-controls="carousel-fade-island" aria-pressed="false" aria-label="Play the slides"><i class="bi bi-play-fill" aria-hidden="true"></i><i class="bi bi-pause-fill" aria-hidden="true"></i></button>
<div class="carousel-indicators"></div>
</div>
</div>Keyboard and accessibility
- The carousel is a named region and each slide a group named by the page's text
slide- “2 of 5”; an indicator button says the same, the current onearia-current="true". - The row takes the focus (
tabindex="0"and a name): the arrow keys scroll it, a slide at a time in a fading carousel. - A slide a person moves to by a button is told politely in a status of the carousel; nothing is told while it plays.
- The play button is a toggle button, pressed while it plays; the pointer resting on the carousel, the focus inside it and a hidden page pause it (WCAG 2.2.2).
- The buttons have no
commandfor, which WebKit would announce as collapsed; their icons mirror right to left. An indicator is a 24 px target round its dot.
Tokens
From css/components/carousel.css. It declares --redz-carousel-items, --redz-carousel-gap, --redz-carousel-peek on its root, never on rows or items.
It reads the theme's --redz-space-12, --redz-radius-12, --redz-space-8, --redz-border-strong, --redz-text, --redz-ease.
From Bootstrap 5.3
| Bootstrap 5.3 | REDZ.UI | Change | Note |
|---|---|---|---|
carousel-caption | - | review | the slide's own text, laid out with utilities |
carousel-control-next | - | review | a btn btn-icon with command="--next" and aria-controls naming the carousel |
carousel-control-next-icon | bi bi-chevron-right | rename | mirrored right to left |
carousel-control-prev | - | review | a btn btn-icon with command="--prev" and aria-controls naming the carousel |
carousel-control-prev-icon | bi bi-chevron-left | rename | mirrored right to left |
carousel-dark | - | review | data-theme="dark" on the carousel |
carousel-item-end | - | remove | the browser scrolls the row |
carousel-item-next | - | remove | the browser scrolls the row |
carousel-item-prev | - | remove | the browser scrolls the row |
carousel-item-start | - | remove | the browser scrolls the row |
data-bs-slide="prev" | command="--prev" aria-controls="ID" | trigger | the id of the carousel, without commandfor |
data-bs-slide="next" | command="--next" aria-controls="ID" | trigger | the id of the carousel, without commandfor |
data-bs-slide-to | - | remove | an empty carousel-indicators makes its buttons |
data-bs-ride | command="--play" aria-pressed="true" | review | a play button pressed from the start, which a person who asks for less motion never sees play |
data-bs-interval | - | remove | slides turn every 5 seconds |
bootstrap.Carousel | the --prev and --next buttons | review | or scrollBy on carousel-inner; the row scrolls by the browser |