INFINITYUI
HomeComponentsDocsTemplates
Star

Getting Started

  • Introduction
  • Installation
  • CLI
  • Audit

Navigation

  • Spotlight Navbar
  • Glass DockNEW
  • Animated Tab Bar
  • Circle Menu
  • Magnet Tabs
  • Animated Sidebar
  • Apple Spotlight
  • Page TOC RailNEW

Text

  • Flip Text
  • Glitch Text
  • Liquid Text
  • Flip Fade Text
  • Mask Cursor Effect

Cards

  • Glow Border Card
  • Testimonials Card
  • Interactive Book
  • Trading CardsNEW
  • Hover Image
  • Chain of ThoughtNEW
  • Masonry Grid
  • Image Pile
  • Staggered Grid

Inputs

  • AI InputNEW
  • OTP Input
  • Leave Rating

Buttons

  • Social Flip Button
  • Creepy Button

Loaders

  • Jelly Loader
  • Rolling Ball Scroll
  • Glowing Scroll

Backgrounds

  • Light Lines
  • Perspective Grid
  • Liquid Ocean
  • Eagle Vision
  • Flow Scroll
  • Horizontal Scroll

Overlays

  • PersonaNEW
  • Infinite Moving Cards
  • Masked Avatars
  • Stacked Logos
  • Icon Wheel
  • Pixelated CarouselNEW
  • Pixelated Image Trail
  • Flip Scroll
  • Interactive Folder
  • Animated Folder IconNEW
  • Stack Scroll
  • Rubik Cube
CardsNEW

Trading Cards

#card#tilt#holographic
Mythic Rare

Cosmic Phoenix

Fire / Celestial Element

Hover and move mouse over the card to reveal foil effects.

implementedInfinityUI component

This component is fully implemented in InfinityUI and wired into the docs and registry flow.

Installation

Use the registry command to add the component source, and install any package dependencies if needed.

Infinity Registry

Install the component source into your project with the shadcn CLI.

npx shadcn@latest add https://infinityui-pearl.vercel.app/r/trading-cards
Package Dependencies

Install the npm packages used by this component source.

npm install framer-motion

Component Code

Copy and paste this code into your component file.

tsx
"use client";

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

interface TradingCardProps {
    imageSrc: string;
    name: string;
    rarity: string;
    type: string;
}

export function TradingCard({ imageSrc, name, rarity, type }: TradingCardProps) {
    const ref = useRef<HTMLDivElement>(null);
    const [isHovered, setIsHovered] = useState(false);

    const mouseX = useMotionValue(0.5);
    const mouseY = useMotionValue(0.5);

    // Spring physics for smooth tilt
    const smoothMouseX = useSpring(mouseX, { stiffness: 300, damping: 30 });
    const smoothMouseY = useSpring(mouseY, { stiffness: 300, damping: 30 });

    const rotateX = useTransform(smoothMouseY, [0, 1], [15, -15]);
    const rotateY = useTransform(smoothMouseX, [0, 1], [-15, 15]);

    // Glare position based on mouse
    const glareX = useTransform(smoothMouseX, [0, 1], [-100, 200]);
    const glareY = useTransform(smoothMouseY, [0, 1], [-100, 200]);
    const glareOpacity = useTransform(smoothMouseY, [0, 0.5, 1], [0.8, 0, 0.8]);

    const handleMouseMove = (e: React.MouseEvent) => {
        if (!ref.current) return;
        const rect = ref.current.getBoundingClientRect();
        mouseX.set((e.clientX - rect.left) / rect.width);
        mouseY.set((e.clientY - rect.top) / rect.height);
    };

    const handleMouseLeave = () => {
        setIsHovered(false);
        mouseX.set(0.5);
        mouseY.set(0.5);
    };

    return (
        <motion.div
            ref={ref}
            onMouseMove={handleMouseMove}
            onMouseEnter={() => setIsHovered(true)}
            onMouseLeave={handleMouseLeave}
            style={{
                rotateX,
                rotateY,
                transformStyle: "preserve-3d",
            }}
            className="relative w-[280px] aspect-[5/7] rounded-xl overflow-hidden cursor-pointer shadow-2xl transition-transform duration-200"
        >
            {/* Holographic Border / Rim Light */}
            <div className="absolute inset-0 rounded-xl border-2 border-white/20 z-20 pointer-events-none mix-blend-overlay" />

            {/* Background Image */}
            <div
                className="absolute inset-0 bg-zinc-900 bg-cover bg-center z-0 scale-[1.03]"
                style={{ backgroundImage: `url(${imageSrc})` }}
            />

            {/* Vignette Overlay for depth */}
            <div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/20 to-black/40 z-10" />

            {/* Card Content (Translates slightly on hover for 3D separation) */}
            <motion.div
                className="absolute inset-0 z-30 p-6 flex flex-col justify-end"
                style={{ translateZ: isHovered ? 40 : 0 }}
            >
                <div>
                    <span className="px-2 py-0.5 rounded border border-white/20 bg-white/10 backdrop-blur-md text-[10px] text-white font-bold uppercase tracking-widest mb-2 inline-block shadow-lg">
                        {rarity}
                    </span>
                    <h2 className="text-2xl font-black text-white uppercase tracking-tighter drop-shadow-xl">{name}</h2>
                    <p className="text-xs text-zinc-300 font-medium tracking-wide">{type}</p>
                </div>
            </motion.div>

            {/* Dynamic Glare / Holographic Foil Effect */}
            {isHovered && (
                <motion.div
                    className="absolute inset-0 z-40 pointer-events-none mix-blend-overlay"
                    style={{
                        background: `radial-gradient(circle at ${glareX}% ${glareY}%, rgba(255,255,255,0.8), transparent 50%)`,
                        opacity: glareOpacity,
                    }}
                />
            )}

            {/* Rainbow Foil Effect */}
            {isHovered && (
                <motion.div
                    className="absolute inset-0 z-50 pointer-events-none mix-blend-color-dodge opacity-40"
                    style={{
                        background: `linear-gradient(115deg, transparent 20%, rgba(255,0,128,0.5) 30%, rgba(128,0,255,0.5) 40%, rgba(0,255,255,0.5) 50%, transparent 60%)`,
                        backgroundSize: "200% 200%",
                        backgroundPosition: `${glareX}% ${glareY}%`
                    }}
                />
            )}
        </motion.div>
    );
}