> ## Documentation Index
> Fetch the complete documentation index at: https://araf.badr.co.id/llms.txt
> Use this file to discover all available pages before exploring further.

# Carousel

> A swipeable slideshow component with navigation controls, autoplay, and loop support — built on Embla Carousel.

## Overview

<div style={{padding:"28px 24px",background:"#f9fafb",borderRadius:"12px",border:"1px solid #e5e7eb",display:"flex",justifyContent:"center"}}>
  <div style={{width:"100%",maxWidth:"360px",fontFamily:"Inter,sans-serif"}}>
    <div style={{position:"relative",overflow:"hidden",borderRadius:"10px"}}>
      <div style={{display:"flex",gap:"0"}}>
        <div style={{minWidth:"100%",background:"linear-gradient(135deg,#EFF8FF,#CDE4F5)",borderRadius:"10px",padding:"40px 20px",textAlign:"center"}}>
          <div style={{fontSize:"16px",fontWeight:600,color:"#0479CE",marginBottom:"4px"}}>Slide 1</div>
          <div style={{fontSize:"13px",color:"#667085"}}>Swipeable carousel content</div>
        </div>
      </div>

      <button style={{position:"absolute",left:"8px",top:"50%",transform:"translateY(-50%)",width:"32px",height:"32px",borderRadius:"50%",background:"#fff",border:"1px solid #E5E7EB",boxShadow:"0 2px 8px rgba(0,0,0,0.1)",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer"}}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#344054" strokeWidth="2">
          <polyline points="15 18 9 12 15 6" />
        </svg>
      </button>

      <button style={{position:"absolute",right:"8px",top:"50%",transform:"translateY(-50%)",width:"32px",height:"32px",borderRadius:"50%",background:"#fff",border:"1px solid #E5E7EB",boxShadow:"0 2px 8px rgba(0,0,0,0.1)",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer"}}>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#344054" strokeWidth="2">
          <polyline points="9 18 15 12 9 6" />
        </svg>
      </button>
    </div>

    <div style={{display:"flex",justifyContent:"center",gap:"6px",marginTop:"12px"}}>
      {[0,1,2].map(i => (
                  <div key={i} style={{width:i===0?"20px":"8px",height:"8px",borderRadius:"4px",background:i===0?"#0479CE":"#E5E7EB",transition:"width 0.2s"}}></div>
                ))}
    </div>
  </div>
</div>

**Carousel** displays content in a swipeable, scrollable strip. Built on Embla Carousel, it supports touch/mouse drag, keyboard navigation, autoplay, and loop modes.

***

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @araf-ds/core embla-carousel-react
  ```

  ```bash yarn theme={null}
  yarn add @araf-ds/core embla-carousel-react
  ```

  ```bash pnpm theme={null}
  pnpm add @araf-ds/core embla-carousel-react
  ```
</CodeGroup>

***

## Usage

```tsx theme={null}
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

```tsx theme={null}
<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

```tsx theme={null}
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

```tsx theme={null}
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

<ParamField path="opts" type="EmblaOptionsType">
  Embla Carousel options. Common keys: `loop`, `align`, `dragFree`, `slidesToScroll`.
</ParamField>

<ParamField path="plugins" type="EmblaPluginType[]">
  Embla plugins array (e.g. `Autoplay`, `AutoScroll`).
</ParamField>

<ParamField path="orientation" type="string" default="horizontal">
  Scroll direction. Values: `horizontal` · `vertical`
</ParamField>

<ParamField path="setApi" type="(api: CarouselApi) => void">
  Callback to access the Embla carousel API for programmatic control.
</ParamField>

### CarouselItem

<ParamField path="className" type="string">
  Use Tailwind `basis-*` classes to control item width (e.g. `basis-1/2` for 2 visible items).
</ParamField>

***

## 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

<CardGroup cols={2}>
  <Card title="Do" icon="check" iconType="solid" color="#16A34A">
    * 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
  </Card>

  <Card title="Don't" icon="xmark" iconType="solid" color="#DC2626">
    * 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
  </Card>
</CardGroup>
