Slider de Testimonios
Sección deslizante para mostrar un carrusel de reseñas de usuarios
HTMLCSSJavaScript
Un control deslizante de carrusel moderno y fluido que utiliza CSS “scroll-snap”. Es responsivo y permite una operación intuitiva con puntos y botones de navegación.
Customer Reviews
Here are some testimonials from people who have used our service.
<div class="testimonial-section">
<div class="testimonial-header">
<h2>Customer Reviews</h2>
<p>Here are some testimonials from people who have used our service.</p>
</div>
<div class="slider-wrapper">
<button class="slider-btn prev" aria-label="Previous review">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="testimonial-slider" id="testimonial-slider">
{/* Testimonial 1 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">"Thanks to this product, our workflow has completely transformed. Its intuitive interface and great support team make it perfect for our growing business."</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah" alt="Sarah Jenkins" class="avatar" />
<div>
<h4 class="author-name">Aya Sato</h4>
<span class="author-role">Product Manager</span>
</div>
</div>
</div>
{/* Testimonial 2 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">"I've tried many similar tools, but this one stands out. The attention to design details and smooth operability makes daily tasks enjoyable."</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Michael" alt="Michael Chen" class="avatar" />
<div>
<h4 class="author-name">Kenichi Tanaka</h4>
<span class="author-role">UX Designer</span>
</div>
</div>
</div>
{/* Testimonial 3 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">"Seamless integration with existing systems saved us weeks of development time. Extremely robust and scales effortlessly with us."</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Elena" alt="Elena Rodriguez" class="avatar" />
<div>
<h4 class="author-name">Elena Suzuki</h4>
<span class="author-role">CTO</span>
</div>
</div>
</div>
{/* Testimonial 4 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">"Customer service is exceptional. Whenever I have a question, they reply instantly with helpful solutions. A true partner in our success."</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=David" alt="David Miller" class="avatar" />
<div>
<h4 class="author-name">Daisuke Yamada</h4>
<span class="author-role">Operations Director</span>
</div>
</div>
</div>
</div>
<button class="slider-btn next" aria-label="Next review">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
<div class="slider-dots" id="slider-dots"></div>
</div>
.testimonial-section {
max-width: 1000px;
margin: 0 auto;
padding: 4rem 2rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: #1a1a2e;
}
.testimonial-header {
text-align: center;
margin-bottom: 3rem;
}
.testimonial-header h2 {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.testimonial-header p {
color: #64748b;
font-size: 1.1rem;
}
.slider-wrapper {
position: relative;
display: flex;
align-items: center;
gap: 1rem;
}
.testimonial-slider {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
gap: 2rem;
padding: 1rem 0;
/* Hide scrollbar */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.testimonial-slider::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
.testimonial-card {
flex: 0 0 100%;
scroll-snap-align: center;
background: #ffffff;
border-radius: 24px;
padding: 2.5rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(255, 255, 255, 0.8);
display: flex;
flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
@media (min-width: 768px) {
.testimonial-card {
flex: 0 0 calc(50% - 1rem); /* 2-column display */
}
}
.testimonial-card:hover {
transform: translateY(-5px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.12);
}
.quote-icon {
font-size: 4rem;
line-height: 1;
color: #e2e8f0;
margin-bottom: -1rem;
font-family: serif;
}
.review {
font-size: 1.125rem;
line-height: 1.7;
color: #334155;
flex-grow: 1;
margin-bottom: 2rem;
font-style: italic;
}
.author-info {
display: flex;
align-items: center;
gap: 1rem;
border-top: 1px solid #f1f5f9;
padding-top: 1.5rem;
}
.avatar {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #e2e8f0;
}
.author-name {
margin: 0 0 0.25rem 0;
font-weight: 700;
font-size: 1.1rem;
}
.author-role {
color: #64748b;
font-size: 0.9rem;
}
.slider-btn {
background: #ffffff;
border: none;
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
color: #4facfe;
transition: all 0.2s ease;
flex-shrink: 0;
z-index: 10;
}
.slider-btn:hover {
background: #4facfe;
color: #ffffff;
transform: scale(1.05);
}
.slider-btn:active {
transform: scale(0.95);
}
.slider-dots {
display: flex;
justify-content: center;
gap: 0.75rem;
margin-top: 2rem;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #cbd5e1;
border: none;
cursor: pointer;
padding: 0;
transition: all 0.3s ease;
}
.dot.active {
background: #4facfe;
width: 24px;
border-radius: 12px;
}
/* Compatible con el modo oscuro */
@media (prefers-color-scheme: dark) {
.testimonial-section {
color: #f8fafc;
}
.testimonial-header p {
color: #94a3b8;
}
.testimonial-card {
background: #1e293b;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
border-color: rgba(255, 255, 255, 0.05);
}
.quote-icon {
color: #334155;
}
.review {
color: #cbd5e1;
}
.author-info {
border-top-color: #334155;
}
.slider-btn {
background: #1e293b;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.slider-btn:hover {
background: #4facfe;
}
.dot {
background: #475569;
}
}
document.addEventListener('DOMContentLoaded', () => {
const slider = document.getElementById('testimonial-slider');
const dotsContainer = document.getElementById('slider-dots');
const prevBtn = document.querySelector('.slider-btn.prev');
const nextBtn = document.querySelector('.slider-btn.next');
if (!slider) return;
const cards = slider.querySelectorAll('.testimonial-card');
const cardCount = cards.length;
// Get the number of visible cards based on screen width
const getVisibleCards = () => window.innerWidth >= 768 ? 2 : 1;
// Generate and update dots
const updateDots = () => {
dotsContainer.innerHTML = '';
const visibleCards = getVisibleCards();
const dotCount = Math.max(1, cardCount - visibleCards + 1);
for (let i = 0; i < dotCount; i++) {
const dot = document.createElement('button');
dot.classList.add('dot');
if (i === 0) dot.classList.add('active');
dot.setAttribute('aria-label', `Go to slide ${i + 1}`);
dot.addEventListener('click', () => scrollToSlide(i));
dotsContainer.appendChild(dot);
}
};
updateDots();
// Recalculate on resize
window.addEventListener('resize', () => {
updateDots();
updateActiveDot();
});
// Scroll to specified slide
const scrollToSlide = (index) => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollTo({
left: index * (cardWidth + gap),
behavior: 'smooth'
});
};
// Update active dot based on scroll position
const updateActiveDot = () => {
const dots = dotsContainer.querySelectorAll('.dot');
if (!dots.length) return;
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
const scrollPosition = slider.scrollLeft;
// Calculate current index
const activeIndex = Math.round(scrollPosition / (cardWidth + gap));
dots.forEach((dot, index) => {
dot.classList.toggle('active', index === activeIndex);
});
};
// Link to scroll event
slider.addEventListener('scroll', updateActiveDot);
// Scroll control with buttons
prevBtn.addEventListener('click', () => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollBy({ left: -(cardWidth + gap), behavior: 'smooth' });
});
nextBtn.addEventListener('click', () => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollBy({ left: (cardWidth + gap), behavior: 'smooth' });
});
});