One scrolling core for panels and websites: card sliders, a fade + autoplay hero, a gallery with thumbnails, a KPI strip. Presets set the control layout; the WAI-ARIA carousel pattern, keyboard support, inert off-screen slides, a mandatory pause button and reduced motion come built in. Marquee is a pure-CSS logo band.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Carousel, CarouselViewport, CarouselSlide, CarouselDots } from "@vamos-ui/react";
<Carousel label="Featured stays" slidesPerView={{ base: 1, md: 2, lg: 3 }}>
<CarouselViewport>
{stays.map((s) => <CarouselSlide key={s.id}>…</CarouselSlide>)}
</CarouselViewport>
<CarouselDots />
</Carousel>Keys when focus is inside: ←/→ (↑/↓ vertical), Home, End. RTL flips the arrows and the direction.
No backdrop-filter on slides: only the overlay controls blur (glass budget).
Examples
Hero (fade + autoplay)
preset="hero": glass arrows inside, dots over the image, a pause button first in the tab order. Autoplay pauses on hover and focus and is off under reduced motion.
Gallery with thumbnails
CarouselThumbs and CarouselCounter follow the selected slide.
Villa Alaçatı · 4 bedrooms · private pool
Slides per breakpoint (KPI strip)
slidesPerView takes a breakpoint map; widths are pure CSS, so the layout is right before hydration. preset="strip" fades the edges.
Vertical
orientation="vertical" with ↑/↓ keys. Set the height with --carousel-height.
Marquee (pure CSS)
No JS, renders with SSR, pauses on hover. Listed once for screen readers; static under reduced motion.
Trusted by 1,200 teams across Türkiye
Controlled, two in sync
index + onIndexChange keep two carousels on the same slide.
Custom controls (useCarousel)
useCarousel() exposes index, count, canPrev/canNext and the commands for your own controls.
API
Carousel
| Prop | Type | Default |
|---|---|---|
labelRequired accessible name (aria-label on the region). | string | — |
presetControl layout: hero = full-bleed with glass arrows inside and dots on the image; gallery = thumbnails below; strip = arrows outside, faded edges. | "default" | "gallery" | "hero" | "strip" | "default" |
controlsArrow placement. Outside arrows hide below 640 px (swipe instead). Keyboard works in every mode. | "inside" | "outside" | "hidden" | inside for hero/gallery, else outside |
slidesPerViewSlides visible at once (CSS flex-basis). auto lets each slide size itself. | number | "auto" | { base?, sm?, md?, lg?, xl? } | 1 |
gapSpace between slides, from --gap. | "none" | "sm" | "md" | "lg" | "md" |
orientationVertical uses ↑/↓ and --carousel-height. | "horizontal" | "vertical" | "horizontal" |
loopWrap around at the ends. | boolean | false |
alignSnap alignment. | "start" | "center" | "start" |
transitionfade cross-fades one slide per view. Reduced motion: instant. | "slide" | "fade" | "slide" |
autoplayAdvance on a timer (default 6 s). Adds a Play/Pause button; pauses on hover/focus; aria-live off while playing. | boolean | { delay?: number; stopOnInteraction?: boolean } | false |
index / defaultIndex / onIndexChangeControlled or uncontrolled selected slide. | number / number / (i) => void | — / 0 |
loadingA skeleton first slide while there are no slides yet. | boolean | false |
options / pluginsEscape hatch to the engine. | EmblaOptions / EmblaPlugin[] | — |
setApiReceives the engine API once ready. | (api) => void | — |
labelsprevious, next, goTo, pause, play, slideOf, counter… (dictionary namespace carousel). | Partial<CarouselLabels> | — |
CarouselViewport
| Prop | Type | Default |
|---|---|---|
arrowsBuilt-in prev/next on the stage (hidden with one slide or controls="hidden"). | boolean | true |
playPauseBuilt-in pause button with autoplay. Turn off only when you render CarouselPlayPause yourself. | boolean | true |
emptyShown when there are no slides. | ReactNode | "Nothing to show yet" |
containerClassNameClass for the flex track. | string | — |
CarouselSlide
| Prop | Type | Default |
|---|---|---|
…div propsrole="group", aria-roledescription="slide", labelled "3 of 7"; inert while off-screen. | HTMLAttributes | — |
Controls
| Prop | Type | Default |
|---|---|---|
CarouselPrev / CarouselNextDisabled at the edges unless loop. Glass buttons over images. | IconButton props + label? | — |
CarouselDotsOne button per snap; aria-current on the active one. | HTMLAttributes | — |
CarouselThumbsThumbnail buttons; the selected one gets a --ring border. | { items: (string | { src; alt? })[]; aspect?: number } | — |
CarouselPlayPauseRenders only while autoplay is configured. | IconButton props | — |
CarouselCounter"3 / 7" in the locale's digits (with a full sentence for screen readers). | HTMLAttributes | — |
useCarousel()State and commands inside a Carousel. | → { api, index, count, canPrev, canNext, playing, scrollTo, scrollPrev, scrollNext, play, pause, toggle } | — |
Marquee
| Prop | Type | Default |
|---|---|---|
labelAccessible name of the list. | string | — |
durationSeconds per loop. | number | 40 |
reverseScroll the other way. | boolean | false |
pauseOnHoverPause while hovered or focused. | boolean | true |
fadeFade the edges. | boolean | true |
gapSpace between items. | "sm" | "md" | "lg" | "lg" |
controlsPause control (a native checkbox) for moving content longer than five seconds (WCAG 2.2.2). | boolean | true |
pauseLabelAccessible name of the pause control. | string | "Pause scrolling" |