Page Sections

3D Tilt Review Card

A slightly askew testimonial card that tilts in 3D perspective with a moving sheen as the pointer moves, the quote floating above the surface, flipping over to switch entries.

A testimonial section that tells one customer story at a time. The centred card rests at a slight angle; move the pointer over it and it tilts in 3D perspective based on the pointer position, with a sheen that follows the pointer across the surface. The big quote mark, the quote and the author row float above the card along the Z axis, while the blurred blobs and fine grid behind it shift the opposite way to add depth. Switching with the buttons below turns the card edge-on, swaps in the next entry and turns it back, in the direction of "previous" or "next". The current index supports both controlled and uncontrolled modes.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add 3d-tilt-review-card.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

10
-3
200
500
Active edited usageDefault props
<ThreeDTiltReviewCard/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/3d-tilt-review-card.en.json

Or, once registries are configured in components.json, install it as @webberui/3d-tilt-review-card.

Usage

import { ThreeDTiltReviewCard } from "@/components/ui/3d-tilt-review-card";

<ThreeDTiltReviewCard
  eyebrow="客戶怎麼說"
  title="一張卡,說一個真實的改變"
  testimonials={[
    {
      quote: "換上新的點餐頁之後,尖峰時段的出杯速度明顯快了一截。",
      name: "林語晴",
      role: "營運經理",
      company: "青嶼咖啡",
      rating: 5,
      highlight: "出杯速度明顯快了一截",
      metric: { value: "+32%", label: "尖峰出杯量" },
    },
    {
      quote: "新進同仁第一天就能自己跑完整條路線。",
      name: "周柏翰",
      role: "調度主任",
      company: "北辰物流",
    },
  ]}
  maxTilt={10}
  restRotate={-3}
  onIndexChange={(i) => console.log(`切到第 ${i + 1} 則`)}
/>

Props

PropTypeDefaultDescription
eyebrowstring—Small label above the title
titlestring—Section title; also the accessible name of the section
subtitlestring—Supporting text below the title
testimonialsThreeDTiltReviewCardItem[]—Testimonials, shown one at a time
maxTiltnumber10Maximum tilt in degrees, reached at the card edge; 0 disables tilt
restRotatenumber-3Resting in-plane tilt (degrees); the --wb-3d-tilt-review-card-rest CSS variable wins
indexnumber—Controlled current index; managed internally when omitted
defaultIndexnumber0Initial index in uncontrolled mode
onIndexChange(index: number) => void—Called when the index changes
glarebooleantrueShow the pointer-following sheen on the card face
stiffnessnumber—Tilt spring stiffness; reads --wb-3d-tilt-review-card-stiffness when omitted (default 200)
durationnumber—Total flip duration (ms); reads --wb-3d-tilt-review-card-duration when omitted (default 500ms)
prevLabelstring"上一則評價" (Previous testimonial)Accessible name of the "previous" button
nextLabelstring"下一則評價" (Next testimonial)Accessible name of the "next" button
classNamestring—Class added to the root section

ThreeDTiltReviewCardItem

FieldTypeDefaultDescription
quotestring—Testimonial text
namestring—Reviewer's name; the avatar shows initials (last two characters for CJK, first letters for Latin)
rolestring—Job title
companystring—Company or organization
ratingnumber—Star rating (1–5); hidden when omitted
avatarHuenumber—Avatar gradient hue (0–360); assigned by index with the golden angle when omitted
highlightstring—Fragment of the quote to mark with the accent colour (must match exactly)
metric{ value: string; label: string }—Result metric to the right of the author row

How it works

  • Timing variables: --wb-3d-tilt-review-card-stiffness (tilt spring, default 200, damping fixed at 20), --wb-3d-tilt-review-card-duration (total flip time, default 500ms, split evenly between exit and enter) and --wb-3d-tilt-review-card-rest (resting tilt, default -3deg). The accent colour comes from --wb-3d-tilt-review-card-accent (#0d9488 light, #2dd4bf dark).
  • Layers: the big quote mark sits at translateZ(60px), the quote and author row at translateZ(40px); the card itself does not use overflow-hidden, so the 3D is not flattened.
  • The sheen is a 15% white radial-gradient whose centre follows the pointer; when the pointer leaves, tilt and sheen spring back to rest with the same spring.
  • Flip direction: "next" turns to 90° and comes back from -90°, "previous" does the reverse; wrapping from the last entry to the first still counts as "next".
  • Touch devices (pointer: coarse) skip pointer tilt and keep only the resting tilt and the flip.

Accessibility

  • The root is a <section>; with a title it uses aria-labelledby pointing at the heading, otherwise the eyebrow or "Testimonials" becomes its aria-label
  • Each entry renders as <figure> / <blockquote> / <figcaption>; the star rating is announced through role="img" with a text label
  • Switch buttons are type="button" with an aria-label and a focus-visible ring; the left and right arrow keys switch entries within the button group
  • On switch, a visually hidden aria-live="polite" region announces the position ("2 of 4") and the quote; the two cards that briefly coexist mid-flip are not announced twice
  • When the system's reduce motion setting is on, pointer tilt, sheen and flip are disabled and switching becomes a 150ms crossfade; entries can still be switched with the buttons