Skip to content
WANCRAFT

H.1523D & Experimental

Coverflow

The classic coverflow: a forward-facing centre cover with the rest turned away in depth, on one spring.

NewReactMotionCSSTailwindadvanced

Overview

Coverflow lays covers out in CSS 3D: the centred one faces you, the others turn away on rotateY and stack in depth with configurable angle and spacing, shading darker as they turn, with an optional faded reflection underneath. Every cover's transform is derived from a single position motion value, so the whole row glides on one configurable spring without re-rendering React. Drag or swipe to scrub (the flick carries into the snap, with a rubber band at the ends), swipe a trackpad or Shift + wheel horizontally, use the arrow, Home and End keys while the stage is focused, the previous/next buttons, or click a side cover to centre it. Only the centred cover is interactive — the others are aria-hidden with inert content — and a polite live region announces "Album 3 of 9: title". With reduced motion every change is instant.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/coverflow.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/coverflow.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/coverflow.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { Coverflow, type CoverflowItem } from "@/components/ui/coverflow";

export function CoverflowExample() {
  const items: CoverflowItem[] = ["Copper Hours", "Paper Weather", "Night Kiln", "Northern Grid", "Glasshouse"].map((title, i) => ({
    id: title.toLowerCase().replace(/\W+/g, "-"),
    label: title,
    content: (
      <div className="flex h-full flex-col justify-end p-4" style={{ background: `hsl(${i * 70} 70% 60%)` }}>
        <p className="font-semibold tracking-tight">{title}</p>
      </div>
    ),
  }));

  return (
    <Coverflow items={items} label="Recent albums" itemName="Album" />
  );
}

03Props

PropTypeDefaultDescription
items{ id: string; content: ReactNode; label: string }[]—Covers. label names the slide and is announced.
labelstring"Coverflow"Accessible name of the carousel.
itemNamestring"Item"Noun used in announcements and button labels: "Album 3 of 9: title".
sizenumber220Cover width and height in px. The scene scales down to fit narrow containers.
anglenumber60rotateY of the side covers, in degrees.
spacingnumber0.26Distance between stacked side covers as a fraction of the cover size.
spring{ stiffness: number; damping: number; mass: number }{ stiffness: 220, damping: 30, mass: 1 }Spring for moves and snaps.
reflectionbooleantrueRender a faded mirror image under each cover (decorative, inert).
showControlsbooleantruePrevious/next buttons and the counter.
accentstring"#FF7448"Focus ring, centre-cover edge and control colour.
defaultIndex / onIndexChangenumber / (index) => void0Initially centred item; called when the centred item changes.

04Features

  • 01Every cover's x, z, rotateY, z-index and shade derived from one position motion value — no per-frame renders
  • 02Drag or swipe with velocity projection into a spring snap, rubber-banding past the ends
  • 03Horizontal trackpad swipes and Shift + wheel step through covers; vertical scrolling stays with the page
  • 04Arrow, Home and End keys on the focused stage; previous/next buttons; click a side cover to centre it
  • 05Optional reflections, angle-based shading and a scene that scales to fit narrow containers
motion · springinput · draginput · pointerinput · clickinput · keyboardinput · focus

05Accessibility

  • A <section> with aria-roledescription="carousel"; each cover is a group with aria-roledescription="slide" named "3 of 9: title".
  • Only the centred cover is interactive: side covers are aria-hidden and their content is inert; reflections are decorative and inert.
  • The focusable stage responds to Left/Right, Home and End; buttons are labelled ("Next album") and show visible focus outlines.
  • A polite live region announces "Album 3 of 9: title" whenever the centred cover changes.
  • prefers-reduced-motion makes every move instant (drag still follows the pointer directly).

06Limitations

  • Reflections duplicate each cover's content; keep cover content light (images, short text) when they are on.