Carousel
Preview: Single
Native scroll-snap carousel: swipe/trackpad scrolling for free, arrow buttons and dots for mouse/keyboard. Slides are a labelled group each ("2 of 5"), following the WAI carousel pattern.
Import
import { Carousel } from '@marbleui/ui'Props
<Carousel>
| Prop | Type | Default | Description |
|---|---|---|---|
| aria-label *required | string | — | |
| children *required | ReactNode | — | |
| className | string | — | |
| gap | number | 12 | |
| loop | boolean | — | |
| perView | 1 | 2 | 3 | 1 | Slides visible at once (CSS width per slide). |
| showArrows | boolean | true | |
| showDots | boolean | true |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitive: React Aria Button. Follow the primitive's docs for the full keyboard map.
Usage notes
Horizontally scrolling set of slides with arrows and dots. For a few always-visible items use a plain grid; for tabbed switching of content use Tabs.
<Carousel aria-label="Featured projects" perView={2} loop>
<ProjectCard />
<ProjectCard />
<ProjectCard />
</Carousel>
aria-labelis required and names the carousel region. Each child becomes a slide labelled “n of N”.- Built on native scroll-snap, so touch and trackpad swiping work.
perViewis1,2or3;gapis in pixels;showArrowsandshowDotsdefault to true. - With
loop, the arrows wrap around; without it they disable (and hide) at the ends. - Uncontrolled only: there is no
indexor change callback.
Example source · show· hide
import { Carousel } from './carousel'
const slides = [
{ title: 'Plan', hue: 'from-indigo-500 to-violet-500' },
{ title: 'Build', hue: 'from-sky-500 to-cyan-400' },
{ title: 'Ship', hue: 'from-emerald-500 to-lime-400' },
{ title: 'Measure', hue: 'from-amber-500 to-orange-500' },
{ title: 'Repeat', hue: 'from-rose-500 to-pink-500' },
]
const Slide = ({ title, hue, tall }: { title: string; hue: string; tall?: boolean }) => (
<div className={`grid ${tall ? 'h-56' : 'h-36'} place-items-center rounded-xl bg-gradient-to-br ${hue} text-xl font-semibold text-white`}>{title}</div>
)
export const styles = {
Single: () => (
<Carousel aria-label="Workflow" className="w-[28rem]">
{slides.map((s) => <Slide key={s.title} {...s} tall />)}
</Carousel>
),
'Three up': () => (
<Carousel aria-label="Workflow" perView={3} className="w-[36rem]">
{slides.map((s) => <Slide key={s.title} {...s} />)}
</Carousel>
),
Loop: () => (
<Carousel aria-label="Workflow" perView={2} loop showDots={false} className="w-[30rem]">
{slides.map((s) => <Slide key={s.title} {...s} />)}
</Carousel>
),
}Source: packages/ui/src/carousel/carousel.tsx
Catalog
- Category
- data-display
- Priority
- Core
- Primitive
- custom