Ever wondered how e-commerce sites showcase their products so elegantly? Often, they use a sleek, interactive product carousel. It looks complex, but what if I told you that you could build one yourself? 😊
In this guide, we’ll break down how to create a beautiful and animated product carousel using nothing but HTML, CSS, and a sprinkle of modern JavaScript. It’s a fantastic project for beginners to get comfortable with DOM manipulation and CSS animations.
Ready to build something awesome? Let’s get started!
Table of Contents
What is a Product Carousel?
Our goal today is to build a product carousel that not only switches between images but does so with a stylish, smooth animation involving colored overlays.
Setting Up the Foundation: The HTML Structure
First things first, let’s create the skeleton of our carousel with HTML. This structure is simple but very effective. We need a main container, a place for our images (slides), colored bars (overlays), and the navigation links.
Here’s what the HTML looks like:
HTML
<div class="carousel">
<div class="slides">
<img
src="https://webdevservices.in/cdn/1.png"
alt="Product Carousel Image 1: Stainless Steel"
class="active"
/>
<img
src="https://webdevservices.in/cdn/2.png"
alt="Product Carousel Image 2: Army Green"
/>
<img
src="https://webdevservices.in/cdn/3.png"
alt="Product Carousel Image 3: Cranberry"
/>
<img
src="https://webdevservices.in/cdn/4.png"
alt="Product Carousel Image 4: Midnight Blue"
/>
</div>
<div class="overlays">
<div class="bar" style="--bar-color: #bdc3c7;"></div>
<div class="bar" style="--bar-color: #218c74;"></div>
<div class="bar" style="--bar-color: #dd6b7b;"></div>
<div class="bar" style="--bar-color: #30465c;"></div>
</div>
<ul class="nav-links">
<li><a href="#" class="nav-link">Stainless Steel</a></li>
<li><a href="#" class="nav-link">Army Green</a></li>
<li><a href="#" class="nav-link">Cranberry</a></li>
<li><a href="#" class="nav-link">Midnight Blue</a></li>
</ul>
</div>
Breaking it Down:
<div class="carousel">
: This is our main wrapper that holds everything together.<div class="slides">
: This div contains all our product images (<img>
). We’ll use CSS and JavaScript to show only one at a time. Notice the first image has anactive
class—this tells us it’s the one to display initially.<div class="overlays">
: This holds the coloredbar
divs. These will be used to create a cool wipe animation during transitions. We’re using a CSS custom property (--bar-color
) to set the color for each bar.<ul class="nav-links">
: This is a simple unordered list containing the links that the user will click to switch between slides.
Bringing it to Life: The CSS Styling
Now for the fun part—making it look good! Our CSS will handle the layout, initial visibility, and the basic styles for our navigation links.
We’ll use position: absolute
to stack our images and overlays on top of each other. This is key to making the carousel work.
CSS
/* Importing a nice font from Google Fonts */
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");
body {
display: flex;
justify-content: flex-start;
align-items: center;
min-height: 100vh;
margin: 0;
font-family: Lora, serif;
}
.carousel {
position: relative;
display: flex;
}
/* --- Slides (The Images) --- */
.carousel .slides {
position: relative;
top: -4em;
left: 10em;
}
.carousel .slides img {
position: absolute;
width: 450px;
height: 450px;
opacity: 0; /* All images are hidden by default */
z-index: 999;
}
.carousel .slides img.active {
opacity: 1; /* Only the active image is visible */
}
/* --- Overlays (The Color Bars) --- */
.carousel .overlays {
position: relative;
width: 36em;
height: 18em;
}
.carousel .overlays .bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bar-color);
transform-origin: left; /* Animation will happen from the left */
}
/* --- Navigation --- */
.carousel .nav-links {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin: 0;
padding: 0 0 0 6em;
list-style-type: none;
}
.carousel .nav-links .nav-link {
font-size: 2rem;
line-height: 2;
text-decoration: none;
color: #7f8c8d;
transition: 0.3s;
}
.carousel .nav-links .nav-link:hover {
color: #1c1e1f;
}
.carousel .nav-links .nav-link.active {
color: black;
pointer-events: none; /* Can't click the active link again */
}
Key CSS Concepts Explained:
- Positioning: We set
.carousel
toposition: relative
so we can useposition: absolute
on its children (.slides img
and.overlays .bar
) to place them precisely within the carousel’s boundaries. - Hiding & Showing Slides: All images have
opacity: 0
by default, making them invisible. We only addopacity: 1
to the image that has the.active
class. This is the core mechanism for switching slides. - CSS Custom Properties: Using
var(--bar-color)
allows us to set the background color of each bar directly in the HTML. This is a clean way to manage different styles without writing extra CSS classes. For more on this, check out the MDN guide on CSS Custom Properties. transform-origin: left
: This tells the browser that any transformations (like scaling) on the.bar
elements should start from the left edge. This is crucial for our wipe animation.
Making it Interactive: The JavaScript Magic
This is where we add the brains to our product carousel. The JavaScript will listen for clicks on our navigation links and then orchestrate the animations to switch between slides. We’ll be using the modern Web Animations API, which is a powerful and easy-to-use tool for creating animations in JavaScript. If you want to dive deeper, you can explore it further on the Web Animations API documentation page.
Let’s break down the script, step by step.
Step 1: Selecting Our Elements
First, we need to grab all the HTML elements we want to work with: the navigation links, the image slides, and the overlay bars.
JavaScript
var navLinks = document.querySelectorAll(".carousel .nav-link");
var slides = document.querySelectorAll(".carousel .slides img");
var overlays = document.querySelectorAll(".carousel .bar");
document.querySelectorAll
returns a NodeList
(which is like an array) of all elements that match the given CSS selector.
Step 2: Setting the Initial State
We need to make sure that when the page loads, the first slide and its corresponding navigation link are marked as “active.”
JavaScript
var maxZIndex = navLinks.length; // Used to layer overlays correctly
var easeInOutQuart = "cubic-bezier(0.77, 0, 0.175, 1)";
// Make the first slide and nav link active on page load
slides[0].classList.add("active");
navLinks[0].classList.add("active");
We also define a variable maxZIndex
to help us manage the stacking order (z-index
) of our overlay animations.
Step 3: Adding the Click Events
Next, we need to loop through each navigation link and add an event listener. This listener will wait for a “click” and then run our animation code.
JavaScript
navLinks.forEach(function (navLink, activeIndex) {
// Set the initial z-index for each overlay bar
overlays[activeIndex].style.zIndex = navLinks.length - activeIndex;
navLink.addEventListener("click", function () {
// All our animation logic will go inside here!
});
});
The forEach
loop gives us two important things: the navLink
itself and its activeIndex
(its position in the list, e.g., 0, 1, 2, 3). This index is crucial because it tells us which slide and overlay correspond to the clicked link.
Step 4: Animating the Slides
Inside the click
event listener, the first thing we do is handle the slide transition. This is a two-part process: fade out the old slide, and then fade in the new one.
JavaScript
// ...inside the click event listener
// 1. Update the active class on the nav links
navLinks.forEach(function (navLink) {
navLink.classList.remove("active");
});
navLink.classList.add("active");
// 2. Find the current active slide
var currentSlide = document.querySelector(".carousel .slides img.active");
// 3. Animate the current slide out
var slideFadeOut = currentSlide.animate([
{ transform: "translateX(0)", opacity: 1 },
{ transform: "translateX(5%)", opacity: 0 }
], {
duration: 600,
easing: "ease-in",
fill: "forwards"
});
// 4. When the fade-out is finished, animate the new slide in
slideFadeOut.onfinish = function () {
slides.forEach(function (slide) {
slide.classList.remove("active");
});
var activeSlide = slides[activeIndex];
activeSlide.classList.add("active");
activeSlide.animate([
{ transform: "translateX(-5%)", opacity: 0 },
{ transform: "translateX(0)", opacity: 1 }
], {
duration: 600,
easing: "ease-out",
fill: "forwards"
});
};
- We first update the
.active
class on the nav links to give the user immediate visual feedback. - We use the
.animate()
method. It takes two arguments: an array of keyframes (the start and end states of the animation) and an options object (likeduration
andeasing
). - The
fill: "forwards"
option is important—it tells the browser to keep the styles of the final keyframe after the animation finishes. - We use the
.onfinish
event to create a sequence. The new slide only starts animating after the old one has completely faded out. This prevents them from overlapping awkwardly.
For a great beginner’s guide on manipulating the DOM, you can check out our article on Introduction to JavaScript DOM Manipulation.
Step 5: Animating the Overlays
Finally, let’s add the colorful overlay wipe. This animation runs at the same time as the slide transition to create a polished effect.
JavaScript
// ...inside the click event listener, after the slide animation logic
maxZIndex += 1; // Increment z-index to ensure the new overlay is on top
var activeOverlay = overlays[activeIndex];
activeOverlay.style.zIndex = maxZIndex;
activeOverlay.animate(
[{ transform: "scaleX(0)" }, { transform: "scaleX(1)" }],
{
duration: 1200,
fill: "forwards",
easing: easeInOutQuart
}
);
Here, we animate the transform: scaleX()
property from 0 (invisible) to 1 (fully visible). Because we set transform-origin: left
in our CSS, this creates a smooth wipe effect from left to right. We also update the z-index
to make sure the newest overlay always appears on top of the previous ones.
The Complete Code
Here is all the code put together so you can easily copy and paste it into your own project files.
HTML
HTML
<div class="carousel">
<div class="slides">
<img src="https://webdevservices.in/cdn/1.png" alt="Stainless Steel" />
<img src="https://webdevservices.in/cdn/2.png" alt="Army Green" />
<img src="https://webdevservices.in/cdn/3.png" alt="Cranberry" />
<img src="https://webdevservices.in/cdn/4.png" alt="Midnight Blue" />
</div>
<div class="overlays">
<div class="bar" style="--bar-color: #bdc3c7;"></div>
<div class="bar" style="--bar-color: #218c74;"></div>
<div class="bar" style="--bar-color: #dd6b7b;"></div>
<div class="bar" style="--bar-color: #30465c;"></div>
</div>
<ul class="nav-links">
<li><a href="#" class="nav-link">Stainless Steel</a></li>
<li><a href="#" class="nav-link">Army Green</a></li>
<li><a href="#" class="nav-link">Cranberry</a></li>
<li><a href="#" class="nav-link">Midnight Blue</a></li>
</ul>
</div>
CSS
CSS
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");
body {
display: flex;
justify-content: flex-start;
align-items: center;
min-height: 100vh;
margin: 0;
}
.carousel {
position: relative;
display: flex;
}
.carousel .slides {
position: relative;
top: -4em;
left: 10em;
}
.carousel .slides img {
position: absolute;
width: 450px;
height: 450px;
opacity: 0;
z-index: 999;
}
.carousel .slides img.active {
opacity: 1;
}
.carousel .overlays {
position: relative;
width: 36em;
height: 18em;
}
.carousel .overlays .bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--bar-color);
transform-origin: left;
}
.carousel .nav-links {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin: 0;
padding: 0 0 0 6em;
list-style-type: none;
}
.carousel .nav-links .nav-link {
font-size: 2rem;
font-family: Lora, serif;
line-height: 2;
text-decoration: none;
color: #7f8c8d;
transition: 0.3s;
}
.carousel .nav-links .nav-link:hover {
color: #1c1e1f;
}
.carousel .nav-links .nav-link.active {
color: black;
pointer-events: none;
}
JavaScript
JavaScript
var navLinks = document.querySelectorAll(".carousel .nav-link");
var slides = document.querySelectorAll(".carousel .slides img");
var overlays = document.querySelectorAll(".carousel .bar");
var maxZIndex = navLinks.length;
var easeInOutQuart = "cubic-bezier(0.77, 0, 0.175, 1)";
slides[0].classList.add("active");
navLinks[0].classList.add("active");
navLinks.forEach(function (navLink, activeIndex) {
overlays[activeIndex].style.zIndex = navLinks.length - activeIndex;
navLink.addEventListener("click", function () {
// nav-link
navLinks.forEach(function (navLink) { return navLink.classList.remove("active"); });
navLink.classList.add("active");
// slide
var currentSlide = document.querySelector(".carousel .slides img.active");
var slideFadeOut = currentSlide.animate([
{ transform: "translateX(0)", opacity: 1 },
{ transform: "translateX(5%)", opacity: 0 }
], {
duration: 600,
easing: "ease-in",
fill: "forwards"
});
slideFadeOut.onfinish = function () {
slides.forEach(function (slide) { return slide.classList.remove("active"); });
var activeSlide = slides[activeIndex];
activeSlide.classList.add("active");
activeSlide.animate([
{ transform: "translateX(-5%)", opacity: 0 },
{ transform: "translateX(0)", opacity: 1 }
], { duration: 600, easing: "ease-out", fill: "forwards" });
};
// overlay
maxZIndex += 1;
var activeOverlay = overlays[activeIndex];
activeOverlay.style.zIndex = maxZIndex;
activeOverlay.animate(
[{ transform: "scaleX(0)" }, { transform: "scaleX(1)" }],
{ duration: 1200, fill: "forwards", easing: easeInOutQuart }
);
});
});
Demo: Codepen
Conclusion
And there you have it! A fully functional, beautifully animated product carousel built from scratch. You’ve learned how to structure content with HTML, layer and style it with CSS, and orchestrate complex animations with the JavaScript Web Animations API.
This project is a perfect stepping stone. You can now try to expand it by adding arrow buttons for navigation, making it autoplay, or adapting it for a touch-screen device. Keep experimenting and building!
Did you find this tutorial helpful? Give it a share! If you have any questions or built your own version, drop a comment below. I’d love to see it!