Cards

Tilt Card

A self-contained card that provides a 3D "lift and tilt" effect on hover, complete with a customizable, mouse-tracking aurora glow.

Project Ember
Abstract artwork with warm, flowing colors of orange, red, and gold
Concept Art & Design

Premium Product

A clean and professional card ideal for e-commerce or showcasing premium products. It uses a subtle, monochromatic glow and a prominent drop-shadow on a transparent product image to create a sense of tangible quality.

Aura Wireless
Immersive High-Fidelity Sound
A pair of sleek, modern wireless headphones

SaaS Pricing Tier

Demonstrates a practical, real-world use case for a pricing page. The effect is customized with the theme's primary color to feel branded and persuasive, drawing the user's eye to the most popular plan.

Pro Plan
For growing teams.
Most Popular
$49/ month
  • Unlimited Projects
  • Advanced Analytics
  • 24/7 Priority Support

Creative Portfolio

This variant showcases a more artistic and expressive use of the component. The liftDistance is customized to make the content (the image) the hero element, lifting dramatically higher than the text for a powerful visual statement. The multi-color gradient adds to the creative feel.

Tides
Abstract digital artwork for a portfolio
Ocean's charm

Installation

CLI

Installation via the CLI is coming soon. For now, please follow the manual installation instructions below.

Manual

  1. Install the following dependencies:
npm install gsap @gsap/react
yarn add gsap @gsap/react
pnpm add gsap @gsap/react
  1. Copy and paste the following code into components/ui/tilt-card.tsx:
"use client";

import React, { useRef } from 'react';
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card";

interface TiltCardAnimationProps {
  liftDistance?: [number, number, number];
  rotationIntensity?: number;
  parallaxMultiplier?: number;
  glowGradient?: string;
}

interface TiltCardProps {
  children: React.ReactNode;
  headerContent: React.ReactNode;
  footerContent: React.ReactNode;
  className?: string;
  animationProps?: TiltCardAnimationProps;
}

export function TiltCard({
  children,
  headerContent,
  footerContent,
  className,
  animationProps = {}
}: TiltCardProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const cardRef = useRef<HTMLDivElement>(null);
  const headerRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);
  const footerRef = useRef<HTMLDivElement>(null);
  const glowRef = useRef<HTMLDivElement>(null);

  const {
    liftDistance =,
    rotationIntensity = 8,
    parallaxMultiplier = 5,
    glowGradient = `radial-gradient(400px circle at var(--x) var(--y), var(--primary) 0%, var(--secondary) 50%, transparent 100%)`,
  } = animationProps;

  const isFlat = liftDistance.every(d => d === 0);

  useGSAP(() => {}, { scope: containerRef });

  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!containerRef.current) return;
    const { left, top, width, height } = containerRef.current.getBoundingClientRect();
    const mouseX = e.clientX - left;
    const mouseY = e.clientY - top;

    gsap.to(containerRef.current, {
      duration: 1,
      ease: "power2.out",
      '--x': `${mouseX}px`,
      '--y': `${mouseY}px`,
    });

    const rotateX = gsap.utils.mapRange(0, height, -rotationIntensity, rotationIntensity, mouseY);
    const rotateY = gsap.utils.mapRange(0, width, rotationIntensity, -rotationIntensity, mouseX);
    gsap.to(cardRef.current, { duration: 1, rotateX, rotateY, ease: "power3.out" });

    if (!isFlat) {
      gsap.to([headerRef.current, contentRef.current, footerRef.current], {
        duration: 1,
        x: (i) => (rotateY / rotationIntensity) * liftDistance[i] * 0.1 * parallaxMultiplier,
        y: (i) => (-rotateX / rotationIntensity) * liftDistance[i] * 0.1 * parallaxMultiplier,
        ease: "power3.out",
      });
    }
  };

  const handleMouseEnter = () => {
    gsap.to(glowRef.current, { duration: 0.3, opacity: 1, ease: "power2.out" });
    if (!isFlat) {
      gsap.to([headerRef.current, contentRef.current, footerRef.current], {
        duration: 0.4,
        translateZ: (index) => liftDistance[index],
        stagger: 0.05,
        ease: "power2.out",
      });
    }
  };

  const handleMouseLeave = () => {
    gsap.killTweensOf([cardRef.current, headerRef.current, contentRef.current, footerRef.current, glowRef.current, containerRef.current]);

    gsap.to(glowRef.current, { duration: 0.3, opacity: 0, ease: "power2.out" });
    gsap.to(cardRef.current, { duration: 0.5, rotateX: 0, rotateY: 0, ease: "power3.out" });
    if (!isFlat) {
      gsap.to([headerRef.current, contentRef.current, footerRef.current], {
        duration: 0.5,
        translateZ: 0,
        x: 0,
        y: 0,
        stagger: 0.05,
        ease: "power3.out",
      });
    }
  };

  return (
    <div
      ref={containerRef}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      onMouseMove={handleMouseMove}
      style={{ '--x': '50%', '--y': '50%' } as React.CSSProperties}
      className={`relative [perspective:1000px] ${className}`}
    >
      <Card
        ref={cardRef}
        className="relative z-10 h-full w-full bg-card [transform-style:preserve-3d]"
      >
        <CardHeader ref={headerRef}>{headerContent}</CardHeader>
        <div ref={contentRef}>
          <CardContent>{children}</CardContent>
        </div>
        <CardFooter ref={footerRef}>{footerContent}</CardFooter>
      </Card>

      <div
        ref={glowRef}
        className="pointer-events-none absolute inset-0 rounded-xl opacity-0"
        style={{ background: glowGradient }}
      />
    </div>
  );
}

