25
Componentregistry:component

Rotunda Carousel

Square images and videos arranged on a spinning ring, largest at a focus angle and tapering to small tiles opposite it. Auto-spins in either direction, eases to a stop on hover, enlarges the hovered tile, and spins by dragging around the centre or scrolling, with momentum. Optional links per tile, reduced-motion support, no dependencies.

Installation

$ npx shadcn@latest add @creative-ui/rotunda-carousel

On an older shadcn CLI, use the URL: npx shadcn@latest add https://ui.krutikraut.com/r/rotunda-carousel.json

Preview

Marble bust of a goddess by a window
Ruined round temple at golden hour
Brass armillary sphere by candlelight
Black-figure Greek amphora on a plinth
Marble muse holding a lyre in a niche
The Pantheon dome and oculus
Little owl on an Ionic capital
Pomegranates, figs and grapes in a bronze bowl

drag around the ring, scroll or use arrow keys · hover to pause

Radius240px
Largest tile150px
Smallest tile56px
Taper1.4
Focus angle180°
Corners16px
Speed10°/s
Hover scale1.18×
Direction
Tiles

Usage

Example.tsx
import { RotundaCarousel } from "@/components/rotunda-carousel"

const items = [
  { src: "/gallery/bust.webp", alt: "Marble bust", href: "/work/bust" },
  { src: "/gallery/temple.webp", alt: "Round temple" },
  { src: "/gallery/reel.mp4", poster: "/gallery/reel.jpg", alt: "Studio reel" },
  { src: "/gallery/amphora.webp", alt: "Greek amphora" },
]

export function Gallery() {
  return (
    <RotundaCarousel
      items={items}
      aria-label="Selected work"
      className="h-[520px] w-full"
    />
  )
}

Props

PropTypeDefaultDescription
items*{ src: string; alt?: string; type?: "image" | "video"; poster?: string; href?: string; target?: "_self" | "_blank" }[]—Tiles around the ring. Videos are detected by extension (or type) and play muted and looped. href turns a tile into a link.
radiusnumber240Ring radius in px to tile centres, before responsive fitting.
maxSizenumber150Tile size at the focus angle, in px. Tiles are square.
minSizenumber56Tile size opposite the focus angle, in px.
tapernumber1.4How sharply tiles shrink away from the focus. 1 is a smooth cosine; higher keeps only the focus tile large.
focusAnglenumber180Where the largest tile sits, in degrees clockwise from 12 o'clock. 180 is the bottom.
minOpacitynumber0.55Opacity of the smallest tiles, 0 to 1.
cornerRadiusnumber16Tile corner radius in px at the largest size. Scales down with the tile.
objectFit"cover" | "contain""cover"How media is cropped inside each tile.
orientation"upright" | "radial""upright"upright keeps tiles level; radial turns them with the ring like spokes.
shadowbooleantrueDrop shadow that deepens on larger tiles.
minItemsnumber8Repeats items in whole passes until the ring has at least this many tiles.
autoSpinbooleantrueRotate on its own. Flicks coast and blend back into the spin.
speednumber10Auto-spin speed in degrees per second.
direction"clockwise" | "counterclockwise""clockwise"Auto-spin direction.
pauseOnHoverbooleantrueEase the spin to a stop while a mouse is over the ring.
hoverScalenumber1.18Scale of the tile under the pointer or keyboard focus. 1 turns it off.
glidenumber0.95Momentum kept per frame after a flick or wheel spin, 0 to 1. Higher coasts longer.
smoothingnumber0.25Lag between the pointer and the ring while dragging, 0 to 0.95.
draggablebooleantrueSpin the ring by dragging around its centre.
dragSensitivitynumber1Multiplier on drag and wheel rotation.
wheelbooleantrueSpin the ring with the mouse wheel or trackpad while hovering it.
grabCursorbooleantrueShow grab and grabbing cursors.
aria-labelstring"Image carousel"Name announced for the carousel region.

Also accepts every div prop, which is spread onto the root element.

Notes

  • Give it a height with className. The ring is centred in the box and scales down to fit smaller containers.
  • Built for square media: tiles are always square and crop with objectFit.
  • Dragging follows the pointer's angle around the centre, so it works from any side of the ring. On touch, vertical swipes still scroll the page.
  • Focusable, with arrow keys stepping one tile. Linked tiles are reachable with Tab and enlarge on focus. Repeated passes from minItems are hidden from screen readers.
  • With reduced motion, auto-spin is off. Stops animating while scrolled out of view.