Rime

Carousel

A row of slides on a native scroll-snap track, with frosted prev and next buttons, dots and arrow keys.

Carousel lays its children out as slides on a scroll-snap track, so swipe, trackpad and the keyboard all scroll the same native element with no dependency. perView shows several at once on wide screens (one on phones); the buttons fade out at the ends unless loop is on.

autoplay (off by default) advances on a timer, stops while the pointer or focus is inside and has its own pause button, as WCAG 2.2.2 requires. Reduced motion scrolls instantly.

Installation

npx shadcn@latest add KezLahd/rime/carousel

Also installs: base, icon.

Usage

import { Carousel } from "@/components/ui";

<Carousel aria-label="Featured projects" perView={3}>
  {projects.map((p) => <ProjectCard key={p.id} project={p} />)}
</Carousel>

Examples

One per view

Three per view

API

Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.

Carousel

Carousel props
PropTypeDefaultDescription
childrenRequiredReactNodeOne child per slide.
aria-labelRequiredstringNames the carousel region: "Featured projects".
perView1 | 2 | 3 | 41Slides visible at once on wide screens. Narrow screens show one. Default 1.
gapnumber12Gap between slides in px. Default 12.
controlsbooleantruePrev and next buttons. On by default.
dotsbooleantrueThe dot row under the track. On by default.
autoplaynumberAdvance every n ms. Off by default; pauses on hover and focus, and has a pause button.
loopbooleanfalseStart again from the first slide after the last. Default false.
onIndexChange(index: number) => void
classNamestring

Theme tokens

Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.

Carousel per-component tokens
TokenDefaultLive value
--carousel-gap12px…
--carousel-nav-radiusvar(--r-full)…
--carousel-per-view1…
--carousel-radiusvar(--r-lg)…
--r-circle50%…

System tokens it reads

Carousel system tokens
TokenDefaultLive value
--brandtokens.css…
--durtokens.css…
--easetokens.css…
--focus-ringtokens.css…
--ink-brandtokens.css…
--ink-headingtokens.css…
--ink-secondarytokens.css…
--line-inputtokens.css…
--neutral-bordertokens.css…
--popover-blurtokens.css…
--popover-surfacetokens.css…
--r-fulltokens.css…
--r-lgtokens.css…
--r-smtokens.css…
--recess-filltokens.css…
--recess-fill-hovertokens.css…
--shadow-floattokens.css…
--surface-solidtokens.css…
--text-captiontokens.css…

Accessibility

  • A section with aria-roledescription="carousel" and a required aria-label; each slide is a group with aria-roledescription="slide" labelled "2 of 5".
  • Left and Right arrows move a slide; the track is focusable; off-screen slides are inert so Tab never lands on a hidden card.
  • The track is a polite live region while not autoplaying, so a change of slide is announced.

Do and don't

Do

  • Use it for browsable, optional content: featured items, screenshots.
  • Show a partial next slide (perView 3 at narrower widths) so people see there is more.

Don't

  • Don't put the only copy of important information on slide two or later.
  • Don't turn autoplay on for content people need to read.

For AI agents

This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.

Open carousel.md

Related: aspect-ratio, card