Cards
TiltCraft
The card tilts toward the pointer in 3D, a soft glare follows, and it springs flat when the pointer leaves.
Download
- React
- Tailwind CSS v4
- motion
components/ui/tilt-craft.tsx
"use client";
import type { ReactNode } from "react";
import {
motion,
useMotionTemplate,
useMotionValue,
useReducedMotion,
useSpring,
useTransform,
} from "motion/react";
type TiltCraftProps = {
children: ReactNode;
className?: string;
/** Largest tilt in degrees. */
max?: number;
/** Soft light that follows the pointer over the card. */
glare?: boolean;
};
const SPRING = { stiffness: 220, damping: 20 };
/* TiltCraft: the card tilts toward the pointer in 3D and springs flat when it leaves. */
export function TiltCraft({
children,
className = "rounded-2xl border border-white/10 bg-[#16132a] p-6 text-white",
max = 12,
glare = true,
}: TiltCraftProps) {
const reduce = useReducedMotion();
const px = useMotionValue(0.5);
const py = useMotionValue(0.5);
const rotateX = useSpring(useTransform(py, [0, 1], [max, -max]), SPRING);
const rotateY = useSpring(useTransform(px, [0, 1], [-max, max]), SPRING);
const glareOpacity = useSpring(0, SPRING);
const gx = useTransform(px, (v) => `${v * 100}%`);
const gy = useTransform(py, (v) => `${v * 100}%`);
const glareBg = useMotionTemplate`radial-gradient(circle at ${gx} ${gy}, rgba(255,255,255,0.22), transparent 55%)`;
const onMove = (e: React.PointerEvent<HTMLDivElement>) => {
if (reduce) return;
const r = e.currentTarget.getBoundingClientRect();
px.set((e.clientX - r.left) / r.width);
py.set((e.clientY - r.top) / r.height);
glareOpacity.set(1);
};
const onLeave = () => {
px.set(0.5);
py.set(0.5);
glareOpacity.set(0);
};
return (
<motion.div
onPointerMove={onMove}
onPointerLeave={onLeave}
style={{ rotateX, rotateY, transformPerspective: 900 }}
className={`relative ${className}`}
>
{children}
{glare && (
<motion.span
aria-hidden
className="pointer-events-none absolute inset-0 rounded-[inherit]"
style={{ background: glareBg, opacity: glareOpacity }}
/>
)}
</motion.div>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/tilt-craft.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/tilt-craft-demo.tsx
import { TiltCraft } from "@/components/ui/tilt-craft"; export default function TiltCraftExample() { return ( <div className="grid h-full place-items-center bg-[#0b0a14] p-6"> <TiltCraft className="w-full max-w-[300px] rounded-2xl border border-white/10 bg-gradient-to-br from-[#221c3d] to-[#120f22] p-6 text-white"> <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-[#a78bfa]">Pro plan</p> <p className="mt-3 text-4xl font-semibold">₹1,499</p> <p className="mt-2 text-sm text-white/60">Move your pointer over the card.</p> </TiltCraft> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| children | ReactNode | — | Card content. |
| max | number | 12 | Largest tilt in degrees. |
| glare | boolean | true | Soft light that follows the pointer. |
| className | string | dark violet card | Replaces the default look. |