Original by Plinth · MIT
Cards
Tilt Card
A card that tilts in 3D toward your cursor.
#card#3d#tilt#hover
Install
npx shadcn@latest add https://plinthui.com/r/tilt-card.json'use client';
import { useRef } from 'react';
const MAX_DEG = 10;
const FLAT = 'perspective(800px) rotateX(0deg) rotateY(0deg) scale(1)';
const SETTLE = 'transform 400ms ease';
export function TiltCard() {
const cardRef = useRef<HTMLElement>(null);
const handleMouseMove = (event: React.MouseEvent<HTMLElement>) => {
const card = cardRef.current;
if (!card) return;
const rect = card.getBoundingClientRect();
const px = (event.clientX - rect.left) / rect.width - 0.5;
const py = (event.clientY - rect.top) / rect.height - 0.5;
// Written straight to the element so the pointer never triggers a
// re-render — the same approach spotlight-card uses.
card.style.transition = 'none';
card.style.transform = `perspective(800px) rotateX(${(-py * 2 * MAX_DEG).toFixed(
2,
)}deg) rotateY(${(px * 2 * MAX_DEG).toFixed(2)}deg) scale(1.02)`;
};
const handleMouseLeave = () => {
const card = cardRef.current;
if (!card) return;
// The 400ms ease only plays on the way back to flat.
card.style.transition = SETTLE;
card.style.transform = FLAT;
};
const style: React.CSSProperties = {
transform: FLAT,
transition: SETTLE,
willChange: 'transform',
};
return (
<article
ref={cardRef}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={style}
className="h-40 w-[260px] rounded-xl border border-[#26282e] bg-[#16181c] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.06),0_12px_28px_rgba(0,0,0,0.35)]"
>
<p className="text-[11px] font-semibold uppercase tracking-[0.12em] text-[#c8a96a]">Cards</p>
<h3 className="mt-2.5 text-[17px] font-semibold text-[#ece9e2]">Tilt toward me</h3>
<p className="mt-2 text-[13px] leading-[1.6] text-[#8a8d96]">
The card leans after your cursor and settles back flat when you leave.
</p>
</article>
);
}