# Slideshow

> Photos that crossfade one to the next on their own, each with a caption and a bar that fills while it shows.

- Name: `slideshow`
- Status: beta
- Categories: media, display
- File: the install writes `slideshow.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/button`, `@arbokui/dither`, `@arbokui/motion-timing`. The install adds them.
- Gallery: https://arbokui.com/components/slideshow
- Registry item: https://arbokui.com/r/slideshow.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/slideshow
   ```

## Use it for

- A few photos that show what something looks like, like the walks, places, or rooms behind a feature, in a hero or beside a list.
- Three to six photos. Give each an alt that says what is in it, and a short title and detail for the caption.
- Pass picture for your own element instead of src, like a next/image.
- Use photos that fill the frame, with no clear parts. A new photo fades in over the one before, which shows through anything clear until the fade ends.
- Set a width. It keeps 4:5. Change the shape with className, like "aspect-video".
- Keep something beside it in step with onIndexChange.
- Change the time each photo shows with className="\[--slideshow-duration:9s\]". The default is 6s.
- Put a small Badge on the top corner with badge.

## Avoid it for

- Photos people must compare or study. Use Media Grid, and Lightbox to view one large.
- Words or steps people must read in order. Nobody reads a slide that moves on by itself.
- Two slideshows on one screen. One moving thing is enough.

## Props

- `slides`: `Slide[]`. Required. The photos, in order. It loops back to the first.
- `Slide`: `{ src?: string; picture?: ReactNode; alt: string; title?: string; detail?: string; id?: string | number }`. Type. src is the photo. picture draws your own element in its place. alt says what is in it. title and detail make the caption.
- `badge`: `ReactNode`. Something small on the top corner, like a Badge.
- `onIndexChange`: `(index: number) => void`. Called with the new photo's index each time it changes.
- `texture`: `"flat" | "dither" | "gradient" | "grain"`. dither draws the photos as dots, gradient fades them from the top, and grain speckles them. Hover fills gradient and grain in. Left out, it follows the Texture around it.
- `aria-label`: `string`. Default `"Slideshow"`. Names the slideshow for screen readers.
- `className`: `string`. Extra classes on the root. Set the width, the shape, or --slideshow-duration here. Later classes win.

## Accessibility

- The root is a carousel with a name. Each photo is a slide named like "2 of 4", and only the one showing reaches screen readers and Tab.
- It holds still while the pointer is over it or keyboard focus is inside it.
- A pause button stops it for good, on touch screens too. It says it is pressed with aria-pressed. While paused, a new caption is read aloud.
- Each bar is a button, like "Slide 3 of 4", so every photo can be reached without waiting. The one showing has aria-current.
- With prefers-reduced-motion, or data-motion="off", it stands still on the first photo, and the bars still change it. The pause button goes away then.
- The caption and controls sit on a dark veil in both themes, so they stay clear on light photos.

## Examples

### Photos with captions

```tsx
<Slideshow
  className="max-w-sm"
  aria-label="Recent photos"
  slides={[
    { src: "/photos/columns.jpg", alt: "Stone columns at dusk", title: "Columns at dusk", detail: "East gallery · Yesterday" },
    { src: "/photos/lake.jpg", alt: "A still lake at sunrise", title: "Lake at first light", detail: "North shore · 3h ago" },
    { src: "/photos/arcade.jpg", alt: "Three arches in a stone wall", title: "Arcade at noon", detail: "Main square · Today" },
  ]}
/>
```

### A list beside it that follows the photo, with a badge and slower slides

```tsx
const [index, setIndex] = useState(0);

<div className="flex gap-4">
  <Slideshow
    slides={slides}
    badge={<Badge emphasis="soft" size="sm">New</Badge>}
    onIndexChange={setIndex}
    className="max-w-xs [--slideshow-duration:9s]"
  />
  <ul>
    {slides.map((slide, i) => (
      <li key={slide.src} aria-current={i === index ? "true" : undefined}>{slide.title}</li>
    ))}
  </ul>
</div>
```

### Photos drawn as dots

```tsx
<Slideshow slides={slides} texture="dither" className="max-w-sm" />
```

### Grain photos that clear on hover

```tsx
<Slideshow slides={slides} texture="grain" className="max-w-sm" />
```
