Skip to content
WANCRAFT

B.97Cards & Containers

Hover Image List

An editorial index list whose rows reveal a floating image that follows the pointer, tilts with velocity and crossfades.

NewReactMotionTailwindintermediate

Overview

Hover Image List is the portfolio-index pattern: a typographic list of projects where hovering a row reveals its image floating under the pointer. The image trails the cursor on a spring, tilts with horizontal velocity and crossfades (with a slight zoom settle) as you move between rows, while the active title slides in, gains an accent underline and the other rows dim. Keyboard focus anchors the image beside the focused row, reduced motion turns following off entirely, and touch screens get inline thumbnails instead of a floating image. Rows with an href are links; rows without one are buttons.

01Installation

Method 01 · Registry

CLI verified

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

npx shadcn@latest add https://wancraft.my/r/hover-image-list.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/hover-image-list.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 { HoverImageList, type HoverImageListItem } from "@/components/ui/hover-image-list";

export function HoverImageListExample() {
  const projects: HoverImageListItem[] = [
    { id: "atlas", title: "Atlas Rebrand", meta: "Identity · 2026", image: "/work/atlas.jpg", href: "/work/atlas" },
    { id: "beacon", title: "Beacon Health", meta: "Product · 2025", image: "/work/beacon.jpg", href: "/work/beacon" },
    { id: "cinder", title: "Cinder Studio", meta: "Web · 2025", image: "/work/cinder.jpg", href: "/work/cinder" },
  ];

  return (
    <HoverImageList items={projects} />
  );
}

03Props

PropTypeDefaultDescription
itemsHoverImageListItem[]—Required. { id, title, meta?, image, href? }. Rows with href render links, others render buttons.
imageWidthnumber240Floating image width in px.
ratio"4/5" | "1/1" | "16/10""4/5"Aspect ratio of the floating image (and of touch thumbnails).
spring{ stiffness; damping; mass }{ 220, 24, 0.6 }Spring the image uses to follow the pointer.
maxRotatenumber8Maximum tilt in degrees, driven by horizontal velocity. 0 disables tilt.
crossfadenumber280Crossfade duration between row images, in ms.
dimOthersbooleantrueFade the inactive rows while one row is active.
showIndexbooleantrueShow a zero-padded index at the start of each row.
accentstring"#FF7448"Underline, arrow and focus ring colour.
onSelect(item) => void—Called when a row without an href is clicked.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root element.

04Features

  • 01Floating image follows the pointer on a configurable spring and tilts with horizontal velocity
  • 02Crossfades between rows with a subtle zoom settle; all images stay mounted, so switching never flashes
  • 03Active row slides in, draws an accent underline and reveals an arrow; other rows dim
  • 04Keyboard focus anchors the image beside the focused row
  • 05Touch screens (hover: none) show inline thumbnails instead of a floating image
  • 06Compensates for scaled ancestors, so the image stays under the pointer inside zoomed layouts
motion · springmotion · tweeninput · hoverinput · pointerinput · focusinput · keyboard

05Accessibility

  • A real list (<ul>) of links or buttons; the row title and meta form the accessible name.
  • The floating image is aria-hidden and pointer-events: none; inline touch thumbnails are decorative (alt="").
  • Keyboard focus shows the same image and active styling as hover, with a visible focus ring.
  • With prefers-reduced-motion the image does not follow or tilt: it fades in anchored beside the row.

06Limitations

  • Images are decorative here; if an image carries information, add it to the row text.
  • The meta column is hidden below the sm breakpoint to keep long titles readable.