General · astro-ui · Carousel

CarouselJS

Accessible scroll-snap slide deck with buttons, counter, drag, and vertical layout.

Carousel is a native scroll container with CSS scroll-snap — scrolling IS the interaction, so touch, trackpad, and drag all work with no positioning math. Prev/next buttons and an optional counter ride the header row; a small scoped script syncs the active slide on scroll, wires buttons, and adds ArrowLeft/ArrowRight (↑/↓ vertical) navigation with optional loop wrap. Slides are CarouselSlide wrappers around any content, each announced as “x of y”. variant=“multiple” shows a peek layout sized via slideBasis. A bubbling carousel:change event fires on move, and carousel:set ({index} or {action}) drives it programmatically.

Features

  • Native scroll-snap: touch, trackpad, and drag work with zero positioning code
  • Prev/next buttons, optional “1 / 5” counter, optional loop wrap
  • Overlay prev/next arrows floating on the slide edges (controlsPlacement="overlay")
  • Dot indicators under the deck — click to jump, active dot tracks the scroll
  • Per-slide captions (title + description) overlaid on the slide's lower edge
  • Autoplay with a configurable interval; pauses on hover and focus
  • Keyboard navigation when the scroll container is focused; slides announced as “x of y”
  • Horizontal and vertical orientations; single and multiple (peek) variants
  • Programmatic API via carousel:set; carousel:change fires on every move
  • snapType mandatory/proximity, custom gap and slide basis

Usage

---
import { Carousel, CarouselSlide } from "astro-ui";
---

<Carousel label="Featured cards">
  <CarouselSlide><div class="card">1</div></CarouselSlide>
  <CarouselSlide><div class="card">2</div></CarouselSlide>
  <CarouselSlide><div class="card">3</div></CarouselSlide>
</Carousel>

Demos

API

Props

PropTypeDefaultDescription
labelstring"Carousel"Accessible name for the carousel region.
orientation"horizontal" | "vertical""horizontal"Scroll axis.
variant"single" | "multiple""single"One slide per snap, or a peek layout sized via slideBasis.
loopbooleanfalseWrap navigation from the last slide to the first and back.
defaultIndexnumber0Zero-based slide shown initially.
controlsbooleantrueShow the prev/next buttons.
controlsPlacement"header" | "overlay""header"Buttons in the header row or floating over the slide edges.
indicatorsbooleanfalseOne dot per slide; click jumps, the active dot tracks the scroll.
autoplaybooleanfalseAuto-advance on an interval (implies loop); pauses on hover/focus and under reduced motion.
intervalnumber4000Autoplay delay in milliseconds (min 1000).
showCounterbooleanhorizontal+singleShow the “1 / 5” counter.
counterLabelstring"Slide"Word used in the counter's aria-label.
previousLabel / nextLabelstring"Previous slide" / "Next slide"Accessible labels for the buttons.
snapType"mandatory" | "proximity""mandatory"Scroll-snap strictness.
contentClassstring—Classes for the scroll container (e.g. fixed height for vertical).
slideBasisstring100% / 50%Slide size for variant="multiple" (any flex-basis value).
gapstringtokenSpace between slides (any CSS gap value).
classstring—Merged via cn(); Tailwind classes welcome.
idstring—Element id.

Accessibility

Implemented accessibility features — semantics, ARIA wiring, and keyboard behavior that ship with the component.

  • Root is role=region with aria-roledescription=carousel and a required accessible name
  • Slides carry aria-roledescription=slide and “x of y” aria-labels via the script
  • Scroll container is tabindex=0 so keyboard users can reach slide navigation
  • Buttons carry explicit aria-labels; counter is live text
  • Reduced motion: scrollIntoView respects the browser's behavior setting

FAQ

Why scroll-snap instead of transform math?

The browser owns the physics: touch momentum, trackpad inertia, rtl, and resize all behave natively, and the script only syncs state. Transform-based carousels re-implement all of that and usually get drag wrong.

How do I drive it from outside?

Dispatch carousel:set on the root with {index: 2} or {action: 'next'}. Listen for carousel:change to sync external UI like dot indicators.

Changelog

Version history mentioning Carousel.

v0.1.0 — 2026-11-11
added
  • General (7): Button, ButtonGroup, Command, Carousel, Clipboard, Kbd, Link.
fixed
  • Dialog, Lightbox, Sheet, and AlertDialog backdrops, Progress stripes, Carousel shadows, and Chart SVG shadows resolve through tokens instead of hard-coded colors.