# Photo review cards (`review-photos`)

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.

| | |
|---|---|
| Tag | Showcase |
| Size | 2.0 KB gzipped own code (JS 1.0 KB + CSS 1.0 KB; 4.6 KB raw) |
| Dependency | none (vanilla + native APIs) |
| Best for | Salons, barbers, nails, med spa, detailers, cake shops: anywhere the result is visual |
| Demo | `index.html` in this folder (live: https://wbl-code-lib.pages.dev/modules/review-photos/) |

## 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.

## Relation to the effects libraries
Batch 1's review-carousel is text cards; this adds the photo, the rating header and visible-range counter.

## Use it
Copy this folder to the site as `/wbl/review-photos/` (keep `vendor/` if there is one), then:

```html
<!-- 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>
```

Colours come from CSS variables with safe defaults: `--wbl-accent`, `--wbl-on-accent`, `--wbl-ink`, `--wbl-paper`,
`--wbl-card`, `--wbl-line`, `--wbl-muted`, `--wbl-band`, `--wbl-radius`, `--wbl-head` (see the library README for the
one-line bridge to the preview builder's `--a / --ink / --bg …`).

## License
Our code: WBL-owned, original. No third-party code.