Usage

Import the component and provide content for the headerContent, footerContent, and children props. Customize the animation with the animationProps object.

import { TiltCard } from "@/components/ui/tilt-card";
import { Button } from "@/components/ui/button";
import { CardTitle, CardDescription } from "@/components/ui/card";
import Image from "next/image";

export default function Example() {
  return (
    <div className="grid grid-cols-1 gap-8 md:grid-cols-2">
      {/* Default Card */}
      <TiltCard
        className="w-full max-w-sm"
        headerContent={<CardTitle>Default Experience</CardTitle>}
        footerContent={<Button className="w-full">Learn More</Button>}
      >
        <p className="text-muted-foreground">
          This card uses the default animation settings.
        </p>
      </TiltCard>

      {/* Customized "Flat" Card */}
      <TiltCard
        className="w-full max-w-sm"
        animationProps={{
          liftDistance:, // Creates the flat effect
          glowGradient: `radial-gradient(circle at 50% 50%, var(--primary) 0%, transparent 70%)`,
        }}
        headerContent={<CardTitle>Flat Card with Glow</CardTitle>}
        footerContent={<Button className="w-full" variant="outline">Select</Button>}
      >
        <p className="text-muted-foreground">
          This card stays flat but gains a static glow on hover.
        </p>
      </TiltCard>
    </div>
  );
}

Props

PropTypeDefaultDescription
childrenReact.ReactNode-The main body content of the card.
headerContentReact.ReactNode-The content for the card's header section.
footerContentReact.ReactNode-The content for the card's footer section.
classNamestring-Optional classes to apply to the main container.
animationPropsTiltCardAnimationProps{}An object to customize the card's animation behaviors.

TiltCardAnimationProps

PropTypeDefaultDescription
liftDistance[number, number, number][60, 40, 20]The distance each layer lifts on the Z-axis. [header, content, footer]
rotationIntensitynumber8The maximum rotation angle in degrees for the main tilt effect.
parallaxMultipliernumber5Multiplier for the parallax positional shift, enhancing the 3D effect.
glowGradientstringradial-gradient(400px circle at var(--x)...)The full CSS gradient string for the interactive glow effect.