25
Componentregistry:component

DNA Carousel

A cinematic 3D image carousel where cards travel along two intertwined helix strands. Drag, swipe, scroll or use the arrow keys, with momentum, snapping, infinite looping, optional autoplay, blur and RGB-split depth effects, and reduced-motion support. No dependencies.

Installation

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

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

Preview

Marble statue

Marble statue · 1/6 · drag, scroll or use arrow keys

Depth260px
Helix spread70px
Curve24°
Rotation22°
Tilt-6°
Blur1.5px
RGB split0px
Side opacity0.80
Speed0.15/s

Usage

Example.tsx
import { DnaCarousel } from "@/components/dna-carousel"

const images = [
  { src: "https://images.unsplash.com/photo-1566054757965-8c4085344c96?w=600&q=80&auto=format&fit=crop", alt: "Marble statue" },
  { src: "https://images.unsplash.com/photo-1579783902614-a3fb3927b6a5?w=600&q=80&auto=format&fit=crop", alt: "Floral still life" },
  { src: "https://images.unsplash.com/photo-1578301978693-85fa9c0320b9?w=600&q=80&auto=format&fit=crop", alt: "Pastoral landscape" },
]

export function Gallery() {
  return (
    <DnaCarousel
      images={images}
      aria-label="Classical artworks"
      className="h-[420px] w-full"
    />
  )
}

Props

PropTypeDefaultDescription
images*{ src: string; alt?: string }[]—Cards to show. Short lists are repeated so the loop fills both sides of the helix.
itemWidthnumber220Card width in px, before responsive fitting.
itemHeightnumber300Card height in px, before responsive fitting.
gapnumber20Extra space between neighbouring cards along the helix axis, in px.
radiusnumber14Card corner radius in px.
depthnumber260How far the back strand sits behind the front strand, in px.
curvenumber24Extra helix twist per card, in degrees. 0 gives a flat zig-zag.
spreadnumber70Vertical distance between the two strands, in px.
perspectivenumber1100CSS perspective distance in px. Lower is more dramatic.
rotationnumber22Y-axis rotation per card away from the centre, in degrees.
tiltnumber-6Tilt of the whole helix axis, in degrees.
activeScalenumber1Scale of the centred card.
inactiveScalenumber0.8Scale of the other cards.
blurnumber1.5Blur in px per card of distance from the centre.
rgbSplitnumber0Chromatic-aberration offset in px, off by default. Grows with distance and scroll speed.
inactiveOpacitynumber0.8Opacity of cards away from the centre, 0 to 1.
shadowbooleantrueDrop shadow under each card.
shadowStrengthnumber0.35Shadow opacity, 0 to 1.
dragSensitivitynumber1Multiplier on drag distance. Higher moves more cards per swipe.
inertianumber0.93Momentum kept per frame after a flick, 0 to 1. Higher coasts longer.
snapStrengthnumber0.08How quickly cards settle into place, 0 to 1.
autoplaybooleantrueDrift on its own. Resumes 2.5 s after the user stops interacting.
autoplaySpeednumber0.15Autoplay speed in cards per second. Negative values run backwards.
pauseOnHoverbooleantruePause autoplay while a mouse is over the carousel.
wheelbooleantrueScroll the carousel with the mouse wheel or trackpad while hovering it.
grabCursorbooleantrueShow grab and grabbing cursors.
visibleItemsnumber4How many cards stay visible on each side of the active one.
onIndexChange(index: number) => void—Called with the index into images of the card that becomes active.
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. Cards are absolutely positioned, and the helix scales down to fit smaller containers.
  • Focusable, with arrow-key navigation. The active card's alt is announced through a polite live region.
  • With reduced motion, autoplay, blur and the RGB split are off and cards settle faster.
  • Stops animating while scrolled out of view.