Stackbits
Cards > Trading Card

🕹️ Trading Card

Level up your UI game with our interactive Trading Card component! Featuring rank, name, and character description, this card isn't just for show—it comes alive with a 3D hover effect that adds depth and style

Preview

#1
Lionel
Messi

Unstoppable force, unrivaled skill - Messi's legacy is built on precision, perseverance, and a relentless drive to redefine greatness on the field.

Follow below steps 👇🏻

Install dependencies

1
npm i framer-motion

Component

Create a file trading-card.tsx in your components folder and paste this code

1
import { motion, useAnimationControls } from 'framer-motion';
2
import React, { useRef } from 'react';
3
4
interface TradingCardProps {
5
illustration: React.ReactNode;
6
rank?: number;
7
name: string;
8
description: string;
9
}
10
11
const TradingCard: React.FC<TradingCardProps> = ({ illustration, rank, name, description }) => {
12
const cardRef = useRef<HTMLDivElement>(null);
13
14
const backgroundControls = useAnimationControls();
15
const contentControls = useAnimationControls();
16
const cardControls = useAnimationControls();
17
18
const onMouseEnter: React.MouseEventHandler<HTMLDivElement> = (e) => {
19
contentControls.start({ x: -300 });
20
backgroundControls.start({ scale: 1.05, opacity: 1 });
21
};
22
23
const onMouseLeave = () => {
24
contentControls.start({ x: 0, transition: { delay: 0.5 } });
25
backgroundControls.start({ scale: 0.95, opacity: 0.4 });
26
cardControls.start({ transform: `rotateY(0deg) rotateX(0deg)` });
27
};
28
29
const onMouseMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
30
if (!cardRef.current) return;
31
32
const rect = cardRef.current?.getBoundingClientRect();
33
if (!rect) return;
34
35
const mx = e.clientX - rect.left;
36
const my = e.clientY - rect.top;
37
38
const width = rect.right - rect.left;
39
const height = rect.bottom - rect.top;
40
41
const xd = (mx - width / 2) / 10;
42
const yd = (height / 2 - my) / 10;
43
44
cardRef.current.style.transform = `perspective(1000px) rotateY(${xd}deg) rotateX(${yd}deg)`;
45
};
46
47
return (
48
<motion.div
49
ref={cardRef}
50
onMouseMove={onMouseMove}
51
onMouseEnter={onMouseEnter}
52
onMouseLeave={onMouseLeave}
53
initial={{
54
transform: 'perspective(1000px) rotateX(0deg) rotateY(0deg)'
55
}}
56
style={{
57
transformStyle: 'preserve-3d'
58
}}
59
animate={cardControls}
60
className="flex flex-col hover:scale-105 transition-all duration-200 ease-linear items-start justify-end rounded-lg relative shadow-xl overflow-hidden h-[400px] w-[300px] cursor-pointer border-[1px] border-neutral-800"
61
>
62
<motion.div
63
className="h-full w-full absolute z-0"
64
initial={{
65
opacity: 0.4,
66
scale: 0.95
67
}}
68
animate={backgroundControls}
69
transition={{ duration: 0.7, ease: 'backOut' }}
70
>
71
{illustration}
72
</motion.div>
73
{rank && <div className="font-semibold absolute top-5 right-5 z-10">#{rank}</div>}
74
<motion.div
75
animate={contentControls}
76
transition={{ duration: 0.5, ease: 'backOut' }}
77
className="p-5 h-full w-full flex flex-col justify-end bg-transparent z-10 opacity-90"
78
>
79
<div className="font-medium">
80
{name.split(' ').map((word) => {
81
return (
82
<div
83
key={word}
84
className="flex flex-col items-start justify-start text-4xl font-bold"
85
>
86
{word} <br />
87
</div>
88
);
89
})}
90
</div>
91
<p className="text-sm">{description}</p>
92
</motion.div>
93
</motion.div>
94
);
95
};
96
97
export default TradingCard;

Usage

1
<TradingCard
2
illustration={
3
<div
4
className="h-full w-full inset-0 bg-cover bg-center"
5
style={{
6
backgroundImage: 'url(https://i.pinimg.com/736x/9c/fa/15/9cfa15fab5013f15b472f91450be5f01.jpg)'
7
}}
8
></div>
9
}
10
rank={1}
11
name="Lionel Messi"
12
description="Unstoppable force, unrivaled skill - Messi's legacy is built on precision, perseverance, and a relentless drive to redefine greatness on the field."
13
/>

⭐️ Got a question or feedback?
Feel free to reach out!