Building a Testimonial Slider with Touchpad and Mouse Support: Part 1 - HTML Structure
Published by: AT Studio
Published:
Summary: In the past, building a slider meant wrestling with heaps of JavaScript. Now, thanks to modern CSS features like @starting-style, scroll-snap-type, scroll-snap-align, scroll-state, and @container-query, plus the SnapEvent interface, we can create a smooth slider that works with touchpad navigation right out of the box. Add a bit of JavaScript, and you’ve got mouse support too. In this first part of a series, we focus on the HTML structure, leveraging semantic elements. Future posts will cover the CSS styling and JavaScript functionality.
In this Article:
Related Topics
The demo:
See the Pen Swiper-Slider: Scroll, Drag, and Click to Navigate by AT Studio (@AtStudio) on CodePen.
HTML
<swiper-slider>
  <article>
    <ul class="slider">
      <li>
        <figure>
          <blockquote>
            <p>"This isn't just about staying relevant in a rapidly evolving industry; it's about leading the way."
            </p>
          </blockquote>
          <figcaption>
            <p>Kenny Demaio</p>
            <p>SVP of Operations</p>
            <a href="">See the story
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" fill="none">
                <path d="M3.333 8.167h9.334m0 0L10 10.833m2.667-2.666L10 5.5" stroke="#fff" stroke-width="1.5"
                  stroke-linecap="round" stroke-linejoin="round">
              </svg>
            </a>
          </figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <blockquote>
            <p>"We can be way more creative in what we're putting out into the world"
            </p>
          </blockquote>
          <figcaption>
            <p>Rudolf Lewis</p>
            <p>SVP of Operations</p>
            <a href="">See the story
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" fill="none">
                <path d="M3.333 8.167h9.334m0 0L10 10.833m2.667-2.666L10 5.5" stroke="#fff" stroke-width="1.5"
                  stroke-linecap="round" stroke-linejoin="round">
              </svg>
            </a>
          </figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <blockquote>
            <p>"Crafter's brand and voice tools help our teams work even better together. We're able to align faster and collaborate more effectively."
            </p>
          </blockquote>
          <figcaption>
            <p>John Bean</p>
            <p>Sr. Manager, Campaign Strategy</p>
            <a href="">See the story
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="17" fill="none">
                <path d="M3.333 8.167h9.334m0 0L10 10.833m2.667-2.666L10 5.5" stroke="#fff" stroke-width="1.5"
                  stroke-linecap="round" stroke-linejoin="round">
              </svg>
            </a>
          </figcaption>
        </figure>
      </li>
    </ul>
    <ul class="img_container">
      <li>
        <img src="https://atstudioadmin.netlify.app/uploads/kenny.avif" alt="Photo of Kenny Demaio">
      </li>
      <li>
        <img src="https://atstudioadmin.netlify.app/uploads/rudolf.avif" alt="Photo of Rudolf Lewis">
      </li>
      <li>
        <img src="https://atstudioadmin.netlify.app/uploads/john.avif" alt="Photo of John Bean">
      </li>
    </ul>
    <menu class="pagination">
      <li>
        <button active></button>
      </li>
      <li>
        <button></button>
      </li>
      <li>
        <button></button>
      </li>
    </menu>
  </article>
</swiper-slider>
Testimonial sliders are a familiar design pattern. Each slide features a quote, the person who said it, and pagination buttons so users can flip through the slides.
Why swiper-slider wraps article?
The <swiper-slider> wrapper is there to leverage @container-query. Here’s the catch: you can’t apply @container-query styles directly to the element defined as the container. So, I added this extra layer to style the <article> responsively.
Custom element vs. div
Instead of using <div class="swiper-slider">, I went with a custom element, <swiper-slider>. Both are generic containers and behave the same for screen readers without ARIA attributes. But <swiper-slider> is cleaner and more intuitive for other developers. Plus, when styling, a type selector (like swiper-slider) has lower specificity than a class selector (.swiper-slider), which keeps things flexible.
Why article element?
The <article> element represents a complete, or self-contained, composition in a document, page, application, or site and that is, in principle, independently distributable or reusable, e.g. in syndication. The swiper slider is a resuable component, therefore, the entire component is a good candidate to be wrapped in an <article> element.
Structuring the content
nside the slider, I prioritized the quote, since that is the most important information for the user who is checking out the testimonial section, follwed by the image, and finally the pagination for the user to switch between slides.
Quoting right
John Rhea has a detailed article about Quoting in HTML. I wrapped the quote in <blockquote>, tucked the person’s name and job title in <figcaption> (since they’re not part of the quote itself), and used <figure> to tie the quote to its attribution. One thing to note: I avoided <cite> for the name and title because it’s meant for titles of works (like books or articles), not people. The HTML spec has a great example of this with <blockquote> and <figure> - notice how “Wonder and Skepticism” uses , but “Carl Sagan” doesn’t.
Image placement
Ideally, the images would live inside <figure> since they’re part of the self-contained unit. But for this design, I had to move them into a separate container. If you’ve got a clever workaround, I’d love to hear it! Drop me an email or ping me on Bluesky at @alexdevvv.bsky.social.
ul vs. menu
What's the difference?
According to MDN
The
<menu>and<ul>elements both represent an unordered list of items. The key difference is that<ul>primarily contains items for display, while<menu>represents a toolbar containing commands that the user can perform or activate.
The HTML specs:
The menu element is simply a semantic alternative to ul to express an unordered list of commands (a "toolbar").
<ul> is for unordered lists of items meant for display, while <menu> is for interactive toolbars with commands. That’s why I used <menu> for the pagination buttons — they’re interactive controls. For the slider content and image list, I stuck with <ul> since they’re just displaying items.
That wraps up the HTML part of the slider. In the next post, we’ll tackle the CSS. Stay tuned!