WBL Code LibraryShowcase · 2.0 KB

Photo review cards

Reviews as photo + quote cards on a native swipe track: the next card peeks, buttons and a "2 / 6" counter on the side, the rating with count on top. Never autoplays.

What clients say

4.9
212 Google reviews

  • Red bob, done at the studio
    ★★★★★
    “I asked for a red that wouldn’t fade orange in a month. Six weeks later it’s still this colour.”
    Maya R. · client since 2019
  • Colour and cut, done at the studio
    ★★★★★
    “Dee listened, then talked me out of the thing I’d have hated. Best cut I’ve had in LA.”
    Jordan P.
  • Pink lengths, done at the studio
    ★★★★★
    “They did a strand test first and told me exactly what it would cost. No surprises.”
    Lina K.
  • Copper, done at the studio
    ★★★★★
    “Fixed my box-dye disaster in one visit and taught me how to keep it.”
    Sam T. · Google review, Sept 2026
  • Dry cut, done at the studio
    ★★★★★
    “Quiet, unhurried, and on time. I walk out looking like me, only better.”
    Ana G.
  • Gray blending, done at the studio
    ★★★★★
    “First salon where nobody pushed me to cover the gray. Maria blended it instead.”
    Ruth L. · client since 2021

Sample reviews and Pexels photos for a fictional salon. On a client site: only their real review text, and photos the review is about (or their own work).

How to use it

Size
2.0 KB gzipped own code (JS 1.0 KB + CSS 1.0 KB; 4.6 KB raw)
Dependency
none (vanilla + native APIs)
Tag
Showcase
Best for
Salons, barbers, nails, med spa, detailers, cake shops: anywhere the result is visual

What it does

A CSS scroll-snap track (so swipe, trackpad and keyboard scrolling are the browser's own, with no jank) of cards with a 4:3 photo, stars, the quote and a byline ("Maya R. · client since 2019"). JS only adds prev/next buttons, the visible-range counter, arrow keys, and hides off-screen cards from screen readers and Tab.

When to use

Don't

Reduced motion, no JS, accessibility

Notes

Copy-ready snippet

<!-- Photo review cards (WBL Code Library) -->
<!-- in <head>: -->
<script>document.documentElement.classList.add('wbl-js')</script>
<link rel="stylesheet" href="/wbl/review-photos/module.css">
<script src="/wbl/review-photos/module.js" defer></script>

<!-- markup: -->
<section class="wbl-rp" data-wbl="review-photos" aria-label="Client reviews">
  <div class="wbl-rp-head">
    <p class="wbl-rp-score"><b>4.9</b><span class="wbl-rp-stars" aria-hidden="true">★★★★★</span><br><a href="https://www.google.com/maps/search/?api=1&amp;query=Juniper+Hair+Studio">212 Google reviews</a></p>
    <div class="wbl-rp-nav"><button type="button" data-rp-prev aria-label="Previous reviews">‹</button><span class="wbl-rp-count" data-rp-count aria-hidden="true">1 / 6</span><button type="button" data-rp-next aria-label="Next reviews">›</button></div>
  </div>
  <ul class="wbl-rp-track" tabindex="0" aria-label="Reviews, scroll sideways">
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/result-bob-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Red bob, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“I asked for a red that wouldn’t fade orange in a month. Six weeks later it’s still this colour.”</blockquote><figcaption><b>Maya R.</b> · client since 2019</figcaption></div></figure></li>
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/color-cut-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Colour and cut, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“Dee listened, then talked me out of the thing I’d have hated. Best cut I’ve had in LA.”</blockquote><figcaption><b>Jordan P.</b></figcaption></div></figure></li>
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/result-pink-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Pink lengths, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“They did a strand test first and told me exactly what it would cost. No surprises.”</blockquote><figcaption><b>Lina K.</b></figcaption></div></figure></li>
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/result-red-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Copper, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“Fixed my box-dye disaster in one visit and taught me how to keep it.”</blockquote><figcaption><b>Sam T.</b> · Google review, Sept 2026</figcaption></div></figure></li>
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/cut-detail-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Dry cut, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“Quiet, unhurried, and on time. I walk out looking like me, only better.”</blockquote><figcaption><b>Ana G.</b></figcaption></div></figure></li>
    <li class="wbl-rp-card"><figure style="display:contents"><img src="../_demo/img/stylist-client-640.jpg" width="640" height="427" loading="lazy" decoding="async" alt="Gray blending, done at the studio">
      <div class="wbl-rp-body"><span class="wbl-rp-stars" role="img" aria-label="5 out of 5 stars">★★★★★</span><blockquote>“First salon where nobody pushed me to cover the gray. Maria blended it instead.”</blockquote><figcaption><b>Ruth L.</b> · client since 2021</figcaption></div></figure></li>
  </ul>
</section>

README.md · module.js · module.css · All modules