← Back
3D

3D Card

Card that tilts in 3D space following the cursor using CSS perspective and rotateX/rotateY transforms.

Frontend Engineer

Awesome FE

2025

ThreeDCard.tsx88 lines
"use client";

import { useRef } from "react";

export default function ThreeDCard() {
  const cardRef = useRef<HTMLDivElement>(null);

  function onMouseMove(e: React.MouseEvent<HTMLDivElement>) {
    const el = cardRef.current;
    if (!el) return;
    const { left, top, width, height } = el.getBoundingClientRect();
    const x = (e.clientX - left) / width - 0.5;
    const y = (e.clientY - top) / height - 0.5;

    el.style.transition = "transform 60ms ease-out";
    el.style.transform = `perspective(900px) rotateY(${x * 22}deg) rotateX(${y * -22}deg) scale(1.02)`;

    // glare position
    el.style.setProperty("--gx", `${(x + 0.5) * 100}%`);
    el.style.setProperty("--gy", `${(y + 0.5) * 100}%`);
  }

  function onMouseLeave() {
    const el = cardRef.current;
    if (!el) return;
    el.style.transition = "transform 500ms cubic-bezier(0.23,1,0.32,1)";
    el.style.transform = "perspective(900px) rotateY(0deg) rotateX(0deg) scale(1)";
  }

  return (
    <div className="flex items-center justify-center py-16">
      <div
        ref={cardRef}
        onMouseMove={onMouseMove}
        onMouseLeave={onMouseLeave}
        className="relative w-72 h-96 rounded-2xl cursor-pointer select-none overflow-hidden shadow-2xl"
        style={{
          background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%)",
        }}
      >
        {/* static inner highlight */}
        <div className="absolute inset-0 bg-gradient-to-tr from-white/5 via-white/10 to-transparent" />

        {/* dynamic glare */}
        <div
          aria-hidden
          className="pointer-events-none absolute inset-0"
          style={{
            background:
              "radial-gradient(circle 160px at var(--gx, 50%) var(--gy, 50%), rgba(255,255,255,0.18) 0%, transparent 70%)",
          }}
        />

        {/* card content */}
        <div className="relative h-full flex flex-col justify-between p-7">
          {/* top */}
          <div className="flex items-center justify-between">
            <div className="w-10 h-10 rounded-full bg-white/20 backdrop-blur-sm" />
            <div className="flex gap-1">
              <div className="w-2 h-2 rounded-full bg-white/40" />
              <div className="w-2 h-2 rounded-full bg-white/40" />
              <div className="w-2 h-2 rounded-full bg-white/40" />
            </div>
          </div>

          {/* middle decoration */}
          <div className="space-y-2">
            <div className="h-1.5 rounded-full bg-white/25 w-full" />
            <div className="h-1.5 rounded-full bg-white/25 w-4/5" />
            <div className="h-1.5 rounded-full bg-white/15 w-3/5" />
          </div>

          {/* bottom */}
          <div>
            <p className="text-white/60 text-xs font-mono mb-1 tracking-widest uppercase">
              Frontend Engineer
            </p>
            <h3 className="text-white font-bold text-2xl tracking-tight">
              Awesome FE
            </h3>
            <p className="text-white/50 text-sm mt-1 font-mono">2025</p>
          </div>
        </div>
      </div>
    </div>
  );
}

More in 3D