Swipe Carousel
betaCards on a ring seen from the front — swipe, tap or step and the ring turns, so nothing ever leaves the frame.
- Display
- 2 files
- 2 npm packages
- Accessibility notes
- Animates with motion
Loading the example…
pnpm dlx @dowel-ui/cli add swipe-carouselnpm packages installed: class-variance-authority, motion.
import { SwipeCarousel } from "@/components/ui/swipe-carousel";Default
Gallery
With Images
Seven Cards
Still
Controlled
Right To Left
<SwipeCarousel>
| Prop | Type | Default |
|---|---|---|
aspectRatioHeight as a multiple of width. | number | 1.417 |
cardWidthAny CSS length. | string | "13rem" |
defaultIndex | number | 0 |
floatLets each card drift gently while idle. | boolean | true |
index | number | — |
labels | Partial<SwipeCarouselLabels> | — |
onIndexChange | (index: number) => void | — |
childrenOne child per card: an image, or any content. | ReactNode | — |
Plus every attribute of <section> except children.
Follows the APG carousel pattern: a section with aria-roledescription="carousel", each card a labelled group with aria-roledescription="slide", and only the front card exposed (the others are aria-hidden and inert). The swipe is never the only way through: named previous/next buttons, arrow keys (mirrored in RTL), Home and End, and a tap on a card peeking out at either side all turn the ring. A drag that ends over a link in the front card does not follow it. Under reduced motion the ring jumps instead of springing and the idle float stops; the spring is `motion`, wrapped in MotionConfig reducedMotion="user".
8/8
checks passed
- Tested — passes
- axe assertion — passes
- Keyboard tested — does not apply
- Storybook examples — passes
- Accessibility documented — passes
- Semantic tokens only — passes
- Motion from tokens — passes
- className merged — passes
- Visible focus — does not apply
- No fixed widths — passes