WBL Code LibraryShowcase · 2.9 KB

Service picker

Fresha-style menu rows with duration, price and a (+). A running "Your visit" total, and on phones a sticky From $X · Book bar that becomes "2 services · 1 h 45 min · $110".

Services and prices

Tap a row to add it. On a phone the bar at the bottom keeps the total and the Book button in reach while the list is on screen.

Cuts

  • Women's cut + blow-dry1 h · with Maria or Dee
    $65
  • Men's cut30 min · scissor or clipper
    $40
  • Kids cut (under 12)30 min
    $30
  • Fringe trim15 min · between cuts
    $15

Colour

  • Gloss / toner45 min · shine and tone refresh
    $55
  • Root touch-up1 h 15 min · single process
    $85
  • Balayage3 h · price after consult
    from $220
  • Vivid colour3 h · reds, pinks, coppers
    from $180

Finish

  • Blow-dry45 min
    $45
  • Bond repair treatment20 min · add to any colour
    $35
From $1510 services
Book

Sample menu and prices for a fictional salon.

How to use it

Size
2.9 KB gzipped own code (JS 1.5 KB + CSS 1.4 KB; 7.5 KB raw)
Dependency
none (vanilla + native APIs)
Tag
Showcase
Best for
Salons, barbers, nails, med spa, massage, detailers, groomers, any BOOK business with a real price list

What it does

Turns a plain price list (works without JS) into a picker: each row toggles with a 44 px (+) that becomes a check, a summary card on wide screens lists the picks with a total and time, and a bar sticks to the bottom of the picker on phones (inside the section only, so it never fights the page's own sticky bar). The Book link carries ?services=id1,id2, and a wbl:services event fires for a booking widget to pick up.

When to use

Don't

Reduced motion, no JS, accessibility

Notes

Copy-ready snippet

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

<!-- markup: -->
<div class="wbl-sp" data-wbl="service-picker" data-book="https://example.com/book" data-currency="$">
  <div class="wbl-sp-list">
    <h3 class="wbl-sp-cat">Cuts</h3>
    <ul>
      <li class="wbl-sp-item" data-id="cut-w" data-price="65" data-min="60">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Women's cut + blow-dry</span><span class="wbl-sp-meta">1 h · with Maria or Dee</span></div>
        <span class="wbl-sp-pr">$65</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Women's cut + blow-dry, $65</span></button>
      </li>
      <li class="wbl-sp-item" data-id="cut-m" data-price="40" data-min="30">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Men's cut</span><span class="wbl-sp-meta">30 min · scissor or clipper</span></div>
        <span class="wbl-sp-pr">$40</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Men's cut, $40</span></button>
      </li>
      <li class="wbl-sp-item" data-id="cut-k" data-price="30" data-min="30">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Kids cut (under 12)</span><span class="wbl-sp-meta">30 min</span></div>
        <span class="wbl-sp-pr">$30</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Kids cut (under 12), $30</span></button>
      </li>
      <li class="wbl-sp-item" data-id="fringe" data-price="15" data-min="15">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Fringe trim</span><span class="wbl-sp-meta">15 min · between cuts</span></div>
        <span class="wbl-sp-pr">$15</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Fringe trim, $15</span></button>
      </li>
    </ul>
    <h3 class="wbl-sp-cat">Colour</h3>
    <ul>
      <li class="wbl-sp-item" data-id="gloss" data-price="55" data-min="45">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Gloss / toner</span><span class="wbl-sp-meta">45 min · shine and tone refresh</span></div>
        <span class="wbl-sp-pr">$55</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Gloss / toner, $55</span></button>
      </li>
      <li class="wbl-sp-item" data-id="roots" data-price="85" data-min="75">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Root touch-up</span><span class="wbl-sp-meta">1 h 15 min · single process</span></div>
        <span class="wbl-sp-pr">$85</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Root touch-up, $85</span></button>
      </li>
      <li class="wbl-sp-item" data-id="balayage" data-price="220" data-min="180" data-from>
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Balayage</span><span class="wbl-sp-meta">3 h · price after consult</span></div>
        <span class="wbl-sp-pr">from $220</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Balayage, $220</span></button>
      </li>
      <li class="wbl-sp-item" data-id="vivid" data-price="180" data-min="180" data-from>
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Vivid colour</span><span class="wbl-sp-meta">3 h · reds, pinks, coppers</span></div>
        <span class="wbl-sp-pr">from $180</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Vivid colour, $180</span></button>
      </li>
    </ul>
    <h3 class="wbl-sp-cat">Finish</h3>
    <ul>
      <li class="wbl-sp-item" data-id="blow" data-price="45" data-min="45">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Blow-dry</span><span class="wbl-sp-meta">45 min</span></div>
        <span class="wbl-sp-pr">$45</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Blow-dry, $45</span></button>
      </li>
      <li class="wbl-sp-item" data-id="treat" data-price="35" data-min="20">
        <div class="wbl-sp-txt"><span class="wbl-sp-name">Bond repair treatment</span><span class="wbl-sp-meta">20 min · add to any colour</span></div>
        <span class="wbl-sp-pr">$35</span>
        <button class="wbl-sp-add" type="button" aria-pressed="false"><span class="wbl-sp-vh">Add Bond repair treatment, $35</span></button>
      </li>
    </ul>
  </div>
  <aside class="wbl-sp-sum" aria-label="Your visit">
    <h3>Your visit</h3>
    <ol data-sp-lines></ol>
    <p data-sp-total aria-live="polite">Nothing picked yet</p>
    <a class="wbl-sp-book" data-sp-book href="https://example.com/book"><span data-sp-book-label>Book</span></a>
  </aside>
  <div class="wbl-sp-bar">
    <div class="wbl-sp-bar-t"><b data-sp-bar-txt aria-live="polite">From $15</b><span data-sp-bar-sub>10 services</span></div>
    <a class="wbl-sp-book" data-sp-book href="https://example.com/book"><span data-sp-book-label>Book</span></a>
  </div>
</div>

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