DocumentationTrading Card

Trading Card

A 3D interactive trading card with perspective transforms that respond to mouse movement. Features smooth reveal animations and a premium holographic feel.

Install using CLI

npx shadcn@latest add "https://obsidianui.dev/r/trading-card.json"

Install Manually

1

Install dependencies

npm install framer-motion
2

Copy the source code

Copy into components/ui/trading-card.tsx

import { motion, useAnimationControls } from 'framer-motion';
import Image from 'next/image';
import React, { useRef } from 'react';
 
interface TradingCardProps {
  imageUrl: string;
  rank: number;
  name: string;
  description: string;
}
 
const TradingCard: React.FC<TradingCardProps> = ({ imageUrl, rank, name, description }) => {
  const cardRef = useRef<HTMLDivElement>(null);
  const backgroundControls = useAnimationControls();
  const contentControls = useAnimationControls();
 
  const onMouseEnter = () => {
    contentControls.start({ x: -300 });
    backgroundControls.start({ scale: 1.05, opacity: 1 });
  };
 
  const onMouseLeave = () => {
    contentControls.start({ x: 0, transition: { delay: 0.5 } });
    backgroundControls.start({ scale: 0.95, opacity: 0.4 });
  };
 
  const onMouseMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
    if (!cardRef.current) return;
    const rect = cardRef.current.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    const xd = (mx - rect.width / 2) / 10;
    const yd = (rect.height / 2 - my) / 10;
    cardRef.current.style.transform = `perspective(1000px) rotateY(${xd}deg) rotateX(${yd}deg)`;
  };
 
  return (
    <motion.div
      ref={cardRef}
      onMouseMove={onMouseMove}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
      className="flex flex-col hover:scale-105 transition-all items-start justify-end rounded-lg relative shadow-xl overflow-hidden h-[400px] w-[300px] cursor-pointer border border-neutral-800"
    >
      <motion.div
        className="h-full w-full absolute z-0"
        initial={{ opacity: 0.4, scale: 0.95 }}
        animate={backgroundControls}
        transition={{ duration: 0.7, ease: 'backOut' }}
      >
        <Image src={imageUrl} alt={name} fill className="object-cover" />
      </motion.div>
      <div className="font-semibold absolute top-5 right-5 z-10 text-white/70">#{rank}</div>
      <motion.div
        animate={contentControls}
        transition={{ duration: 0.5, ease: 'backOut' }}
        className="p-5 flex flex-col justify-end z-10"
      >
        <div className="text-4xl font-bold">{name}</div>
        <p className="text-sm text-white/90">{description}</p>
      </motion.div>
    </motion.div>
  );
};
 
export default TradingCard;

Props

Prop NameTypeDefaultDescription
imageUrlstring-URL of the card background image
ranknumber-Rank number displayed in corner
namestring-Name/title of the card
descriptionstring-Description text