CoreHigh-End Animated Cardsmotion/react

HoloTiltCard

Three-dimensional perspective card with layered Z-depth planes, moving specular highlights, and a pointer-driven shifting holographic foil gradient.

Preview

3D Holographic Depth

Holographic Foil Surface

Pointer coordinates drive both 3D perspective pitch/roll and spectral diffraction angle.

Customize

4 props
14
0.24
920

Installation

Copies the component source and its CSS Module into your project.

npx motionfoundry add holo-tilt-card

Writes components/ui/holo-tilt-card.tsx and components/styles/holo-tilt-card.module.css.

Usage

Updates live as you change the controls — only props that differ from the defaults are included.

example.tsx
1import { HoloTiltCard } from "@motionfoundry/ui";2 3export function Example() {4  return (5    <HoloTiltCard />6  );7}

Props

PropTypeDefault
title

Card heading.

string'Holographic Foil Surface'
subtitle

Supporting copy.

string'Pointer coordinates drive...'
maxTilt

Maximum pitch and roll tilt angle in degrees.

number14
holoOpacity

Opacity of the shifting holographic gradient layer.

number0.24
perspective

CSS 3D perspective distance in pixels.

number920

Accessibility

Smoothly restores the card to its flat resting plane on pointer leave and disables 3D perspective rotation when prefers-reduced-motion is active.

Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.

Source

components/ui/holo-tilt-card.tsx
1"use client";2 3import * as React from "react";4import { motion, useMotionValue, useSpring, useReducedMotion } from "motion/react";5import styles from "../styles/holo-tilt-card.module.css";6 7export interface HoloTiltCardProps {8  /** Card title */9  title?: string;10  /** Supporting subtitle */11  subtitle?: string;12  /** Maximum tilt angle in degrees */13  maxTilt?: number;14  /** Holographic foil opacity (0.05 to 0.5) */15  holoOpacity?: number;16  /** CSS perspective distance in pixels */17  perspective?: number;18  /** Optional children */19  children?: React.ReactNode;20  /** Optional className */21  className?: string;22}23 24export function HoloTiltCard({25  title = "Holographic Foil Surface",26  subtitle = "Pointer coordinates drive both 3D perspective pitch/roll and spectral diffraction angle.",27  maxTilt = 14,28  holoOpacity = 0.24,29  perspective = 920,30  children,31  className = "",32}: HoloTiltCardProps) {33  const ref = React.useRef<HTMLDivElement>(null);34  const prefersReducedMotion = useReducedMotion();35 36  const rawRx = useMotionValue(0);37  const rawRy = useMotionValue(0);38  const rotateX = useSpring(rawRx, { stiffness: 240, damping: 22 });39  const rotateY = useSpring(rawRy, { stiffness: 240, damping: 22 });40  const [holoState, setHoloState] = React.useState({ x: 50, y: 50, angle: 125, active: false });41 42  const handlePointerMove = (e: React.PointerEvent<HTMLDivElement>) => {43    if (prefersReducedMotion || !ref.current) return;44    const rect = ref.current.getBoundingClientRect();45    const px = (e.clientX - rect.left) / rect.width;46    const py = (e.clientY - rect.top) / rect.height;47    rawRx.set((0.5 - py) * maxTilt * 2);48    rawRy.set((px - 0.5) * maxTilt * 2);49    setHoloState({50      x: px * 100,51      y: py * 100,52      angle: 110 + (px - 0.5) * 90,53      active: true,54    });55  };56 57  const handlePointerLeave = () => {58    rawRx.set(0);59    rawRy.set(0);60    setHoloState((prev) => ({ ...prev, active: false }));61  };62 63  return (64    <div style={{ perspective: `${perspective}px` }} className={styles.stage}>65      <motion.div66        ref={ref}67        onPointerMove={handlePointerMove}68        onPointerLeave={handlePointerLeave}69        style={{70          rotateX: prefersReducedMotion ? 0 : rotateX,71          rotateY: prefersReducedMotion ? 0 : rotateY,72          transformStyle: "preserve-3d",73        }}74        className={`${styles.card} ${className}`.trim()}75      >76        <div77          aria-hidden="true"78          className={styles.holoLayer}79          style={{80            opacity: holoState.active && !prefersReducedMotion ? holoOpacity : 0.06,81            backgroundImage: `linear-gradient(${holoState.angle}deg, rgba(56,189,248,0.55) 0%, rgba(168,85,247,0.55) 35%, rgba(244,114,182,0.5) 68%, rgba(52,211,153,0.55) 100%)`,82            backgroundPosition: `${holoState.x}% ${holoState.y}%`,83          }}84        />85        <div86          aria-hidden="true"87          className={styles.specularGlare}88          style={{89            opacity: holoState.active && !prefersReducedMotion ? 0.18 : 0,90            background: `radial-gradient(circle at ${holoState.x}% ${holoState.y}%, rgba(255,255,255,0.9), transparent 60%)`,91          }}92        />93        <div className={styles.depthContent}>94          <span className={styles.badge}>3D Holographic Depth</span>95          <h3 className={styles.title}>{title}</h3>96          <p className={styles.subtitle}>{subtitle}</p>97          {children}98        </div>99      </motion.div>100    </div>101  );102}