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-cardsPackage Dependencies
Install the npm packages used by this component source.
npm install framer-motionComponent 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>
);
}