Navigation
Carousel
A labelled strip of slides shown one at a time with previous and next controls.
When to use it: Use it to feature a few highlights in limited space; use Tabs when people pick sections by name.
Example
import {
Carousel,
CarouselAutoplayToggle,
CarouselNext,
CarouselPrevious,
CarouselSlide,
CarouselViewport,
} from "@comp0/react";
import { ChevronLeftIcon, ChevronRightIcon, PlayPauseIcon } from "@heroicons/react/16/solid";
export function Example() {
return (
<Carousel
aria-label="Featured recipes"
loop
autoplay={5000}
className="w-full max-w-sm rounded border border-zinc-950/10 p-3 dark:border-white/10"
>
<div className="mb-2 flex items-center gap-1">
<CarouselAutoplayToggle className="rounded px-3 py-2.5 text-zinc-700 hover:bg-zinc-100 data-stopped:text-teal-700 sm:py-2 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:data-stopped:text-teal-300">
<PlayPauseIcon className="size-4" aria-hidden="true" />
</CarouselAutoplayToggle>
<CarouselPrevious className="ml-auto rounded px-3 py-2.5 text-zinc-700 hover:bg-zinc-100 disabled:opacity-40 sm:py-2 dark:text-zinc-300 dark:hover:bg-zinc-800">
<ChevronLeftIcon className="size-4" aria-hidden="true" />
</CarouselPrevious>
<CarouselNext className="rounded px-3 py-2.5 text-zinc-700 hover:bg-zinc-100 disabled:opacity-40 sm:py-2 dark:text-zinc-300 dark:hover:bg-zinc-800">
<ChevronRightIcon className="size-4" aria-hidden="true" />
</CarouselNext>
</div>
<CarouselViewport className="overflow-hidden rounded">
<div
className="flex transition-transform duration-300"
style={{ transform: "translateX(calc(var(--comp0-carousel-index) * -100%))" }}
>
<CarouselSlide className="flex h-32 w-full shrink-0 items-center justify-center bg-teal-50 text-base font-medium text-teal-900 sm:text-sm dark:bg-teal-950 dark:text-teal-100">
Roasted tomato soup
</CarouselSlide>
<CarouselSlide className="flex h-32 w-full shrink-0 items-center justify-center bg-zinc-100 text-base font-medium text-zinc-900 sm:text-sm dark:bg-zinc-800 dark:text-zinc-100">
Charred corn salad
</CarouselSlide>
<CarouselSlide className="flex h-32 w-full shrink-0 items-center justify-center bg-teal-100 text-base font-medium text-teal-950 sm:text-sm dark:bg-teal-900 dark:text-teal-50">
Smoky bean stew
</CarouselSlide>
</div>
</CarouselViewport>
</Carousel>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
Carousel
Section with the carousel roledescription; owns the slide index and the autoplay pause bookkeeping. Owns a DOM element.
CarouselViewport
Wraps the slides; exposes --comp0-carousel-index for transform tracks and flips aria-live between off (rotating) and polite (paused or stopped). Owns a DOM element.
CarouselSlide
One slide, announced as a group with the slide roledescription. Owns a DOM element.
CarouselPreviousOptional
Native button that shows the previous slide; disabled on the first slide unless loop. Owns a DOM element.
CarouselNextOptional
Native button that shows the next slide; disabled on the last slide unless loop. Owns a DOM element.
CarouselAutoplayToggleOptional
Native button that stops and restarts auto-rotation; renders nothing without autoplay. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap everything in Carousel with an aria-label, and put the slides in CarouselViewport.
- 2
Add the supporting parts
Add CarouselPrevious and CarouselNext, plus CarouselAutoplayToggle when autoplay is set.
- 3
Make the behavior clear
Pass autoplay in milliseconds only with the toggle present; rotation pauses on hover or focus and the toggle stops it for good.
Exampletsx <Carousel aria-label="Featured recipes" autoplay={5000}><CarouselAutoplayToggle /><CarouselPrevious /><CarouselNext /><CarouselViewport><CarouselSlide>Soup</CarouselSlide></CarouselViewport></Carousel>
Keyboard
- ⇥
- Moves through the rotation, previous, and next controls.
- ↵
- Presses the focused carousel control.
- Space
- Presses the focused carousel control.
Forms and accessibility
A carousel does not create form values.
Accessibility checklist
- Give the carousel an aria-label naming the content, such as Featured recipes.
- Keep CarouselAutoplayToggle first in the tab order whenever autoplay is set; hover and focus pauses are temporary, the toggle is the guaranteed stop (WCAG 2.2.2).
- Slide changes announce politely only while rotation is paused or stopped; name slides after their content with aria-label when N of M is too vague.
API reference
import { Carousel, CarouselAutoplayToggle, CarouselNext, CarouselPrevious, CarouselSlide, CarouselViewport } from "@comp0/react";Carousel
DOM elementSection with the carousel roledescription; owns the slide index and the autoplay pause bookkeeping.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the carousel for assistive technology; required. |
index / defaultIndex | number | Controlled or initial slide index. |
onIndexChange | (index: number) => void | Receives the next slide index. |
loop | boolean | Previous on the first slide and Next on the last wrap around. |
autoplay | number | Milliseconds between automatic advances; rotation pauses on hover or focus and stops via the toggle. |
CarouselViewport
DOM elementWraps the slides; exposes --comp0-carousel-index for transform tracks and flips aria-live between off (rotating) and polite (paused or stopped).
| Prop | Type | Description |
|---|---|---|
style | CSSProperties | Merged under the exposed --comp0-carousel-index variable. |
CarouselSlide
DOM elementOne slide, announced as a group with the slide roledescription.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the slide after its content; defaults to its computed "N of M" position. |
CarouselPrevious
OptionalDOM elementNative button that shows the previous slide; disabled on the first slide unless loop.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Accessible name; defaults to "Previous slide". |
CarouselNext
OptionalDOM elementNative button that shows the next slide; disabled on the last slide unless loop.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Accessible name; defaults to "Next slide". |
CarouselAutoplayToggle
OptionalDOM elementNative button that stops and restarts auto-rotation; renders nothing without autoplay.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Defaults to "Pause carousel" while rotating and "Play carousel" once stopped. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
[data-current]on CarouselSlide
- This slide is showing.
[data-rotating]on Carousel
- Auto-rotation is advancing.
[data-stopped]on Carousel, CarouselAutoplayToggle
- The toggle stopped auto-rotation.
:disabledon CarouselPrevious, CarouselNext
- The bound was reached without loop.