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.
“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
★★★★★
“Dee listened, then talked me out of the thing I’d have hated. Best cut I’ve had in LA.”
Jordan P.
★★★★★
“They did a strand test first and told me exactly what it would cost. No surprises.”
Lina K.
★★★★★
“Fixed my box-dye disaster in one visit and taught me how to keep it.”
Sam T. · Google review, Sept 2026
★★★★★
“Quiet, unhurried, and on time. I walk out looking like me, only better.”
Ana G.
★★★★★
“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).
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
After one big pull-quote (skill rule: pull-quote + short cards + count), when there are 4+ good reviews with matching photos.
Mobbin upgrade #5: photo-backed quote, "client since" only when the review says it.
Don't
Autoplay (never).
A stock photo implying it is the reviewer's result: use their own work photos.
Trim quotes mid-sentence or rewrite them; bylines are "First L." only.
Reduced motion, no JS, accessibility
Reduced motion: buttons jump instead of gliding.
No JS: a swipeable row of cards (no buttons).
Track is a focusable labelled list; ←/→ move one card; off-screen cards are inert; stars have an aria-label.
Notes
3 cards across on desktop, 2.15 on tablets, 1 + peek on phones.
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&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>