Skip to content
ssntl/ui

Carousel

A looping carousel with neighbours peeking in and a progress dot per card: autoplay pauses on interaction and off screen, and waits for Play with reduced motion.

Installation

Add it with the shadcn CLI, or copy the source by hand.

$ pnpm dlx shadcn@latest add @ssntl/carousel

API reference

Carousel

  • duration?Type: numberHow long each card stays up during autoplay, in ms.Default:5000

Also takes every prop of <div>.

CarouselTabList

  • aria-labelrequiredType: stringNames the set: “Our clients' businesses”.Default:—

Also takes every prop of <div>.

CarouselPlayButton

  • playLabel?Type: stringDefault:"Play carousel"
  • pauseLabel?Type: stringDefault:"Pause carousel"

Also takes every prop of Button.

No props of their own: each passes every prop to the element on the right.

  • CarouselContent<div>
  • CarouselItem<li>
  • CarouselControls<div>
  • CarouselTab<button>

useCarouselItem()

The enclosing item's state. Content also renders in clones: check isClone before playing media or tracking impressions.

Returns

  • indexType: numberThe item's position among the items in CarouselContent.
  • isCurrentType: booleanWhether this item is the one being shown.
  • isCloneType: booleanWhether this copy is a clone that keeps the loop seamless.

Rules

What this component expects from you, as it ships in components/ui/carousel.md next to the code. The full file adds when to use it, keyboard, the UI foundations it applies and every shared rule.

components/ui/carousel.md
## Rules for Carousel

- **Pausing.** Autoplay has a play button (WCAG 2.2.2) and pauses by itself when people scroll, swipe, focus a dot or the carousel leaves the screen.
- **Reduced motion.** No autoplay until Play is pressed, and cards jump instead of sliding.
- One tab per card, in the same order, whose text names the card; arrows, Home and End move between them (WAI-ARIA carousel with tabs).
- Only the current card can be reached: clones and peeking cards are inert, so nobody follows a link on a card they aren't looking at.
- The progress fill is the clock: a scale animation on the compositor that pauses in place; the dot grows into a pill over `--duration-panel`.
- Dots are 8px; the target around each is at least 32 × 48px, well over WCAG's 24px for a dense set.
- Photos fill the card with a 1px outline at 10%; a caption sits on a dark fade, and peeking cards dim theirs.
- **Content also renders in clones.** Check `isClone` (`useCarouselItem`) before playing media or tracking impressions.
Open the full rules file (opens in a new tab)