Learn step by step how to create a 3D product card using HTML, CSS, and JavaScript. Easy guide with code examples for beginners.
Table of Contents
In today’s digital world, websites need attractive designs that keep users engaged. One such design is a 3D product card. A product card is a small section on a website that displays product details such as image, price, features, and an “Add to Cart” button.
By adding 3D tilt effects and shadows, the card looks more realistic and interactive. In this blog, we will learn step by step how to create a 3D product card using HTML, CSS, and JavaScript.
Prerequisites
Before starting, make sure you have:
- Basic knowledge of HTML, CSS, and JavaScript
- A simple code editor (like VS Code)
- A browser (Google Chrome or any other modern browser)
Source Code
Step 1 (HTML Code):
To get started, we need to create a basic HTML file. This file will contain the main structure of our 3D Product Card. Let’s go step by step and understand what the HTML code does:
1. HTML Structure
<!DOCTYPE html>
<html lang="en">
- Declares the document type (HTML5).
- lang="en" sets the page language to English.
2. Head Section
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- charset="UTF-8" → supports all characters (including emojis).
- viewport → makes it responsive on mobile devices.
<title>3D Product Card</title>
- The title shown in the browser tab.
<script src="https://cdn.tailwindcss.com"></script>
- Loads Tailwind CSS (utility-first CSS framework).
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
- Imports the Inter font from Google Fonts.
<script src="https://unpkg.com/lucide@latest"></script>
- Loads Lucide icons (used for the small feature icons).
<link rel="stylesheet" href="styles.css">
- Links an external CSS file (for extra effects like 3D shadow or animations).
3. Body Section
<body class="min-h-screen flex items-center justify-center p-4">
- min-h-screen → full height of screen.
- flex items-center justify-center → centers content both vertically & horizontally.
- p-4 → padding around the body.
4. Main Card
<div class="card-container w-full max-w-sm mx-auto">
<div class="card relative w-full h-auto shadow-2xl shadow-rose-100 p-8">
- max-w-sm → card has a fixed max width (small screen).
- mx-auto → centers horizontally.
- shadow-2xl shadow-rose-100 → big shadow with a rose tint.
- p-8 → padding inside the card.
- relative → allows positioned child elements.
Extra elements inside:
<div class="sheen-light"></div>
<div class="product-shadow"></div>
- 3D lighting and shadow effects (defined in styles.css).
5. Product Image
<img src="..." alt="Elegant Headphone" class="w-full h-auto object-cover rounded-2xl drop-shadow-[0_30px_30px_rgba(0,0,0,0.15)]">
- Loads a sneaker image.
- rounded-2xl → smooth corners.
- drop-shadow → realistic floating shadow effect.
6. Card Content
<h1 class="text-3xl font-bold mb-1 text-stone-800">StrideX Shoes</h1>
<p class="text-sm mb-6">Next-gen shoes with lightweight design...</p>
- Product name (large, bold).
- Description (small text).
7. Details Section
<div class="details-section mb-6 space-y-4">
Organized info about the product:
Color Options
<h3 class="text-xs font-semibold text-stone-500 mb-2">COLOR</h3>
<div class="flex items-center space-x-2">
<button class="w-6 h-6 rounded-full bg-stone-700 ring-2 ring-offset-2 ring-stone-700"></button>
<button class="w-6 h-6 rounded-full bg-rose-200 hover:ring-2 ring-offset-2 ring-rose-300"></button>
<button class="w-6 h-6 rounded-full bg-slate-200 hover:ring-2 ring-offset-2 ring-slate-300"></button>
</div>
- Small color circles (buttons) represent shoe color choices.
- First color has an active ring-2.
- Others add a ring only when hovered.
Features
<h3 class="text-xs font-semibold text-stone-500 mb-2">FEATURES</h3>
<div class="flex items-center space-x-4 text-stone-600">
<div class="flex items-center space-x-1">
<i data-lucide="smile" class="w-4 h-4"></i>
<span class="text-xs">All-Day Comfort</span>
</div>
<div class="flex items-center space-x-1">
<i data-lucide="footprints" class="w-4 h-4"></i>
<span class="text-xs">Flexible Sole</span>
</div>
</div>
- Lists product features with icons (comfort, flexible sole).
- data-lucide="..." → inserts Lucide icons dynamically.
8. Price & Add to Cart
<div class="flex items-center justify-between">
<div class="price-container">
<span class="text-3xl font-bold text-rose-500">$189</span>
</div>
<button class="add-to-cart-btn bg-rose-400 text-white font-semibold py-3 px-6 rounded-xl shadow-lg shadow-rose-200 hover:bg-rose-500 hover:shadow-rose-300">
Add to Cart
</button>
</div>
- Shows price in bold pink.
- Stylish Add to Cart button with hover effects.
9. Scripts
<script src="script.js"></script>
- External JavaScript file (handles animations, 3D tilt effect, and Lucide icon rendering).
Step 2 (CSS Code):
Once the basic HTML structure of the 3D Product Card is ready, the next step is to style the card using CSS. Let’s go through the CSS code step by step:
1. Base Styles
body {
font-family: 'Inter', sans-serif;
background-color: #f8f4f1;
}
- Uses the Inter font (Google font imported earlier).
- Background color = light beige (#f8f4f1).
2. Card Container
.card-container {
perspective: 2000px;
}
- Adds 3D perspective → makes child elements look 3D when rotated.
- Bigger number = less distortion, smaller number = more dramatic depth.
3. Card Styling
.card {
position: relative;
transform-style: preserve-d;
transition: transform 0.1s ease;
transform: rotateY(var(--rotateX)) rotateX(var(--rotateY));
background: rgba(255, 255, 255, 0.4); /* Frosted glass effect */
backdrop-filter: blur(25px);
border: 1px solid rgba(0, 0, 0, 1);
border-radius: 24px;
}
- transform-style: preserve-3d (looks like a typo here: written preserve-d) → ensures child elements maintain 3D depth.
- transform: rotateY(...) rotateX(...) → rotates card dynamically using CSS variables (--rotateX, --rotateY), usually set in JS.
- Frosted glass look:
- background: rgba(255, 255, 255, 0.4) → transparent white.
- backdrop-filter: blur(25px) → blurs background behind the card.
- border-radius: 24px → rounded corners.
4. Soft Sheen (Light Reflection Effect)
.sheen-light {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: radial-gradient(
circle at var(--light-x) var(--light-y),
rgba(224, 173, 173, 0.3),
transparent 40%
);
border-radius: 24px;
opacity: 0;
transition: opacity 0.3s ease-out;
pointer-events: none;
}
- Creates a radial light spot (gradient circle) on hover.
- Position of light controlled by variables (--light-x, --light-y).
- opacity: 0 → invisible at first. Becomes visible when JS updates it.
5. Product Image (Floating Effect)
.product-image {
transform: translateZ(100px) scale(0.95);
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
- Moves the image forward in 3D space (translateZ(100px) = closer to user).
- scale(0.95) → slightly smaller initially.
- Smooth animation curve defined by cubic-bezier(...).
6. Product Shadow
.product-shadow {
position: absolute;
top: 10%; left: 10%;
width: 80%; height: 80%;
background: radial-gradient(
circle,
rgba(100, 80, 80, 0.15) 0%,
rgba(0, 0, 0, 0) 60%
);
transform: translateZ(1px) rotateX(90deg) translateY(-100px)
translateX(var(--shadow-x)) translateY(var(--shadow-y));
filter: blur(15px);
opacity: 0.8;
transition: transform 0.1s ease, opacity 0.3s ease;
pointer-events: none;
}
- Fake shadow beneath the shoe image.
- Uses radial gradient (dark center fading to transparent).
- Positioned in 3D (rotateX(90deg)) → looks like a shadow on the ground.
- Moves dynamically using variables (--shadow-x, --shadow-y).
- Blurred edges (filter: blur(15px)).
7. Hover Effects
.card-container:hover .product-image {
transform: translateZ(140px) scale(1);
}
.card-container:hover .product-shadow {
opacity: 1;
}
- On hover:
- Product floats higher (translateZ(140px)) and grows to normal size (scale(1)).
- Shadow becomes darker (opacity: 1).
8. Depth for Card Content
.card-content h1 { transform: translateZ(70px); }
.card-content p { transform: translateZ(50px); }
.details-section { transform: translateZ(80px); }
.price-container { transform: translateZ(90px); }
.add-to-cart-btn {
transform: translateZ(100px);
transition: transform 0.2s, box-shadow 0.2s;
}
.add-to-cart-btn:hover {
transform: translateZ(110px) scale(1.05);
}
- Each element (title, description, details, price, button) is placed at different depths in 3D space.
- This creates a layered parallax effect when the card tilts.
- The button pops out more on hover (scale(1.05) = slightly bigger).
body {
font-family: 'Inter', sans-serif;
background-color: #f8f4f1;
}
.card-container {
perspective: 2000px;
}
.card {
position: relative;
transform-style: preserve-d;
transition: transform 0.1s ease;
transform: rotateY(var(--rotateX)) rotateX(var(--rotateY));
background: rgba(255, 255, 255, 0.4); /* Frosted glass effect */
backdrop-filter: blur(25px);
border: 1px solid rgba(0, 0, 0, 1);
border-radius: 24px;
}
/* Soft sheen effect */
.sheen-light {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
circle at var(--light-x) var(--light-y),
rgba(224, 173, 173, 0.3),
transparent 40%
);
border-radius: 24px;
opacity: 0;
transition: opacity 0.3s ease-out;
pointer-events: none;
}
/* The main product image that floats high above */
.product-image {
transform: translateZ(100px) scale(0.95);
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* The dynamic shadow cast by the product image */
.product-shadow {
position: absolute;
top: 10%;
left: 10%;
width: 80%;
height: 80%;
background: radial-gradient(
circle,
rgba(100, 80, 80, 0.15) 0%,
rgba(0, 0, 0, 0) 60%
);
transform: translateZ(1px) rotateX(90deg) translateY(-100px)
translateX(var(--shadow-x)) translateY(var(--shadow-y));
filter: blur(15px);
opacity: 0.8;
transition: transform 0.1s ease, opacity 0.3s ease;
pointer-events: none;
}
.card-container:hover .product-image {
transform: translateZ(140px) scale(1);
}
.card-container:hover .product-shadow {
opacity: 1;
}
/* Applying depth to other content */
.card-content h1 {
transform: translateZ(70px);
}
.card-content p {
transform: translateZ(50px);
}
.details-section {
transform: translateZ(80px);
}
.price-container {
transform: translateZ(90px);
}
.add-to-cart-btn {
transform: translateZ(100px);
transition: transform 0.2s, box-shadow 0.2s;
}
.add-to-cart-btn:hover {
transform: translateZ(110px) scale(1.05);
} Step 3 (JavaScript Code):
Finally, we need to create a function in JavaScript. Let’s go through the code line by line to understand what it does.
1. Initialize Lucide Icons
lucide.createIcons();
- This scans the DOM for elements like
<i data-lucide="smile">and replaces them with actual SVG icons from the Lucide library.
2. Select Elements
const cardContainer = document.querySelector('.card-container');
const card = document.querySelector('.card');
const light = document.querySelector('.sheen-light');
const shadow = document.querySelector('.product-shadow');
const productImage = document.querySelector('.product-image');
- Finds key elements from the HTML so we can manipulate them later:
- cardContainer → whole wrapper (listens for mouse events).
- card → the frosted-glass product card.
- light → shiny reflection effect.
- shadow → fake shadow under the shoe.
- productImage → the floating shoe image.
3. Tilt Strength
const TILT_AMOUNT = 12; // Slightly less tilt for a softer feel
- Defines how much the card tilts when you move your mouse.
- Larger number = more dramatic 3D tilt.
4. Mouse Move Effect
cardContainer.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
- Gets the card’s bounding box (size & position).
- Finds mouse position (x, y) relative to the card itself (not the whole screen).
- Calculates the center point of the card (centerX, centerY).
const rotateX = ((x - centerX) / centerX) * -TILT_AMOUNT;
const rotateY = ((y - centerY) / centerY) * TILT_AMOUNT;
- Figures out how much to tilt based on mouse offset:
- If the mouse is on the left side, the card tilts left.
- If the mouse is at the top, the card tilts upward.
card.style.setProperty('--rotateX', `${rotateX}deg`);
card.style.setProperty('--rotateY', `${rotateY}deg`);
- Updates CSS variables (--rotateX, --rotateY) in real-time.
- CSS uses these variables to rotate the card in 3D.
5. Light Position (Sheen Effect)
light.style.setProperty('--light-x', `${x}px`);
light.style.setProperty('--light-y', `${y}px`);
- Moves the radial light spot to follow the mouse.
- Creates the illusion of shiny reflection moving across the surface.
6. Shadow Movement
const shadowX = rotateX * -1.5;
const shadowY = rotateY * -1.5;
shadow.style.setProperty('--shadow-x', `${shadowX}px`);
shadow.style.setProperty('--shadow-y', `${shadowY}px`);
- Shadow shifts opposite the tilt direction.
- If the card tilts right, the shadow moves left.
- If the card tilts up, the shadow moves down.
- Makes the shadow look more realistic.
7. Mouse Enter
cardContainer.addEventListener('mouseenter', () => {
card.style.transition = 'transform 0.1s ease-out';
light.style.opacity = '1';
});
- When the mouse enters the card:
- Card transitions smoothly (ease-out).
- The sheen light becomes visible (opacity = 1).
8. Mouse Leave
cardContainer.addEventListener('mouseleave', () => {
card.style.transition = 'transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1)';
light.style.opacity = '0';
card.style.setProperty('--rotateX', '0deg');
card.style.setProperty('--rotateY', '0deg');
});
- When the mouse leaves the card:
- Card slowly eases back to normal (using a spring-like cubic-bezier curve).
- Sheen light fades out.
- Card rotation resets to straight position (no tilt).
lucide.createIcons();
const cardContainer = document.querySelector('.card-container');
const card = document.querySelector('.card');
const light = document.querySelector('.sheen-light');
const shadow = document.querySelector('.product-shadow');
const productImage = document.querySelector('.product-image');
const TILT_AMOUNT = 12; // Slightly less tilt for a softer feel
cardContainer.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = ((x - centerX) / centerX) * -TILT_AMOUNT;
const rotateY = ((y - centerY) / centerY) * TILT_AMOUNT;
card.style.setProperty('--rotateX', `${rotateX}deg`);
card.style.setProperty('--rotateY', `${rotateY}deg`);
light.style.setProperty('--light-x', `${x}px`);
light.style.setProperty('--light-y', `${y}px`);
const shadowX = rotateX * -1.5;
const shadowY = rotateY * -1.5;
shadow.style.setProperty('--shadow-x', `${shadowX}px`);
shadow.style.setProperty('--shadow-y', `${shadowY}px`);
});
cardContainer.addEventListener('mouseenter', () => {
card.style.transition = 'transform 0.1s ease-out';
light.style.opacity = '1';
});
cardContainer.addEventListener('mouseleave', () => {
card.style.transition = 'transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1)';
light.style.opacity = '0';
card.style.setProperty('--rotateX', '0deg');
card.style.setProperty('--rotateY', '0deg');
});Final Output:
Conclusion:
Creating a 3D product card using HTML, CSS, and JavaScript is simple yet powerful. With just a few lines of code, you can make your website look more stylish and professional. This type of design is perfect for e-commerce websites, portfolio showcases, or modern landing pages. By following this guide, you now have the skills to build your own interactive product cards and take your web design to the next level.That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊


