Imagine you're working on a website for a company, and they want to show off what their happy customers have to say. This little section of code creates a neat, sliding testimonial carousel like a slideshow where each "slide" shows a different customer review.
We're going to walk through this in plain English. You don't need to be an expert just a little curious.
Step 1: HTML Structure (The Frame)
The HTML part is like the bones of the page. It's where you lay out what shows up on the screen.
Inside the <head>
section:
- Fonts and icons are loaded. For example:
- One link brings in cool star icons from Font Awesome.
- Another link loads the "Roboto" font from Google, which makes the text look nicer.
- There's also a reference to an external
styles.css
file, which controls the colors, layout, and spacing.
Style.css
:root {
--primary-color: #1976d2;
--primary-light: #e3f2fd;
--text-primary: rgba(0, 0, 0, 0.87);
--text-secondary: rgba(0, 0, 0, 0.6);
--divider: rgba(0, 0, 0, 0.12);
--background: #f5f5f5;
--paper: #ffffff;
--rating-color: #ffb400;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
background-color: var(--background);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: var(--text-primary);
line-height: 1.5;
}
.testimonial-container {
max-width: 900px;
width: 100%;
margin: 0 auto;
text-align: center;
padding: 40px 20px;
}
.section-title {
font-size: 2rem;
font-weight: 500;
margin-bottom: 8px;
color: var(--text-primary);
}
.section-subtitle {
font-size: 1rem;
font-weight: 400;
color: var(--text-secondary);
margin-bottom: 40px;
}
.testimonial-carousel {
position: relative;
overflow: hidden;
margin: 0 auto 40px;
width: 100%;
}
.testimonial-slide {
position: absolute;
width: 100%;
padding: 0 15px;
opacity: 0;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.testimonial-slide.active {
opacity: 1;
position: relative;
}
.testimonial-card {
background-color: var(--paper);
border-radius: 4px;
padding: 32px;
box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2),
0px 1px 1px 0px rgba(0,0,0,0.14),
0px 1px 3px 0px rgba(0,0,0,0.12);
transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.testimonial-card:hover {
box-shadow: 0px 3px 5px -1px rgba(0,0,0,0.2),
0px 5px 8px 0px rgba(0,0,0,0.14),
0px 1px 14px 0px rgba(0,0,0,0.12);
}
.quote-icon {
font-size: 2.5rem;
color: var(--primary-light);
opacity: 0.8;
position: absolute;
top: 16px;
right: 24px;
}
.testimonial-text {
font-size: 1rem;
line-height: 1.75;
color: var(--text-secondary);
margin-bottom: 24px;
text-align: left;
position: relative;
z-index: 1;
font-weight: 400;
}
.client-info {
display: flex;
align-items: center;
text-align: left;
}
.client-img {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
margin-right: 16px;
border: 2px solid var(--primary-light);
}
.client-details {
flex: 1;
}
.client-name {
font-size: 1rem;
font-weight: 500;
color: var(--text-primary);
margin-bottom: 4px;
}
.client-role {
font-size: 0.875rem;
color: var(--text-secondary);
margin-bottom: 8px;
}
.rating {
color: var(--rating-color);
font-size: 0.875rem;
}
.carousel-controls {
display: flex;
justify-content: center;
align-items: center;
margin-top: 24px;
}
.mui-btn {
background-color: transparent;
color: var(--primary-color);
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
font-size: 1rem;
cursor: pointer;
margin: 0 8px;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.mui-btn:hover {
background-color: rgba(25, 118, 210, 0.04);
}
.mui-btn:active {
background-color: rgba(25, 118, 210, 0.12);
}
.indicators {
display: flex;
gap: 8px;
}
.indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--divider);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.indicator.active {
background-color: var(--primary-color);
transform: scale(1.3);
}
/* Responsive design */
@media (max-width: 768px) {
.section-title {
font-size: 1.5rem;
}
.section-subtitle {
font-size: 0.875rem;
}
.testimonial-card {
padding: 24px 16px;
}
.testimonial-text {
font-size: 0.875rem;
}
.client-img {
width: 48px;
height: 48px;
}
}
@media (max-width: 480px) {
.client-info {
flex-direction: column;
text-align: center;
}
.client-img {
margin-right: 0;
margin-bottom: 12px;
}
.testimonial-text {
text-align: center;
}
.client-details {
text-align: center;
}
}
The <body>
has the actual content users see:
We start with a title like:
<h1>Client Testimonials</h1>
<p>Hear what our customers say about us</p>
This introduces the section.
Then comes the main part: a carousel of testimonials. Each testimonial is wrapped in a "slide." Each one shows:
- A quote icon.
- The customer's feedback.
- A photo of the person.
- Their name and job.
- A star rating.
This is repeated three times — for three different people. Think of each one as a card in a slideshow.
Step 2: Carousel Navigation
Below the testimonials, there are left and right arrows:
<button class="prev-btn">←</button>
<button class="next-btn">→</button>
There are also little dots (called indicators). These let you jump to a specific testimonial. One dot is highlighted at a time, depending on which slide you're on.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client Testimonials</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="testimonial-container">
<h1 class="section-title">Client Testimonials</h1>
<p class="section-subtitle">Hear what our customers say about us</p>
<div class="testimonial-carousel">
<div class="testimonial-slide active">
<div class="testimonial-card">
<div class="quote-icon"><i class="fas fa-quote-left"></i></div>
<p class="testimonial-text">This service completely transformed my business. The results were beyond my expectations and the team was incredibly professional throughout the entire process.</p>
<div class="client-info">
<img src="https://randomuser.me/api/portraits/women/32.jpg" alt="Client" class="client-img">
<div class="client-details">
<h4 class="client-name">Sarah Johnson</h4>
<p class="client-role">CEO, TechSolutions</p>
<div class="rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
</div>
</div>
</div>
<div class="testimonial-slide">
<div class="testimonial-card">
<div class="quote-icon"><i class="fas fa-quote-left"></i></div>
<p class="testimonial-text">I've worked with many companies in the past, but none have delivered the level of quality and attention to detail that I experienced here. Highly recommended!</p>
<div class="client-info">
<img src="https://randomuser.me/api/portraits/men/45.jpg" alt="Client" class="client-img">
<div class="client-details">
<h4 class="client-name">Michael Chen</h4>
<p class="client-role">Marketing Director, BrandWorks</p>
<div class="rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
</div>
</div>
</div>
</div>
<div class="testimonial-slide">
<div class="testimonial-card">
<div class="quote-icon"><i class="fas fa-quote-left"></i></div>
<p class="testimonial-text">The customer support is exceptional. They went above and beyond to ensure we were satisfied with the product. Will definitely be using their services again.</p>
<div class="client-info">
<img src="https://randomuser.me/api/portraits/women/68.jpg" alt="Client" class="client-img">
<div class="client-details">
<h4 class="client-name">Emily Rodriguez</h4>
<p class="client-role">Founder, DesignHub</p>
<div class="rating">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="carousel-controls">
<button class="mui-btn prev-btn" aria-label="Previous testimonial">
<i class="fas fa-chevron-left"></i>
</button>
<div class="indicators">
<span class="indicator active"></span>
<span class="indicator"></span>
<span class="indicator"></span>
</div>
<button class="mui-btn next-btn" aria-label="Next testimonial">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Step 3: JavaScript Functionality (Making It Work)
Now comes the interactive part — the JavaScript.
This script runs once the page loads, and it makes everything behave properly.
Here's what it does, step by step:
- Sets up the slides: It finds all the testimonial slides and dots.
- Starts with the first slide active: When you load the page, only the first testimonial is visible.
- Slide navigation:
- If you click the right arrow, it shows the next testimonial.
- If you click the left arrow, it shows the previous one.
- Looping: Once you get to the end, it starts again from the beginning. It's like a loop.
- Keyboard shortcuts:
- Press the right arrow key → next testimonial.
- Press the left arrow key ← previous testimonial.
- Clicking the dots:
- You can also click a dot to go directly to that testimonial.
There's also a nice touch:
The slides move automatically every 8 seconds — like a slideshow on autoplay. But when you hover your mouse over it, it pauses. When your mouse leaves, it resumes. This gives users time to read without rushing.
Script.js
document.addEventListener('DOMContentLoaded', function() {
const slides = document.querySelectorAll('.testimonial-slide');
const indicators = document.querySelectorAll('.indicator');
const prevBtn = document.querySelector('.prev-btn');
const nextBtn = document.querySelector('.next-btn');
let currentSlide = 0;
const slideCount = slides.length;
// Initialize carousel
function initCarousel() {
slides.forEach((slide, index) => {
if (index === 0) {
slide.classList.add('active');
} else {
slide.classList.remove('active');
}
});
indicators.forEach((indicator, index) => {
if (index === 0) {
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
});
}
// Go to specific slide
function goToSlide(n) {
slides[currentSlide].classList.remove('active');
indicators[currentSlide].classList.remove('active');
currentSlide = (n + slideCount) % slideCount;
slides[currentSlide].classList.add('active');
indicators[currentSlide].classList.add('active');
}
// Next slide
function nextSlide() {
goToSlide(currentSlide + 1);
}
// Previous slide
function prevSlide() {
goToSlide(currentSlide - 1);
}
// Event listeners
nextBtn.addEventListener('click', nextSlide);
prevBtn.addEventListener('click', prevSlide);
// Keyboard navigation
document.addEventListener('keydown', function(e) {
if (e.key === 'ArrowRight') {
nextSlide();
} else if (e.key === 'ArrowLeft') {
prevSlide();
}
});
// Indicator click events
indicators.forEach((indicator, index) => {
indicator.addEventListener('click', () => {
goToSlide(index);
});
});
// Auto-rotate slides (optional)
let slideInterval = setInterval(nextSlide, 8000);
// Pause on hover
const carousel = document.querySelector('.testimonial-carousel');
carousel.addEventListener('mouseenter', () => {
clearInterval(slideInterval);
});
carousel.addEventListener('mouseleave', () => {
slideInterval = setInterval(nextSlide, 8000);
});
// Initialize
initCarousel();
// Add animation to cards when they become active
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = 1;
entry.target.style.transform = 'translateY(0)';
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.testimonial-card').forEach(card => {
card.style.opacity = 0;
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1), transform 0.4s cubic-bezier(0.4, 0, 0.2, 1)';
observer.observe(card);
});
});
Step 4: Smooth Animations
There's a small animation added. When a testimonial becomes visible, it fades in and slightly slides up into place. This is done using a thing called Intersection Observer. It detects when something comes into view and then makes it animate.
It's subtle, but it adds a polished feeling.
Why Use a Testimonial Carousel?
If you're wondering why someone would go through all this effort, here's why:
- Customer reviews build trust.
- Instead of showing all reviews at once (which can take up space), this carousel displays them one at a time.
- It looks clean, organized, and interactive — a great impression for visitors.
Summary of the Key Features
- HTML sets up the structure — headings, images, buttons, etc.
- CSS controls the look and feel — colors, fonts, spacing.
- JavaScript adds interactivity — sliding between testimonials, autoplay, pause on hover, animations.
This combination gives you a professional testimonial section that's easy for users to explore.
Ideas to Make It Your Own
If you're building this for yourself or your own website, you could:
- Add more testimonials.
- Use your real customer photos.
- Change the star ratings.
- Adjust the timing (e.g., autoplay every 5 seconds instead of 8).
- Use different icons or font styles to match your brand.
Final Thoughts
This code builds a modern, responsive section where users can read through customer reviews one by one. It works on phones and computers, lets users navigate easily, and even adds nice animations. It's a small detail that makes a big difference in how professional your website looks.
And the best part? You don't need to start from scratch. You can take this code, tweak it a bit, and make it suit your style.
Hope that explanation helped! If you want, I can walk you through how to change the design or even how to connect it to real data later on. 😊