* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.testimonial-slider-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.testimonial-slider {
max-width: 900px;
width: 100%;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 24px;
padding: 3rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
}
.testimonial-slider::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
}
.testimonial-header {
text-align: center;
margin-bottom: 3rem;
}
.slider-title {
font-size: 2.5rem;
font-weight: 700;
color: white;
margin-bottom: 0.5rem;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.slider-subtitle {
font-size: 1.1rem;
color: rgba(255, 255, 255, 0.8);
font-weight: 400;
}
.testimonial-wrapper {
position: relative;
overflow: hidden;
border-radius: 16px;
margin-bottom: 2rem;
}
.testimonial-track {
display: flex;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.testimonial-slide {
min-width: 100%;
opacity: 0;
transform: scale(0.95);
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.testimonial-slide.active {
opacity: 1;
transform: scale(1);
}
.testimonial-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 20px;
padding: 2.5rem;
position: relative;
min-height: 300px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.quote-icon {
position: absolute;
top: -10px;
left: 20px;
font-size: 4rem;
color: rgba(255, 255, 255, 0.2);
font-family: Georgia, serif;
line-height: 1;
}
.testimonial-content {
position: relative;
z-index: 2;
}
.testimonial-text {
font-size: 1.2rem;
line-height: 1.7;
color: rgba(255, 255, 255, 0.95);
margin-bottom: 2rem;
font-style: italic;
text-align: center;
}
.testimonial-author {
display: flex;
align-items: center;
gap: 1rem;
padding-top: 1.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.author-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
border: 3px solid rgba(255, 255, 255, 0.3);
flex-shrink: 0;
}
.author-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.author-info {
flex: 1;
}
.author-name {
font-size: 1.1rem;
font-weight: 600;
color: white;
margin-bottom: 0.25rem;
}
.author-title {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 0.5rem;
}
.rating {
display: flex;
gap: 0.2rem;
}
.star {
color: #ffd700;
font-size: 1rem;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.testimonial-controls {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.5rem;
}
.control-btn {
width: 50px;
height: 50px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.control-btn:active {
transform: scale(0.95);
}
.control-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.testimonial-dots {
display: flex;
gap: 0.75rem;
align-items: center;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
border: none;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.dot.active {
background: white;
transform: scale(1.2);
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}
.dot:hover {
background: rgba(255, 255, 255, 0.6);
transform: scale(1.1);
}
.autoplay-controls {
display: flex;
justify-content: center;
}
.autoplay-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 25px;
color: white;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.autoplay-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.play-icon {
font-size: 1rem;
}
/* Animación de Carga */
.testimonial-slide.loading {
position: relative;
}
.testimonial-slide.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid white;
border-radius: 50%;
transform: translate(-50%, -50%);
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
/* Animaciones de Deslizamiento */
.testimonial-slide.slide-in-right {
animation: slideInRight 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.testimonial-slide.slide-in-left {
animation: slideInLeft 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.testimonial-slide.slide-out-left {
animation: slideOutLeft 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.testimonial-slide.slide-out-right {
animation: slideOutRight 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes slideInRight {
from {
transform: translateX(100%) scale(0.95);
opacity: 0;
}
to {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes slideInLeft {
from {
transform: translateX(-100%) scale(0.95);
opacity: 0;
}
to {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes slideOutLeft {
from {
transform: translateX(0) scale(1);
opacity: 1;
}
to {
transform: translateX(-100%) scale(0.95);
opacity: 0;
}
}
@keyframes slideOutRight {
from {
transform: translateX(0) scale(1);
opacity: 1;
}
to {
transform: translateX(100%) scale(0.95);
opacity: 0;
}
}
/* Diseño Responsivo */
@media (max-width: 768px) {
.testimonial-slider-container {
padding: 1rem;
}
.testimonial-slider {
padding: 2rem;
}
.slider-title {
font-size: 2rem;
}
.testimonial-card {
padding: 2rem;
min-height: 250px;
}
.testimonial-text {
font-size: 1.1rem;
}
.testimonial-author {
flex-direction: column;
text-align: center;
gap: 0.75rem;
}
.author-avatar {
width: 50px;
height: 50px;
}
.control-btn {
width: 45px;
height: 45px;
}
.testimonial-controls {
flex-direction: column;
gap: 1rem;
}
}
@media (max-width: 480px) {
.testimonial-slider {
padding: 1.5rem;
}
.slider-title {
font-size: 1.75rem;
}
.testimonial-card {
padding: 1.5rem;
}
.testimonial-text {
font-size: 1rem;
}
.quote-icon {
font-size: 3rem;
}
}
/* Soporte para Modo Oscuro */
@media (prefers-color-scheme: dark) {
.testimonial-slider {
background: rgba(0, 0, 0, 0.3);
border-color: rgba(255, 255, 255, 0.1);
}
.testimonial-card {
background: rgba(0, 0, 0, 0.2);
}
}
/* Soporte para Movimiento Reducido */
@media (prefers-reduced-motion: reduce) {
.testimonial-track,
.testimonial-slide,
.control-btn,
.dot,
.autoplay-btn {
transition: none;
}
.testimonial-slide.slide-in-right,
.testimonial-slide.slide-in-left,
.testimonial-slide.slide-out-left,
.testimonial-slide.slide-out-right {
animation: none;
}
}
class TestimonialSlider {
constructor(container) {
this.container = container || document.querySelector('.testimonial-slider');
this.track = this.container.querySelector('.testimonial-track');
this.slides = this.container.querySelectorAll('.testimonial-slide');
this.dots = this.container.querySelectorAll('.dot');
this.prevBtn = this.container.querySelector('.prev-btn');
this.nextBtn = this.container.querySelector('.next-btn');
this.autoplayBtn = this.container.querySelector('.autoplay-btn');
this.playIcon = this.autoplayBtn.querySelector('.play-icon');
this.currentSlide = 0;
this.isAutoplay = true;
this.autoplayInterval = null;
this.autoplayDelay = 5000;
this.isTransitioning = false;
this.touchStartX = 0;
this.touchEndX = 0;
this.init();
}
init() {
this.setupEventListeners();
this.startAutoplay();
this.updateSlider();
this.setupTouchEvents();
this.setupKeyboardNavigation();
}
setupEventListeners() {
// Botones de navegación
this.prevBtn.addEventListener('click', () => this.previousSlide());
this.nextBtn.addEventListener('click', () => this.nextSlide());
// Navegación por puntos
this.dots.forEach((dot, index) => {
dot.addEventListener('click', () => this.goToSlide(index));
});
// Toggle de auto-reproducción
this.autoplayBtn.addEventListener('click', () => this.toggleAutoplay());
// Pausar al pasar el mouse
this.container.addEventListener('mouseenter', () => this.pauseAutoplay());
this.container.addEventListener('mouseleave', () => this.resumeAutoplay());
// API de Visibilidad para rendimiento
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.pauseAutoplay();
} else if (this.isAutoplay) {
this.resumeAutoplay();
}
});
}
setupTouchEvents() {
this.container.addEventListener('touchstart', (e) => {
this.touchStartX = e.changedTouches[0].screenX;
}, { passive: true });
this.container.addEventListener('touchend', (e) => {
this.touchEndX = e.changedTouches[0].screenX;
this.handleSwipe();
}, { passive: true });
}
setupKeyboardNavigation() {
this.container.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
this.previousSlide();
break;
case 'ArrowRight':
e.preventDefault();
this.nextSlide();
break;
case ' ':
e.preventDefault();
this.toggleAutoplay();
break;
case 'Home':
e.preventDefault();
this.goToSlide(0);
break;
case 'End':
e.preventDefault();
this.goToSlide(this.slides.length - 1);
break;
}
});
// Hacer el contenedor enfocable
this.container.setAttribute('tabindex', '0');
}
handleSwipe() {
const swipeThreshold = 50;
const diff = this.touchStartX - this.touchEndX;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
this.nextSlide();
} else {
this.previousSlide();
}
}
}
nextSlide() {
if (this.isTransitioning) return;
const nextIndex = (this.currentSlide + 1) % this.slides.length;
this.goToSlide(nextIndex, 'next');
}
previousSlide() {
if (this.isTransitioning) return;
const prevIndex = this.currentSlide === 0 ? this.slides.length - 1 : this.currentSlide - 1;
this.goToSlide(prevIndex, 'prev');
}
goToSlide(index, direction = 'next') {
if (this.isTransitioning || index === this.currentSlide) return;
this.isTransitioning = true;
const currentSlideEl = this.slides[this.currentSlide];
const nextSlideEl = this.slides[index];
// Agregar clases de animación
if (direction === 'next') {
currentSlideEl.classList.add('slide-out-left');
nextSlideEl.classList.add('slide-in-right');
} else {
currentSlideEl.classList.add('slide-out-right');
nextSlideEl.classList.add('slide-in-left');
}
// Actualizar slide actual
this.currentSlide = index;
// Limpiar después de la animación
setTimeout(() => {
this.updateSlider();
this.cleanupAnimationClasses();
this.isTransitioning = false;
}, 600);
}
updateSlider() {
// Actualizar slide activo
this.slides.forEach((slide, index) => {
slide.classList.toggle('active', index === this.currentSlide);
});
// Actualizar punto activo
this.dots.forEach((dot, index) => {
dot.classList.toggle('active', index === this.currentSlide);
});
// Actualizar botones de navegación
this.updateNavigationButtons();
// Actualizar atributos ARIA
this.updateAriaAttributes();
}
cleanupAnimationClasses() {
this.slides.forEach(slide => {
slide.classList.remove(
'slide-in-left', 'slide-in-right',
'slide-out-left', 'slide-out-right'
);
});
}
updateNavigationButtons() {
// Habilitar/deshabilitar botones basado en posición actual (opcional)
// Para bucle infinito, mantener todos los botones habilitados
this.prevBtn.disabled = false;
this.nextBtn.disabled = false;
}
updateAriaAttributes() {
this.slides.forEach((slide, index) => {
slide.setAttribute('aria-hidden', index !== this.currentSlide);
});
this.dots.forEach((dot, index) => {
dot.setAttribute('aria-pressed', index === this.currentSlide);
});
}
startAutoplay() {
if (!this.isAutoplay) return;
this.autoplayInterval = setInterval(() => {
this.nextSlide();
}, this.autoplayDelay);
}
stopAutoplay() {
if (this.autoplayInterval) {
clearInterval(this.autoplayInterval);
this.autoplayInterval = null;
}
}
pauseAutoplay() {
this.stopAutoplay();
}
resumeAutoplay() {
if (this.isAutoplay) {
this.startAutoplay();
}
}
toggleAutoplay() {
this.isAutoplay = !this.isAutoplay;
if (this.isAutoplay) {
this.startAutoplay();
this.playIcon.textContent = '⏸️';
} else {
this.stopAutoplay();
this.playIcon.textContent = '▶️';
}
}
// Métodos de API pública
getCurrentSlide() {
return this.currentSlide;
}
getTotalSlides() {
return this.slides.length;
}
setAutoplayDelay(delay) {
this.autoplayDelay = delay;
if (this.isAutoplay) {
this.stopAutoplay();
this.startAutoplay();
}
}
addSlide(slideHTML) {
const slideElement = document.createElement('div');
slideElement.className = 'testimonial-slide';
slideElement.innerHTML = slideHTML;
this.track.appendChild(slideElement);
this.slides = this.container.querySelectorAll('.testimonial-slide');
// Agregar punto correspondiente
const dot = document.createElement('button');
dot.className = 'dot';
dot.setAttribute('data-slide', this.slides.length - 1);
dot.addEventListener('click', () => this.goToSlide(this.slides.length - 1));
this.container.querySelector('.testimonial-dots').appendChild(dot);
this.dots = this.container.querySelectorAll('.dot');
}
removeSlide(index) {
if (index >= 0 && index < this.slides.length) {
this.slides[index].remove();
this.dots[index].remove();
this.slides = this.container.querySelectorAll('.testimonial-slide');
this.dots = this.container.querySelectorAll('.dot');
if (this.currentSlide >= this.slides.length) {
this.currentSlide = this.slides.length - 1;
}
this.updateSlider();
}
}
// Monitoreo de rendimiento
getPerformanceMetrics() {
return {
currentSlide: this.currentSlide,
totalSlides: this.slides.length,
isAutoplay: this.isAutoplay,
autoplayDelay: this.autoplayDelay,
isTransitioning: this.isTransitioning
};
}
// Método de limpieza
destroy() {
this.stopAutoplay();
// Remover event listeners
this.prevBtn.removeEventListener('click', this.previousSlide);
this.nextBtn.removeEventListener('click', this.nextSlide);
this.autoplayBtn.removeEventListener('click', this.toggleAutoplay);
this.dots.forEach(dot => {
dot.removeEventListener('click', this.goToSlide);
});
// Limpiar eventos táctiles
this.container.removeEventListener('touchstart', this.handleTouchStart);
this.container.removeEventListener('touchend', this.handleTouchEnd);
// Limpiar eventos de teclado
this.container.removeEventListener('keydown', this.handleKeydown);
}
}
// Función utilitaria para crear testimonios
function createTestimonial(config) {
return `
<div class="testimonial-card">
<div class="quote-icon">"</div>
<div class="testimonial-content">
<p class="testimonial-text">${config.text}</p>
<div class="testimonial-author">
<div class="author-avatar">
<img src="${config.avatar}" alt="${config.name}" />
</div>
<div class="author-info">
<h4 class="author-name">${config.name}</h4>
<p class="author-title">${config.title}</p>
<div class="rating">
${'★'.repeat(config.rating || 5)}
</div>
</div>
</div>
</div>
</div>
`;
}
// Inicializar el slider
document.addEventListener('DOMContentLoaded', () => {
window.testimonialSlider = new TestimonialSlider();
// Ejemplo de agregar un testimonio personalizado
// const customTestimonial = createTestimonial({
// text: "¡Servicio y soporte increíbles!",
// name: "Juana Pérez",
// title: "Gerente de Producto, TechStart",
// avatar: "https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face",
// rating: 5
// });
//
// window.testimonialSlider.addSlide(customTestimonial);
});