Overview
Slide 1
Swipeable carousel content
Carousel displays content in a swipeable, scrollable strip. Built on Embla Carousel, it supports touch/mouse drag, keyboard navigation, autoplay, and loop modes.
Installation
npm install @araf-ds/core embla-carousel-react
Usage
import {
Carousel ,
CarouselContent ,
CarouselItem ,
CarouselNext ,
CarouselPrevious ,
} from "@araf-ds/core"
export default function Example () {
return (
< Carousel className = "w-full max-w-sm" >
< CarouselContent >
{ Array . from ({ length: 5 }). map (( _ , i ) => (
< CarouselItem key = { i } >
< Card >
< CardContent className = "flex items-center justify-center p-6" >
< span className = "text-4xl font-semibold" > { i + 1 } </ span >
</ CardContent >
</ Card >
</ CarouselItem >
)) }
</ CarouselContent >
< CarouselPrevious />
< CarouselNext />
</ Carousel >
)
}
Variants
Multiple Items Visible
< Carousel opts = { { align: "start" } } className = "w-full" >
< CarouselContent className = "-ml-2" >
{ products . map ( product => (
< CarouselItem key = { product . id } className = "pl-2 md:basis-1/2 lg:basis-1/3" >
< ProductCard product = { product } />
</ CarouselItem >
)) }
</ CarouselContent >
< CarouselPrevious />
< CarouselNext />
</ Carousel >
With Dots Indicator
const [ api , setApi ] = useState < CarouselApi >()
const [ current , setCurrent ] = useState ( 0 )
const [ count , setCount ] = useState ( 0 )
useEffect (() => {
if ( ! api ) return
setCount ( api . scrollSnapList (). length )
setCurrent ( api . selectedScrollSnap ())
api . on ( "select" , () => setCurrent ( api . selectedScrollSnap ()))
}, [ api ])
< div >
< Carousel setApi = { setApi } >
< CarouselContent >
{ slides . map (( slide , i ) => (
< CarouselItem key = { i } > { slide } </ CarouselItem >
)) }
</ CarouselContent >
</ Carousel >
< div className = "flex justify-center gap-1.5 mt-3" >
{ Array . from ({ length: count }). map (( _ , i ) => (
< button
key = { i }
onClick = { () => api ?. scrollTo ( i ) }
className = { cn (
"h-2 rounded-full transition-all" ,
i === current ? "w-5 bg-primary" : "w-2 bg-muted"
) }
/>
)) }
</ div >
</ div >
Autoplay
import Autoplay from "embla-carousel-autoplay"
< Carousel
plugins = { [ Autoplay ({ delay: 3000 , stopOnInteraction: true })] }
opts = { { loop: true } }
>
< CarouselContent >
{ slides . map (( slide , i ) => (
< CarouselItem key = { i } > { slide } </ CarouselItem >
)) }
</ CarouselContent >
< CarouselPrevious />
< CarouselNext />
</ Carousel >
API Reference
Carousel
Embla Carousel options. Common keys: loop, align, dragFree, slidesToScroll.
Embla plugins array (e.g. Autoplay, AutoScroll).
orientation
string
default: "horizontal"
Scroll direction. Values: horizontal · vertical
setApi
(api: CarouselApi) => void
Callback to access the Embla carousel API for programmatic control.
CarouselItem
Use Tailwind basis-* classes to control item width (e.g. basis-1/2 for 2 visible items).
Accessibility
Carousel uses role="region" with aria-label="carousel"
CarouselPrevious and CarouselNext have aria-label="Previous slide" / "Next slide"
Keyboard: Arrow Left/Right navigate slides when carousel is focused
CarouselItem has role="group" with aria-label="Slide N of M"
Autoplay carousels must pause on focus and hover per WCAG 2.1
Do’s & Don’ts
Do
Show navigation arrows and dots for discoverability
Use opts={{ loop: true }} for image galleries and testimonials
Use stopOnInteraction: true with Autoplay so users can read without pressure
Set basis-1/2 or basis-1/3 for product card carousels
Don't
Don’t autoplay without giving users a way to pause
Don’t use Carousel for critical information — users may not see hidden slides
Don’t use for navigation — use Tabs or a sidebar instead
Don’t make slides too tall — users expect to see the next slide peeking in