Carousel
Scroll-snap track with arrow buttons, position dots, and optional autoplay. Swipe, wheel, arrows, and keyboard all move the same scroll position.
Component behavior
Carousel uses native scrolling and CSS scroll snapping. Pointer gestures, wheel input, and navigation buttons update the same scroll position. The active index is derived from that position.
- A short browsable collection, such as release notes, onboarding steps, screenshots.
- Horizontal card collections with slide navigation.
ScrollArea: the region needs scrolling without slide controls, indexing, or autoplay.
Tabs: users select views by name.
Stepper: a task has ordered steps and completion state.
Props
Carousel extends HTMLAttributes<HTMLDivElement> (minus onChange).
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | CarouselSlide children. |
perView | number | 1 | Number of visible slides. Fractional values display part of the next slide. |
gap | keyof SpacingScale | string | number | 3 | Space between slides, from the spacing scale. |
index | number | — | Controlled active slide index. Use with onIndexChange. |
defaultIndex | number | 0 | Starting slide when uncontrolled. |
onIndexChange | (index: number) => void | — | Fires when the active slide changes, including from a manual scroll. |
loop | boolean | — | Wraps past the ends instead of stopping. |
autoPlay | number | — | Advance every N milliseconds. Pauses on hover, focus and off-screen. |
arrows | boolean | true | Previous / next buttons over the track. |
dots | boolean | true | Position dots under the track. |
label | string | "Carousel" | Names the region for assistive tech. |
Keyboard
The track is a tab stop. From it, ← and → step by one slide, Home and End jump to the ends, and the arrows and dots are ordinary buttons with their own labels. Each slide is a group labelled "n of m", so a screen reader announces where it is in the set.
Examples
One at a time
Several at a time, with a peek
A fractional perView leaves part of the next slide visible, which signals that the strip continues. This is the same job the edge fade does in a ScrollArea.
Looping
import { Carousel, CarouselSlide } from "@solarius/onset";
<Carousel perView={1} label="Onboarding" onIndexChange={setStep}>
<CarouselSlide><Step one /></CarouselSlide>
<CarouselSlide><Step two /></CarouselSlide>
</Carousel>
// A card strip that peeks at the next item:
<Carousel perView={3.2} gap="2" arrows dots={false}>
{items.map((i) => <CarouselSlide key={i.id}><Card /></CarouselSlide>)}
</Carousel>