Hey there, future web dev superstar! 😊
Ever visited a website and seen those cool, sliding boxes showing off happy customer reviews? That’s a testimonials slider, and it’s a super powerful tool for building trust. Today, we’re going to build a beautiful responsive testimonials slider from the ground up.
And the best part? We’re ditching the heavy JavaScript libraries like jQuery or Swiper.js. Why?
- Faster Load Times: No extra files to download means your website loads quicker.
- Deeper Learning: You’ll understand exactly how it works, which is a massive skill boost.
- Total Control: You can customize every single pixel without fighting a library’s limitations.
This project is perfect for beginners who want to get their hands dirty with the fundamentals of HTML, CSS, and “vanilla” JavaScript (that’s just a fancy way of saying plain ol’ JS). Let’s get started!
Table of Contents
The Big Picture: How Does This Slider Actually Work? 🔗
Before we write a single line of code, let’s understand the core idea. It’s surprisingly simple!
Imagine you have a long roll of film with several pictures on it. Now, imagine you’re looking at that film through a small window that’s only big enough to show one picture at a time. To see the next picture, you just slide the entire film roll to the left.
Our slider works the exact same way:
- The “Film Roll”: We’ll have a long horizontal container (
.slide-row
) that holds all of our testimonials side-by-side. This container will be much wider than the screen. - The “Window”: We’ll place that long container inside a parent container (
.slider
) that has a fixed width and, crucially,overflow: hidden
. This property hides everything that sticks out of the “window.” - The “Sliding” Action: We’ll use JavaScript to change the CSS
transform
property of the “film roll” container, moving it left or right to bring the desired testimonial into view through the “window.”
That’s it! It’s a classic and powerful technique you’ll use all the time in web development.
Step 1: Setting Up the HTML Structure (The Skeleton)
First things first, we need a solid HTML structure. This is the backbone of our component.
HTML
<body>
<main>
<h1>Testimonials</h1>
<div class="slider">
<div class="slide-row" id="slide-row">
<div class="slide-col">
<div class="content">
<p>Zen Doan is a business analyst, entrepreneur and media proprietor, and investor. She also known as the best selling book author.</p>
<h2>Zen</h2>
<p>Author</p>
</div>
<div class="hero">
<img src="https://user-images.githubusercontent.com/13468728/234031693-6bbaba7d-632c-4d7d-965f-75a76a549ce2.jpg" alt="Avatar of Zen Doan">
</div>
</div>
<div class="slide-col">
<div class="content">
<p>Jonathan Koletic is an American internet entrepreneur and media proprietor, and investor. He is the founder of the multi-national technology company Treymont.</p>
<h2>Jonathan</h2>
<p>Treymont Inc.</p>
</div>
<div class="hero">
<img src="https://user-images.githubusercontent.com/13468728/234031617-2dfb19ea-01d0-4370-b63b-bb6bdfb4f78e.jpg" alt="Avatar of Jonathan Koletic">
</div>
</div>
<div class="slide-col">
<div class="content">
<p>Charlie Green is an European entrepreneur and media consultant, and investor. He is the founder of the Hallmark Inc.</p>
<h2>Charlie</h2>
<p>Hallmark Inc.</p>
</div>
<div class="hero">
<img src="https://user-images.githubusercontent.com/13468728/234031646-10533999-39e5-4c7b-ab54-d0299b13ce74.jpg" alt="Avatar of Charlie Green">
</div>
</div>
<div class="slide-col">
<div class="content">
<p>Sarah Dam is an American internet entrepreneur and media proprietor, and investor. She is the founder of the multi-national technology company Zara.</p>
<h2>Sarah</h2>
<p>Zara Inc.</p>
</div>
<div class="hero">
<img src="https://github.com/ecemgo/ecemgo/assets/13468728/55116c98-5f9a-4b0a-9fdb-4911b52d5ef3" alt="Avatar of Sarah Dam">
</div>
</div>
</div>
</div>
<div class="indicator">
<span class="btn active"></span>
<span class="btn"></span>
<span class="btn"></span>
<span class="btn"></span>
</div>
</main>
</body>
Breaking it down:
<main>
: A wrapper for our whole component..slider
: This is our “window.” It will have a fixed width and hide the overflow..slide-row
: This is the “film roll.” It will hold all the slides and be very wide. We give it anid
so we can easily grab it with JavaScript..slide-col
: Each individual testimonial slide..content
&.hero
: Inside each slide, we have a div for the text content and another for the person’s image..indicator
: This holds the little navigation dots (our<span>
elements with a class of.btn
) that will allow users to jump between slides.
Step 2: Styling the Slider with CSS (The Visuals)
Now let’s add some style to make it look good! This CSS handles everything from the layout and colors to the slick “glassmorphism” effect on the content cards.
CSS
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap");
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "Poppins", sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
/* Cool gradient background */
background-image: radial-gradient(at 40% 20%, rgb(255, 184, 122) 0px, transparent 50%), radial-gradient(at 80% 0%, rgb(31, 221, 255) 0px, transparent 50%), radial-gradient(at 0% 50%, rgb(255, 219, 222) 0px, transparent 50%), radial-gradient(at 80% 50%, rgb(255, 133, 173) 0px, transparent 50%), radial-gradient(at 0% 100%, rgb(255, 181, 138) 0px, transparent 50%), radial-gradient(at 80% 100%, rgb(107, 102, 255) 0px, transparent 50%), radial-gradient(at 0% 0%, rgb(255, 133, 167) 0px, transparent 50%);
background-repeat: no-repeat;
}
main {
width: 800px; /* This width is key for our JS calculation */
}
main h1 {
text-align: center;
font-size: clamp(2rem, 4vw, 2.6rem);
color: #fff;
margin-bottom: 70px;
}
/* --- The Core Slider Logic --- */
.slider {
width: 100%;
overflow: hidden; /* This is our "window" */
}
.slide-row {
display: flex;
/* Width = 4 slides * 800px each */
width: 3200px;
transition: 0.5s; /* Smooth sliding animation */
}
.slide-col {
position: relative;
width: 800px;
height: 400px;
}
/* --- Content and Image Styling --- */
.hero {
position: absolute;
top: 0;
right: 0;
height: 100%;
}
.hero img {
height: 100%;
border-radius: 10px;
width: 320px;
object-fit: cover;
}
.content {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 520px;
height: 270px;
color: #4d4352;
/* Glassmorphism Effect */
background: rgba(255, 255, 255, 0.7);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(4.5px);
-webkit-backdrop-filter: blur(4.5px);
border-radius: 10px;
padding: 45px;
z-index: 2;
}
/* --- Indicator Styling --- */
.indicator {
display: flex;
justify-content: center;
margin-top: 4rem;
}
.indicator .btn {
display: inline-block;
height: 15px;
width: 15px;
margin: 4px;
border-radius: 15px;
background: #fff;
cursor: pointer;
transition: all 0.5s ease-in-out;
}
.btn.active {
width: 30px; /* Makes the active dot wider */
}
Key CSS Properties Explained:
overflow: hidden
on.slider
: This is the magic property that creates our “window” effect, hiding the other slides.display: flex
on.slide-row
: This lines up all our.slide-col
children horizontally in a row.width: 3200px
on.slide-row
: We have 4 slides, and ourmain
container is800px
wide. So, 4times800px=3200px. This makes our “film roll” long enough to hold all slides.transition: 0.5s
on.slide-row
: This tells the browser to animate any changes to the element’s properties (liketransform
) over half a second, creating a smooth slide instead of an instant jump.backdrop-filter: blur(4.5px)
on.content
: This creates that cool, frosted glass look where the background behind the content box is blurred.
Step 3: Making It Truly Responsive with Media Queries
A slider isn’t much good if it only works on one screen size. Let’s add media queries to ensure it looks great on tablets and mobile phones too.
CSS
/* For tablets and smaller desktops */
@media (max-width: 850px) {
main {
width: 500px;
}
.slide-row {
width: 2000px; /* 4 slides * 500px */
}
.slide-col {
width: 500px;
height: 250px;
}
.hero img {
width: 200px;
}
.content {
width: 320px;
height: 200px;
padding: 20px;
}
/* Adjusting font sizes for smaller screens */
.content p {
font-size: 0.9rem;
}
.content h2 {
font-size: 1.2rem;
margin-top: 20px;
}
}
/* For mobile phones */
@media (max-width: 550px) {
main {
width: 300px;
}
.slide-row {
width: 1200px; /* 4 slides * 300px */
}
.slide-col {
width: 300px;
height: 400px; /* Taller to stack content */
}
/* Stack image below the content card on mobile */
.hero {
top: 55%;
left: 50%;
transform: translateX(-50%);
height: 150px;
z-index: 5;
}
.hero img {
width: 150px;
height: 150px;
border-radius: 50%; /* Make avatar circular */
border: 5px solid white;
}
.content {
width: 300px;
height: 250px;
}
}
Notice how we adjust the width
of the main
container at each breakpoint and then recalculate the total width
of the .slide-row
to match. This is crucial for our JavaScript logic to work correctly on all screen sizes! On the smallest screens, we even change the layout to stack the image below the content for a better mobile experience.
Step 4: Adding Functionality with JavaScript (The Brains)
This is where we bring our slider to life. Our vanilla JavaScript will handle all the logic for moving the slides and updating the active indicator dot.
JavaScript
const btns = document.querySelectorAll(".btn");
const slideRow = document.getElementById("slide-row");
const main = document.querySelector("main");
// This variable will keep track of which slide we are on.
// It starts at 0, which is the first slide.
let currentIndex = 0;
// This is our main function that does all the work!
function updateSlide() {
// 1. Get the current width of the main container.
// We use offsetWidth to get the true width, including padding.
const mainWidth = main.offsetWidth;
// 2. Calculate how much to move the slide-row.
// If we're on slide 0, it's 0 * -width = 0.
// If we're on slide 1, it's 1 * -width = -800px (or -500px, etc.)
const translateValue = currentIndex * -mainWidth;
// 3. Apply the transformation to the slide-row.
// This is what actually moves the slides!
slideRow.style.transform = `translateX(${translateValue}px)`;
// 4. Update the active state on the indicator buttons.
btns.forEach((btn, index) => {
// The toggle method is a clean way to add/remove a class.
// The second argument is a boolean: if true, it adds the class; if false, it removes it.
// 'index === currentIndex' will only be true for the current active button.
btn.classList.toggle("active", index === currentIndex);
});
}
// Loop through each button and add a click event listener.
btns.forEach((btn, index) => {
btn.addEventListener("click", () => {
// When a button is clicked, update the currentIndex to match its index.
currentIndex = index;
// Then, call our main function to update the slider's position.
updateSlide();
});
});
// CRITICAL: Add an event listener for window resize.
// This makes our slider truly responsive.
window.addEventListener("resize", () => {
// When the window size changes, just re-run the update function.
// This will recalculate the widths and reposition the slide perfectly.
updateSlide();
});
The comments in the code explain each step, but the most important part is the window.addEventListener("resize", updateSlide)
. This single line is what makes our slider robust. Without it, if you were to resize your browser, the slider would break because its position would be calculated based on the old window size. By re-running updateSlide()
on resize, we ensure it always looks perfect.
The Final Code and Live Demo
Here is a live, interactive demo on CodePen where you can see the final result and play around with the code yourself.
Recap: The Role of Each Technology
It’s helpful to see how the three languages work together.
Technology | Role in this Project |
HTML | Provides the structure and content for the testimonials and navigation buttons. It’s the skeleton. |
CSS | Handles all the styling, layout, colors, and animations. The overflow and transition properties are key. It’s the clothes and makeup. |
JavaScript | Manages the interactivity and logic. It listens for clicks, calculates the slide positions, and updates the CSS transform property. It’s the brain. |
Further Reading & Resources
Want to dive deeper into the concepts we used? Here are some excellent resources from the Mozilla Developer Network (MDN), which is the gold standard for web documentation.
- MDN Web Docs:
transform
property – Learn more about howtranslateX()
works. - MDN Web Docs:
element.offsetWidth
– Understand how to get the exact width of an element. - MDN Web Docs:
addEventListener()
– The core of handling user events in JavaScript.
And there you have it! A fully functional, beautiful, and responsive testimonials slider built without relying on any external libraries. You now have a powerful new component for your toolkit and a much deeper understanding of how HTML, CSS, and JavaScript work together.
What do you think?
Did you find this tutorial helpful? What other features would you add to this slider, like autoplay or arrow navigation? Let me know in the comments below! 👇
If you liked this post, please consider sharing it with a friend who is also learning to code! 👍