Skip to content
Protocore Design Systemv1.6.9

/// Media

Carousel

Accessible slide carousel — a scroll-snap track with mono prev/next buttons, square dot indicators, arrow-key navigation and optional autoplay.

import { Carousel } from "@protocore/pds";
View as Markdown

Basics

Wrap each panel in a Carousel.Slide. The region gets aria-roledescription="carousel"; prev/next buttons and a dot per slide are drawn for you. Give the region a name via aria-label.

Looping autoplay

Set autoPlay with loop to rotate continuously. Autoplay pauses while the pointer is over the carousel or focus is inside it, and is disabled entirely under prefers-reduced-motion.

When to use it

Use Carousel for a small, browsable set of peer panels where showing one at a time is intentional — a hero rotation, a testimonial reel, a screenshot gallery. It degrades gracefully: the track is a native CSS scroll-snap container, so slides remain swipeable even if JavaScript never loads.

  • Carrying navigation between views of one entity? That's Tabs, not a carousel.
  • Showing a continuous logo strip? Use LogoMarquee.
  • Hiding essential content behind autoplay is an anti-pattern — keep autoplay for ambient, non-critical panels and never bury a primary CTA on slide 3.

Usage

Do

  • Give the carousel an accessible name with `aria-label`.
  • Keep autoplay for ambient content; it already pauses on hover/focus and respects reduced-motion.
  • Use `loop` when the set is a true rotation with no natural start or end.
  • Keep each slide's content self-contained so any slide reads on its own.

Don't

  • Don't autoplay content users must read or act on — they can't keep pace.
  • Don't put your only copy of a primary action inside a non-first slide.
  • Don't use a carousel where a simple grid or stack would show everything at once.
  • Don't remove the dots and arrows — they're the visible, keyboard-reachable controls.

Accessibility

KeysAction
Arrow Left / RightMove to the previous / next slide.
Home / EndJump to the first / last slide.
TabMove into the prev/next buttons and the dot controls.
Enter / SpaceActivate the focused control.
  • The region carries `aria-roledescription="carousel"`; each slide is a `role="group"` labelled "n of m" (override per slide with your own `aria-label`).
  • The slide track is an `aria-live` region — polite while paused, off while autoplaying — so slide changes are announced without fighting a running rotation.
  • Prev/next arrows are inline SVGs (never emoji glyphs); the active dot uses the reserved accent as the current-slide marker.
  • Autoplay honours `prefers-reduced-motion` (no rotation) and pauses on hover and on focus within the carousel.

Carousel props

PropTypeDefaultDescription
aria-labelstringCarouselAccessible name for the carousel region. Default `"Carousel"`.
autoPlaybooleanfalseAdvance automatically. Paused on hover/focus and disabled under reduced-motion. Default `false`.
children *ReactNode`Carousel.Slide` children.
classNamestring
defaultIndexnumber0Initial slide index in uncontrolled mode. Default `0`.
hideControlsbooleanfalseHide the prev/next/dots control row (e.g. for a swipe-only mobile track). Default `false`.
indexnumberActive slide index (controlled).
intervalnumber5000Autoplay dwell time per slide, in ms. Default `5000`.
loopbooleanfalseWrap from last→first (and first→last) instead of stopping at the ends. Default `false`.
onIndexChange((index: number) => void)Fires whenever the active slide changes (both modes).
styleCSSProperties

Related

  • AspectRatioA box that locks its content to a fixed width-to-height ratio.
  • TabsUnderlined tab set — mono UPPERCASE triggers over a hairline rail, the active tab inking its label and growing a 2px underline.
  • LogoMarqueeThe "trusted by" wall — a masked strip of wordmarks on an infinite loop.
  • ProductCardAn illustrated product card — art panel, eyebrow, title, one-line body, a mono tag row, and an outbound footer link, with a [ 0N ] index.