Carousel
Touch-friendly slider with multiple variants — slide, card/3D, fade, and free-scroll. Built on Embla Carousel with autoplay, loop, RTL, and vertical orientation support.
Showcase
Usage
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
CarouselDots,
} from "@tessinaui/ui/carousel";
<Carousel size="full" loop autoplay={4000}>
<CarouselContent>
<CarouselItem>Slide 1</CarouselItem>
<CarouselItem>Slide 2</CarouselItem>
<CarouselItem>Slide 3</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
<CarouselDots />
</Carousel>API
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "card" | "fade" | "scroll" | "default" | Animation style — card scales adjacent slides, fade crossfades, scroll is native overflow scroll |
size | "sm" | "md" | "lg" | "full" | "auto" | "full" | Default item width — sm=200px, md=300px, lg=420px, full=viewport, auto=content |
rounded | "none" | "sm" | "md" | "lg" | "xl" | "full" | "none" | Corner radius applied to inner card wrapper (variant=card) |
orientation | "horizontal" | "vertical" | "horizontal" | Scroll axis |
gap | "none" | "sm" | "md" | "lg" | "xl" | "md" | Spacing between items (0/8/16/24/32px) |
align | "start" | "center" | "end" | "start" | Where snap points align within the viewport |
loop | boolean | false | Infinite loop |
dragFree | boolean | false | Free momentum scrolling — no snapping |
autoplay | boolean | number | false | Auto-advance — true = 3000ms, or pass a number for custom delay |
dir | "ltr" | "rtl" | "ltr" | Text direction — RTL mirrors scroll |
opts | EmblaOptionsType | — | Pass-through options to underlying Embla instance |
setApi | (api: CarouselApi) => void | — | Receive the Embla API instance for imperative control |
CarouselItem
| Prop | Type | Default | Description |
|---|---|---|---|
size | CarouselSize | inherited | Override the carousel's size for this item |
CarouselPrevious / CarouselNext
| Prop | Type | Default | Description |
|---|---|---|---|
navVariant | "default" | "ghost" | "solid" | "default" | Button visual — default is bordered white, ghost is translucent, solid is primary fill |
navSize | "sm" | "md" | "lg" | "md" | Button size (32/40/48px square) |
position | "overlay" | "inline" | "overlay" | overlay floats over the viewport; inline flows in normal layout |
CarouselDots
| Prop | Type | Default | Description |
|---|---|---|---|
dotVariant | "circle" | "pill" | "line" | "circle" | Indicator shape |
useCarouselApi
const { setApi, current, count } = useCarouselApi();
<Carousel setApi={setApi} ... />
<p>{current + 1} / {count}</p>Variants
default
Standard slide carousel — instant snap with smooth easing.
<Carousel size="full">...</Carousel>card
Center-focused carousel where adjacent slides scale down and fade. Modeled after the HeroUI carousel pattern. Pair with align="center" and size="lg" for the classic stacked-card effect.
<Carousel variant="card" size="lg" align="center" loop gap="lg">
<CarouselContent>...</CarouselContent>
</Carousel>fade
Opacity crossfade between slides — best for full-width testimonials, hero rotations, or anywhere a side-scroll animation would feel jarring.
<Carousel variant="fade" size="full" loop autoplay={5000}>
<CarouselContent>...</CarouselContent>
</Carousel>scroll
Native overflow scroll with momentum — no snap, no arrows, just drag. The Satispay / Afterpay "explore brands" pattern. Combine with dragFree for the smoothest feel.
<Carousel variant="scroll" size="sm" gap="md" dragFree>
<CarouselContent>...</CarouselContent>
</Carousel>Autoplay
Pass a boolean to use the default 3000ms interval, or a number for custom timing. Autoplay pauses on user interaction and on hover.
<Carousel autoplay loop>...</Carousel>
<Carousel autoplay={6000} loop>...</Carousel>Vertical orientation
<Carousel orientation="vertical" className="h-64">
<CarouselContent className="h-full">
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>RTL
The carousel's scroll direction reverses and the prev/next icons flip naturally.
<Carousel dir="rtl" loop>...</Carousel>Skeleton
Render CarouselSkeleton while content loads.
<CarouselSkeleton count={3} size="md" rounded="lg" aspectRatio="4/3" />Accessibility
- Root has
role="region"andaria-roledescription="carousel" - Each item has
role="group"andaria-roledescription="slide" - Prev / Next buttons have
aria-label="Previous slide"/"Next slide" - Dots have
aria-label="Go to slide N"andaria-currenton the active dot - Touch / swipe / keyboard arrow navigation handled by Embla
Card
A flexible surface container for grouping related content. Four variants (elevated/outlined/filled/ghost), five intents, six rounded options, five sizes, horizontal layout, interactive states, and LTR/RTL support.
Divider
A thin line that separates content. Horizontal or vertical, solid / dashed / dotted, five thickness sizes, tonal colours, optional inline label, and RTL support.