Skip to content

Tilt Card

A 3D card that tilts toward the cursor with spring physics.

Freeglassplayful
Category
Components
Added
2026-08-10
Deps
1
Updated
2026-09-01
Preview

Editorial Card

Tilts toward your cursor with a soft spring.

Props

12
Glare overlay
Requires framer-motion
"use client";

import { useRef } from "react";
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";

export function TiltCard({
  title = "Editorial Card",
  body = "Tilts toward your cursor with a soft spring.",
  maxTilt = 12,
  glare = true,
}: {
  title?: string;
  body?: string;
  maxTilt?: number;
  /** Soft highlight across the upper-left face, as if lit from there. */
  glare?: boolean;
}) {
  const ref = useRef<HTMLDivElement>(null);
  const x = useMotionValue(0);
  const y = useMotionValue(0);
  const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [maxTilt, -maxTilt]), {
    stiffness: 200,
    damping: 20,
  });
  const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-maxTilt, maxTilt]), {
    stiffness: 200,
    damping: 20,
  });

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

  return (
    <div style={{ perspective: 800 }}>
      <motion.div
        ref={ref}
        onMouseMove={handleMove}
        onMouseLeave={() => {
          x.set(0);
          y.set(0);
        }}
        style={{ rotateX, rotateY, transformStyle: "preserve-3d" }}
        className="relative w-64 rounded-2xl border border-black/10 bg-white p-6 shadow-xl"
      >
        {glare && (
          <div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/40 via-transparent to-transparent" />
        )}
        <div className="mb-3 h-8 w-8 rounded-full bg-red-500" />
        <h3 className="mb-1 text-lg font-bold">{title}</h3>
        <p className="text-sm text-black/60">{body}</p>
      </motion.div>
    </div>
  );
}
Notes

About this component

A hover-scale card is the default reach when a card needs to feel interactive, but it doesn't convey depth the way a real 3D tilt toward the cursor does. This tracks pointer position as two motion values, maps them through useTransform into rotation ranges, and runs the result through useSpring so the tilt settles with weight instead of snapping back. The detail that matters when adapting the markup: perspective has to live on a separate parent wrapper, not the same element that carries the 3D transform style and the rotation values — putting both on one element breaks the depth read entirely. Reach for this over a vanilla-tilt.js integration when Framer Motion is already in the project and a second, unrelated tilt dependency isn't worth adding for one card. One thing to note: the glare overlay visible in the live preview is preview-only, controlled by a prop that doesn't exist in the plain exported code, so copying just the code tab won't include it.

Curator’s note

Tuned to feel weighty, not floaty — lower stiffness reads cheap on this kind of card.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

3D Flip Card

components

A card that flips on a real Y-axis rotation to reveal a back face.

cardflip3d
Featured

Magnetic Button

components

A button whose label leans toward the cursor with a springy pull.

buttoncursorspring

A soft blurred blob that trails the cursor with spring-smoothed lag.

cursorblobspring