19 CSS Blockquote Designs

Testimonial Carousel

Three testimonials with dot pagination and arrow keys — auto-advances every 6s, pauses on hover/focus, uses aria-live=polite for screen-reader announcement.

Light JS MIT licensed

Testimonial Carousel the 17th of 19 designs in the 19 CSS Blockquote Designs collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.

Live preview

Open in playground

The code

<figure class="cbq-car" aria-roledescription="carousel" aria-label="Testimonials">
  <div class="cbq-car-track" aria-live="polite">
    <article class="cbq-car-slide is-active">
      <blockquote><p>"It saves me hours every week."</p></blockquote>
      <figcaption>— Aria S., Designer</figcaption>
    </article>
    <article class="cbq-car-slide" hidden>
      <blockquote><p>"Finally, snippets that don't fight my stack."</p></blockquote>
      <figcaption>— Marcus T., DevOps</figcaption>
    </article>
    <article class="cbq-car-slide" hidden>
      <blockquote><p>"My team's dashboard ships twice as fast."</p></blockquote>
      <figcaption>— Priya R., Eng Lead</figcaption>
    </article>
  </div>
  <nav class="cbq-car-dots" aria-label="Slide navigation">
    <button
      type="button"
      class="cbq-car-dot is-active"
      aria-label="Go to slide 1"
      aria-current="true"
    ></button>
    <button type="button" class="cbq-car-dot" aria-label="Go to slide 2"></button>
    <button type="button" class="cbq-car-dot" aria-label="Go to slide 3"></button>
  </nav>
</figure>
.cbq-car {
  max-width: 340px;
  padding: 24px 22px 18px;
  background: #18181f;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 16px;
  font-family: system-ui, sans-serif;
  color: #f0eeff;
}

.cbq-car-track {
  min-height: 90px;
  position: relative;
}

.cbq-car-slide blockquote {
  margin: 0 0 10px;
}

.cbq-car-slide p {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.5;
  font-style: italic;
}

.cbq-car-slide figcaption {
  font-size: 12px;
  color: #a78bfa;
}

.cbq-car-slide[hidden] {
  display: none;
}

.cbq-car-dots {
  display: flex;
  gap: 6px;
  justify-content: center;
  margin-top: 14px;
}

.cbq-car-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition:
    background 0.2s ease,
    transform 0.2s ease;
}

.cbq-car-dot:hover {
  background: rgba(255, 255, 255, 0.32);
}

.cbq-car-dot.is-active {
  background: #7c6cff;
  transform: scale(1.25);
}

.cbq-car-dot:focus-visible {
  outline: 2px solid #a78bfa;
  outline-offset: 2px;
}
(function () {
  document.querySelectorAll(".cbq-car").forEach(function (root) {
    var slides = root.querySelectorAll(".cbq-car-slide");
    var dots = root.querySelectorAll(".cbq-car-dot");
    var idx = 0,
      timer = null;
    function show(i) {
      idx = (i + slides.length) % slides.length;
      slides.forEach(function (s, n) {
        if (n === idx) {
          s.removeAttribute("hidden");
          s.classList.add("is-active");
        } else {
          s.setAttribute("hidden", "");
          s.classList.remove("is-active");
        }
      });
      dots.forEach(function (d, n) {
        var on = n === idx;
        d.classList.toggle("is-active", on);
        if (on) d.setAttribute("aria-current", "true");
        else d.removeAttribute("aria-current");
      });
    }
    function start() {
      stop();
      timer = setInterval(function () {
        show(idx + 1);
      }, 6000);
    }
    function stop() {
      if (timer) {
        clearInterval(timer);
        timer = null;
      }
    }
    dots.forEach(function (d, n) {
      d.addEventListener("click", function () {
        show(n);
        start();
      });
    });
    root.addEventListener("mouseenter", stop);
    root.addEventListener("mouseleave", start);
    root.addEventListener("focusin", stop);
    root.addEventListener("focusout", start);
    root.addEventListener("keydown", function (e) {
      if (e.key === "ArrowRight") {
        e.preventDefault();
        show(idx + 1);
        start();
      }
      if (e.key === "ArrowLeft") {
        e.preventDefault();
        show(idx - 1);
        start();
      }
    });
    if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) start();
  });
})();

Search CodeFronts

Loading…