Carousel
A slide deck with swipe, keyboard nav, autoplay, fractional slides-per-view and fade mode.
Slide one
"use client";
import { Carousel, Center, Text, Box } from "astralis-ui";
const slides = [
{ label: "Slide one", bg: "brand-subtle" },
{ label: "Slide two", bg: "teal-subtle" },
{ label: "Slide three", bg: "purple-subtle" },
] as const;
export function CarouselDemo() {
return (
<Box w="full" maxW="md">
<Carousel>
<Carousel.Track>
{slides.map((slide) => (
<Carousel.Slide key={slide.label}>
<Center bg={slide.bg} h="40" rounded="xl">
<Text weight="medium">{slide.label}</Text>
</Center>
</Carousel.Slide>
))}
</Carousel.Track>
<Carousel.Control>
<Carousel.Prev />
<Carousel.Indicators />
<Carousel.Next />
</Carousel.Control>
</Carousel>
</Box>
);
}Import#
import { Carousel } from "astralis-ui";
// Carousel.Track, .Slide, .Control, .Prev, .Next,
// .Indicators, .ProgressText, .AutoPlayTriggerAnatomy#
The compound parts compose freely — put controls above, below, or skip them:
<Carousel loop>
<Carousel.Track>
<Carousel.Slide>…</Carousel.Slide>
</Carousel.Track>
<Carousel.Control>
<Carousel.Prev />
<Carousel.Indicators />
<Carousel.Next />
</Carousel.Control>
</Carousel>Autoplay#
autoPlay advances on an interval and pauses on hover by default;
AutoPlayTrigger gives users a play/pause control and ProgressText a
screen-reader-friendly readout.
Aurora
1 / 0
"use client";
import { Carousel, Center, Text, Box } from "astralis-ui";
export function CarouselAutoplay() {
return (
<Box w="full" maxW="md">
<Carousel autoPlay autoPlayInterval={2500} loop>
<Carousel.Track>
{["Aurora", "Nebula", "Quasar"].map((name) => (
<Carousel.Slide key={name}>
<Center bg="blue-subtle" h="32" rounded="xl">
<Text weight="medium">{name}</Text>
</Center>
</Carousel.Slide>
))}
</Carousel.Track>
<Carousel.Control>
<Carousel.AutoPlayTrigger />
<Carousel.Indicators variant="line" />
<Carousel.ProgressText />
</Carousel.Control>
</Carousel>
</Box>
);
}Multiple slides per view#
Fractional slidesPerView peeks the next slide — the strongest "you can
swipe this" cue there is.
1
2
"use client";
import { Carousel, Center, Text, Box } from "astralis-ui";
export function CarouselMulti() {
return (
<Box w="full" maxW="md">
{/* Fractional slidesPerView peeks the next slide — a strong swipe cue. */}
<Carousel slidesPerView={1.5} slideGap={12} loop>
<Carousel.Track>
{[1, 2, 3, 4, 5].map((n) => (
<Carousel.Slide key={n}>
<Center bg="green-subtle" h="28" rounded="xl">
<Text weight="medium">{n}</Text>
</Center>
</Carousel.Slide>
))}
</Carousel.Track>
<Carousel.Control>
<Carousel.Prev />
<Carousel.Indicators variant="number" />
<Carousel.Next />
</Carousel.Control>
</Carousel>
</Box>
);
}Fade#
animation="fade" cross-fades in place instead of translating the track —
better for hero imagery.
First slide
"use client";
import { Carousel, Center, Text, Box } from "astralis-ui";
export function CarouselFade() {
return (
<Box w="full" maxW="md">
<Carousel animation="fade" speed={500} loop colorScheme="pink">
<Carousel.Track>
{["First", "Second", "Third"].map((label) => (
<Carousel.Slide key={label}>
<Center bg="pink-subtle" h="32" rounded="xl">
<Text weight="medium">{label} slide</Text>
</Center>
</Carousel.Slide>
))}
</Carousel.Track>
<Carousel.Control>
<Carousel.Prev />
<Carousel.Indicators />
<Carousel.Next />
</Carousel.Control>
</Carousel>
</Box>
);
}Props#
| Prop | Type | Default | Description |
|---|---|---|---|
index · defaultIndex · onIndexChange | number · number · (index: number) => void | — · 0 · — | Controlled / initial slide, and the change callback. |
slidesPerView | number | 1 | Visible slides; fractional values (1.5) peek the next slide. |
slidesToScroll | number | 1 | Slides advanced per step. |
slideGap | number | 0 | Pixel gap between slides. |
loop | boolean | false | Wraps from the last slide back to the first. |
autoPlay · autoPlayInterval · pauseOnHover | boolean · number · boolean | false · 3000 · true | Automatic advancing, its cadence, and hover pause. |
animation | "slide" | "fade" | "slide" | Translate the track, or cross-fade in place. |
orientation | "horizontal" | "vertical" | "horizontal" | Slide axis; keyboard arrows follow it. |
speed · easing | number · string | 300 · "ease-out" | Transition duration (ms) and CSS easing. |
swipeable · draggable | boolean | true · false | Touch swipe and mouse drag gestures. |
size | "sm" | "md" | "lg" | "md" | Control and indicator sizing. |
colorScheme | "brand" | "gray" | … (all 11 schemes) | "brand" | Active indicator and focus-ring hue. |
beforeChange · afterChange | (from, to) => void · (current) => void | — | Transition lifecycle hooks. |
Parts#
| Prop | Type | Default | Description |
|---|---|---|---|
Carousel.Track / Carousel.Slide | children | — | The scroller and each slide (role=group with slide labels). |
Carousel.Prev / Carousel.Next | icon?: ReactNode | — | Step buttons; default chevrons follow the orientation. |
Carousel.Indicators | variant?: "dot" | "line" | "number" | "dot" | One indicator per slide, rendered as an accessible tablist. |
Carousel.ProgressText | format?: (index, total) => ReactNode | "1 / 3" | Live-region progress readout. |
Carousel.AutoPlayTrigger | children?: (isPlaying) => ReactNode | — | Play/pause toggle for autoPlay. |
Keyboard#
With focus anywhere inside the carousel:
| Key | Action |
|---|---|
← / → | Previous / next page (horizontal) |
↑ / ↓ | Previous / next page (vertical) |
Accessibility#
- The root is a
regionwitharia-roledescription="carousel"; slides are labeled groups ("Slide 2 of 5"). - Off-screen slides are
aria-hiddenandinert, so focus can't leak into them. - Indicators form a real tablist.