caliper uidocs

Carousel

Free

A swipeable row of slides with controls and dots.

Installs components/ui/carousel.tsxDepends on @radix-ui/react-slot, lucide-reactMotion honours reduced motion
pnpm dlx shadcn@latest add https://caliperui.site/r/carousel.json

1 of 6

Harbour study illustration

Harbour study

Cool shadows, one warm accent.

Terrace study illustration

Terrace study

Repeating arches at three depths.

Ridge study illustration

Ridge study

Layered hills with a low sun.

Palette notes

Six categorical colors, quieter in light mode.

Grid notes

Eight-point grid with a 4-point half step.

Type notes

Two weights. Size does the rest.

When to use #

Use it for

  • Showing a handful of related items one or few at a time.
  • Touch-first browsing.

Skip it when

  • The content is critical; hidden slides may be missed.
  • There is room to show everything in a grid.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/carousel.json

Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import Image from "next/image"
import { Carousel } from "@/components/ui/carousel"

const studies = [
  { id: "harbour", src: "/media/harbour.svg", title: "Harbour study", note: "Cool shadows, one warm accent." },
  { id: "terrace", src: "/media/terrace.svg", title: "Terrace study", note: "Repeating arches at three depths." },
  { id: "ridge", src: "/media/ridge.svg", title: "Ridge study", note: "Layered hills with a low sun." },
];

const slides = [
  ...studies.map((s) => ({
    id: s.id,
    label: s.title,
    content: (
      <article className="overflow-hidden rounded-overlay border border-border">
        <Image src={s.src} alt={`${s.title} illustration`} width={800} height={600} className="aspect-[4/3] w-full object-cover" draggable={false} />
        <div className="p-3">
          <h4 className="text-sm font-medium">{s.title}</h4>
          <p className="mt-1 text-xs text-muted-foreground">{s.note}</p>
        </div>
      </article>
    ),
  })),
  ...["Palette", "Grid", "Type"].map((t, i) => ({
    id: `note-${i}`,
    label: `${t} notes`,
    content: (
      <article className="grid h-full content-between gap-6 rounded-overlay border border-border bg-surface p-4">
        <h4 className="text-sm font-medium">{t} notes</h4>
        <p className="text-sm text-muted-foreground">{["Six categorical colors, quieter in light mode.", "Eight-point grid with a 4-point half step.", "Two weights. Size does the rest."][i]}</p>
      </article>
    ),
  })),
];

export function CarouselDemo() {
  return (
    <Carousel label="Illustration studies" items={slides} />
  );
}

API reference #

Accessibility #

It exposes status and group semantics, with aria-roledescription, aria-label, aria-hidden and aria-current kept in step with state. Changes that matter are announced through a live region.

KeyBehaviour
→Moves to the next item or value.
←Moves to the previous item or value.
HomeJumps to the first item or the minimum.
EndJumps to the last item or the maximum.

Motion #

It checks prefers-reduced-motion itself and settles immediately instead of travelling. Every state is still reachable.