Skip to content

Scroll-Snap Testimonial Slider

Horizontally scrolling testimonial cards using CSS scroll snap, with star ratings, avatars and accessible previous and next buttons.

Live preview

Open in new tab

Code

A horizontally scrolling testimonial slider built with native CSS scroll snap and a few lines of vanilla JavaScript for the previous and next buttons. Warm serif quote cards with star ratings and initials avatars make it a good fit for landing pages, pricing pages and case-study sections.

How it works

The root section.ts-slider holds a .ts-head with the heading and the .ts-controls buttons, followed by the ul.ts-track list. Each li.ts-slide contains a figure.ts-card with a .ts-stars rating, a blockquote.ts-quote and a figcaption.ts-author with a .ts-avatar, .ts-name and .ts-role.

The track is a CSS grid with grid-auto-flow: column, so slides line up in one row and overflow horizontally. scroll-snap-type: x mandatory on the track and scroll-snap-align: start on every slide make each swipe, trackpad gesture or arrow-key scroll settle on a card edge. The column width is calculated from --ts-per-view and --ts-gap: a little more than one card on phones, two from 700px and three from 1024px.

On DOMContentLoaded the script initializes every .ts-slider, reveals the hidden .ts-controls and listens for click on .ts-prev and .ts-next. Each click calls scrollBy() with the width of one slide plus the gap. A scroll listener, throttled with requestAnimationFrame, disables the previous button at the start and the next button at the end. Without JavaScript the buttons stay hidden and the track still scrolls and snaps natively.

Customization

  • --ts-per-view on .ts-slider: how many cards are visible; it is set to 2 and 3 in the 700px and 1024px media queries, and fractional values such as 1.15 show a peek of the next card.
  • --ts-gap: space between cards, also used in the column width calculation.
  • --ts-accent: the kicker text, avatar background, decorative quote mark, scrollbar and focus outline.
  • --ts-bg, --ts-card and --ts-dark: section background, light card and dark card colors.
  • Add .ts-card--dark to any figure.ts-card to switch it to the dark brown style.
  • --ts-radius: corner radius of the cards; the bottom-left corner stays nearly square on purpose.
  • Wrap the last star in .ts-star-off and update the aria-label on .ts-stars to show a 4-star rating.

Accessibility

The section is labelled by its heading and has aria-roledescription="carousel". Slides stay a real list, so screen readers announce the number of testimonials. The track has tabindex="0" and an aria-label, so keyboard users can focus it and scroll with the arrow keys. The buttons have aria-label and aria-controls, become disabled at the ends, and focus moves to the other button before one is disabled. Ratings use role="img" with a text label instead of reading out star characters, and avatars are aria-hidden. With prefers-reduced-motion: reduce scrolling jumps instead of animating. Every interactive element has a visible :focus-visible outline.

Usage notes

For several sliders on one page, give each .ts-track a unique id and update aria-controls on its buttons. The quote text uses a serif stack; swap the font-family on .ts-slider for your brand font.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Scroll-Snap Testimonial Slider – Pure CSS testimonials component screenshot
Scroll-Snap Testimonial Slider · Pure CSS Testimonials

More Testimonials

Testimonial Carousel – Bootstrap 5 testimonials preview

Testimonial Carousel

Bootstrap 5 testimonial carousel with serif quote cards, pill indicators, arrow buttons and an accessible pause control.

Testimonials Grid – Tailwind CSS testimonials preview

Testimonials Grid

Masonry-style Tailwind CSS testimonials grid with star ratings, initial avatars and an accessible Show more button.