SnippetUI

Slider de témoignages

Section slider pour afficher un carrousel d'avis d'utilisateurs

HTMLCSSJavaScript

Un curseur de carrousel moderne et fluide utilisant CSS scroll-snap. Il est réactif et permet une utilisation intuitive avec des points et des boutons de navigation.

Customer Reviews

Here are some testimonials from people who have used our service.

"

"Thanks to this product, our workflow has completely transformed. Its intuitive interface and great support team make it perfect for our growing business.”

Sarah Jenkins

Aya Sato

Product Manager
"

"I’ve tried many similar tools, but this one stands out. The attention to design details and smooth operability makes daily tasks enjoyable.”

Michael Chen

Kenichi Tanaka

UX Designer
"

"Seamless integration with existing systems saved us weeks of development time. Extremely robust and scales effortlessly with us.”

Elena Rodriguez

Elena Suzuki

CTO
"

"Customer service is exceptional. Whenever I have a question, they reply instantly with helpful solutions. A true partner in our success.”

David Miller

Daisuke Yamada

Operations Director
<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 mode sombre */
@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' });
  });
});