carousel-wc
Scroll carousel with prev/next buttons, dot indicators, autoplay, keyboard navigation, and ARIA.
Overview
A scroll-snap carousel with full progressive enhancement. Without JavaScript it renders as a simple scrollable row. With JS it adds prev/next buttons, dot indicators, autoplay, looping, keyboard navigation, and ARIA roles.
Usage
Wrap slide elements inside <carousel-wc>. Each direct child becomes a slide.
Autoplay with Looping
Add data-autoplay and data-loop to cycle slides automatically. Autoplay pauses on hover and focus, and is disabled entirely when prefers-reduced-motion is active.
Multi-item Carousel
Set data-layout-item-width="auto" to show multiple items at once with a gap.
No Indicators
Set data-indicators="false" to hide dot indicators.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
data-autoplay | boolean | — | Enable autoplay |
data-autoplay-delay | number (ms) | 5000 | Autoplay interval |
data-loop | boolean | — | Wrap around at ends |
data-indicators | "true", "false" | "true" | Show dot indicators |
data-item-width | full, auto, CSS length | full | Slide width |
data-gap | xs, s, m, l, xl | — | Gap between slides |
data-start | number | 0 | Initial slide index |
data-persist | string key | — | localStorage key for slide persistence |
data-transition | fade, slide, scale | — | Enable View Transition animations (switches to stacked-grid layout) |
Events
| Event | Detail | Description |
|---|---|---|
carousel-change | { index, slide } | Fired when the active slide changes. |
carousel-play | { } | Fired when autoplay starts. |
carousel-pause | { } | Fired when autoplay pauses. |
JavaScript API
| Property / Method | Type | Description |
|---|---|---|
element.currentIndex | number | Current slide index (read-only) |
element.slideCount | number | Total number of slides (read-only) |
element.playing | boolean | Whether autoplay is active (read-only) |
element.next() | method | Go to next slide |
element.prev() | method | Go to previous slide |
element.goTo(index) | method | Jump to specific slide |
element.play() | method | Start autoplay |
element.pause() | method | Pause autoplay |
element.reset() | method | Return to initial slide, clear persistence |
Keyboard Navigation
Focus the carousel track (Tab into it), then use arrow keys:
| Key | Action |
|---|---|
| ArrowLeft | Previous slide |
| ArrowRight | Next slide |
| Home | First slide |
| End | Last slide |
Accessibility
ARIA Roles
The component uses role="region" with aria-roledescription="carousel". Each slide has role="group" with aria-roledescription="slide" and a label like "1 of 4". Dot indicators use role="tablist" and role="tab".
Live Region
A visually hidden aria-live="polite" region announces slide changes to screen readers.
Reduced Motion
When prefers-reduced-motion is active, autoplay is completely disabled and scroll behavior uses instant transitions.
Styling
Override the button and dot styles using the .carousel-prev, .carousel-next, and .carousel-dot classes.
Progressive Enhancement
Without JavaScript, <carousel-wc> renders as a horizontal scrollable row using the :not(:defined) fallback. Once JS registers the component, controls and indicators appear.
View Transitions
Add data-transition to switch from scroll-snap to View Transition animations. When active, the carousel uses a stacked-grid layout and animates between slides using the View Transitions API.
| Value | Effect |
|---|---|
fade (default) | Crossfade between slides |
slide | Directional slide — forward when advancing, backward when retreating |
scale | Scale down old slide, scale up new slide |
Dual-Mode Behavior
Without data-transition, the carousel uses scroll-snap with IntersectionObserver — slides are arranged in a horizontal row and the user scrolls between them. With data-transition, the carousel switches to a stacked CSS Grid layout where slides occupy the same grid cell and are shown/hidden with View Transition animations.
Directional slide transitions
Forward and backward animations
Keyboard navigation works too
Related
<layout-reel>— CSS-only horizontal scroll with snap<tabs-wc>— Tabbed content switching<splitter-wc>— Resizable panel splitter