A carousel is a strip of slides with one of them in view: a set of testimonials, three plans, a
tour of a screen. It is an interaction rather than a surface, and what it owns is the moving, the
index, the advance, the swipe and the two things a reader uses to leave the slide they are on.

What separates it from `Tabs` is that the content is sequential and passive: nothing here
changes what the page does, so the slides can advance on their own, and the two controls are a
pair of arrows over the frame rather than a row of named sections.

```tsx demo="data-display/carousel-preview"
```

## Usage

```tsx
import { Carousel } from "@asheeui/web";

<Carousel
  items={[
    { id: "testimonial", content: <Testimonial /> },
    { id: "plan", content: <PlanCard /> },
    { id: "statistic", content: <Statistic /> },
  ]}
  defaultIndex={0}
/>
```

`items` is the shape a slide is described in: a `content` node and an optional `id` used as its
key. Children work too, `<Carousel><Slide /><Slide /></Carousel>` becomes the same array, but a
child has no identity of its own, so the position is its key.

`defaultIndex` is where an uncontrolled carousel starts and `index` with `onIndexChange` makes it
controlled: the component reports every move it would make and draws the index it is given, which
is what a screen that syncs the strip with a caption outside it needs. The two are exclusive in
practice, a controlled carousel ignores `defaultIndex`.

`size` is the frame's height rather than a control's density: `sm` is 15rem at its narrowest and
`md`, the registered value, 20rem. `radius` resolves from the application's `defaultRadius` and
falls back to `lg`, and `variant` is `bordered`, a two-pixel frame, or `ghost`, which keeps the
slides and drops the frame.

## Autoplay, pausing and reduced motion

`autoPlay` is registered `false`, so nothing moves unless a screen asks it to. With it on, the
carousel advances every `autoPlayInterval` milliseconds, registered `5000`, and pauses for four
reasons: a pointer resting on the frame, a reader tabbing into it, a drag in progress, and a
document with fewer than two slides. The first two are `pauseOnHover`, registered `true`.

```tsx demo="data-display/carousel-autoplay"
```

**Reduced motion is honoured.** The component reads `prefers-reduced-motion: reduce` through
`usePrefersReducedMotion` and stops autoplay altogether rather than merely slowing it, and it
drops the transition so a change of slide lands instantly instead of travelling. Both are decided
by the document, not by the consumer: a screen that wants a carousel that never advances with
`autoPlay` off gets the same result, and a screen that wants the change of slide to land instantly
for everyone passes `disableAnimation` to the strip.

With `loop` false, autoplay stops at the last slide instead of wrapping, and each arrow disables
at its end, the registered `loop` is `true`, which is what a strip of testimonials wants and
what a set of numbered steps does not.

## Controls and indicators

The registered frame shows both: two arrows drawn over the slides and a row of dots under them.
`showControls` and `showIndicators` (both registered `true`) remove either, and the pair is only
drawn at all when there is more than one slide, a single slide gets no affordance that would do
nothing.

```tsx demo="data-display/carousel-controls"
```

`renderPrevControl` and `renderNextControl` replace a control's button with a consumer's,
receiving the handler and the disabled state. They replace the button and not its placement: the
framework draws its own arrows absolutely over the track, so a custom control that should sit over
a slide supplies its own positioning, and one that is left in the flow renders inside the frame,
under the track. `controlClassName`, `indicatorClassName` and `itemClassName` are the escape
hatches for the three parts.

## Props

```props Carousel
```

The registered values are `size` `md`, `autoPlay` `false`, `autoPlayInterval` `5000`, `loop`
`true`, `showControls` `true`, `showIndicators` `true` and `pauseOnHover` `true`, behaviour rather
than dressing, so a product states its motion once under `components.carousel`.

`disableAnimation` is a cascade value like the rest, so `components.carousel.disableAnimation`
drops the slide transition for every carousel in the application; the prop wins over the section
when a single strip wants it anyway.

`variant` and `radius` are deliberately **not** registered: `radius` resolves from the
application's `defaultRadius`, falling back to `lg`, and `variant` resolves from the
application's `defaultVariant` onto the two frames a carousel draws. A carousel is only ever
`bordered` or `ghost`, so the shared vocabulary is mapped onto those two rather than looked up and
missed: `solid`, `faded` and `underlined` all land on the `bordered` frame, and `ghost` stays
`ghost`.

`items`, `defaultIndex`, `index`, `onIndexChange`, the two render functions and the three
class-name overrides are instance-only: which slides there are, where the strip starts and what
replaces an arrow are statements about one strip.

## Accessibility

- The two arrows are ordinary `<button type="button">` elements named `Previous slide` and
  `Next slide`, so they are reached by <kbd>Tab</kbd> and pressed with <kbd>Enter</kbd> or
  <kbd>Space</kbd>; each disables at its end when the carousel does not loop.
- Each indicator is a button named for its destination, `Go to slide 1`, and the current slide's
  dot carries `aria-current="true"`, so which slide is showing is announced as well as drawn.
- Only the active slide is in the tab order and in the accessibility tree: the others are
  `inert` (and `aria-hidden` where `inert` is unsupported), so a reader tabs through the strip's
  own controls rather than through the content of slides they cannot see. The sighted transition
  is unchanged, the slides are still translated, not unmounted.
- Autoplay stops when the document asks for reduced motion and while a reader is hovering or
  focused, so a reader who tabbed in is not moved out from under themselves. There is no pause
  button of the component's own: the way to stop the motion entirely is to leave `autoPlay` off.

## Native

`@asheeui/native` renders a `Carousel` with the same props and a platform scroll view underneath,
so the swipe is the platform's paging rather than a pointer drag. The platform has no pointer
resting on anything, so `pauseOnHover` there means "pause while the reader is dragging", and
`loop` wraps the autoplay without wrapping the scroll, a reader who swiped to the end stays
there. The frame states `Slide 2 of 3` as its own label, and the arrows are the characters `‹`
and `›`. See [React Native](/platforms/native).

## Related

```related tabs, image, card
```
