Open demo

CSS code

HTML
<div><span class="lbl">List and map that select each other, with search</span>
<section class="sl" data-locator aria-labelledby="sl-title">
  <div class="sl-grid">
    <div>
      <h2 id="sl-title">Find a Kaffebar</h2>
      <p class="sl-sub">Seven cafés across Copenhagen. Pick one from the list or the map.</p>
      <form class="sl-search" role="search"><label for="sl-q">Search by area or street</label><div class="sl-field"><svg class="sl-icon" viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input id="sl-q" type="search" placeholder="e.g. Vesterbro" autocomplete="off" data-locator-search></div></form>
      <p class="sl-count" data-locator-count aria-live="polite">7 of 7 cafés</p>
      <ul class="sl-list">
        <li class="sl-row" data-store-item="nb"><button class="sl-item" type="button" data-store="nb" aria-pressed="false"><span class="sl-num" aria-hidden="true">1</span><strong>Kaffebar Nørrebro</strong><small>Jægersborggade 41, 2200 København N</small><small>Mon–Sun 7:00–18:00 · Roastery bar</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6918,12.5440" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="ob"><button class="sl-item" type="button" data-store="ob" aria-pressed="false"><span class="sl-num" aria-hidden="true">2</span><strong>Kaffebar Østerbro</strong><small>Nordre Frihavnsgade 31, 2100 København Ø</small><small>Mon–Fri 7:00–18:00 · Pastry counter</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6990,12.5800" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="fb"><button class="sl-item" type="button" data-store="fb" aria-pressed="false"><span class="sl-num" aria-hidden="true">3</span><strong>Kaffebar Frederiksberg</strong><small>Værnedamsvej 9, 1819 Frederiksberg C</small><small>Mon–Sun 7:30–18:00 · Step-free entrance</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6735,12.5420" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="ib"><button class="sl-item" type="button" data-store="ib" aria-pressed="true"><span class="sl-num" aria-hidden="true">4</span><strong>Kaffebar Indre By</strong><small>Kronprinsensgade 6, 1114 København K</small><small>Mon–Sat 8:00–18:00 · Brew bar and beans</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6800,12.5790" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="ch"><button class="sl-item" type="button" data-store="ch" aria-pressed="false"><span class="sl-num" aria-hidden="true">5</span><strong>Kaffebar Christianshavn</strong><small>Overgaden oven Vandet 10, 1415 København K</small><small>Mon–Sun 8:00–17:00 · Canal-side seats</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6730,12.5935" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="vb"><button class="sl-item" type="button" data-store="vb" aria-pressed="false"><span class="sl-num" aria-hidden="true">6</span><strong>Kaffebar Vesterbro</strong><small>Istedgade 88, 1650 København V</small><small>Mon–Sun 7:00–19:00 · Open latest</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6695,12.5560" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
        <li class="sl-row" data-store-item="is"><button class="sl-item" type="button" data-store="is" aria-pressed="false"><span class="sl-num" aria-hidden="true">7</span><strong>Kaffebar Islands Brygge</strong><small>Islands Brygge 25, 2300 København S</small><small>Mon–Sun 8:00–18:00 · Harbour terrace</small></button>
          <p class="sl-meta"><a href="https://www.google.com/maps/dir/?api=1&destination=55.6640,12.5770" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span>Opens Google Maps</span></p></li>
      </ul>
      <p class="sl-empty" data-locator-empty hidden>No café matches that search. Try an area such as Nørrebro.</p>
    </div>
    <div>
      <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
      <g fill="#57534e" font-family="inherit" font-size="11" font-weight="700" letter-spacing="1.4" text-anchor="middle">
        <text x="120" y="70">NØRREBRO</text><text x="405" y="122">ØSTERBRO</text><text x="370" y="268">INDRE BY</text><text x="528" y="306">CHRISTIANSHAVN</text>
        <text x="205" y="372">VESTERBRO</text><text x="80" y="194">FREDERIKSBERG</text><text x="470" y="410">ISLANDS BRYGGE</text>
      </g>
    </svg>
      <button class="sl-pin" type="button" data-pin data-store="nb" data-label="Nørrebro" aria-pressed="false" aria-label="Kaffebar Nørrebro" style="left:21.7%;top:28.4%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">1</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ob" data-label="Østerbro" aria-pressed="false" aria-label="Kaffebar Østerbro" style="left:65.8%;top:19.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">2</span></button>
      <button class="sl-pin" type="button" data-pin data-store="fb" data-label="Frederiksberg" aria-pressed="false" aria-label="Kaffebar Frederiksberg" style="left:15.3%;top:55.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">3</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ib" data-label="Indre By" aria-pressed="true" aria-label="Kaffebar Indre By" style="left:63.3%;top:51.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">4</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ch" data-label="Christianshavn" aria-pressed="false" aria-label="Kaffebar Christianshavn" style="left:83.7%;top:62.7%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">5</span></button>
      <button class="sl-pin" type="button" data-pin data-store="vb" data-label="Vesterbro" aria-pressed="false" aria-label="Kaffebar Vesterbro" style="left:35.0%;top:73.3%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">6</span></button>
      <button class="sl-pin" type="button" data-pin data-store="is" data-label="Islands Brygge" aria-pressed="false" aria-label="Kaffebar Islands Brygge" style="left:68.3%;top:87.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">7</span></button>
      </div>
      <p class="sl-note">Illustrated map, not to scale. Directions open your maps app.</p>
    </div>
  </div>
</section></div>
<div><span class="lbl">Pick-up point chooser with a mini map (CSS :has, no JavaScript)</span>
<section class="sl pp" aria-label="Pick-up point">
  <fieldset>
    <legend>Choose a pick-up point</legend>
    <label class="pp-opt"><input type="radio" name="pp" id="pp-p1" value="p1" checked><strong>Parcel locker, Sankt Hans Torv</strong><small>350 m · open 24 hours</small></label>
    <label class="pp-opt"><input type="radio" name="pp" id="pp-p2" value="p2"><strong>Kiosk Elmegade</strong><small>600 m · 7:00–23:00</small></label>
    <label class="pp-opt"><input type="radio" name="pp" id="pp-p3" value="p3"><strong>Nørrebro Cykler</strong><small>900 m · 9:00–18:00</small></label>
    <label class="pp-opt"><input type="radio" name="pp" id="pp-p4" value="p4"><strong>Post office, Nørrebrogade</strong><small>1.2 km · 10:00–17:30</small></label>
  </fieldset>
  <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
    </svg>
      <span class="sl-dot" data-dot="p1" style="left:34%;top:30%"></span>
      <span class="sl-dot" data-dot="p2" style="left:52%;top:44%"></span>
      <span class="sl-dot" data-dot="p3" style="left:24%;top:62%"></span>
      <span class="sl-dot" data-dot="p4" style="left:68%;top:70%"></span>
      <span class="sl-you" style="left:42%;top:52%"></span>
  </div>
</section></div>
CSS
/* Store locator: an illustrated map (inline SVG, not to scale) with pins positioned in percent of the frame. */
.sl-frame{position:relative;aspect-ratio:4/3;border-radius:16px;overflow:hidden;background:#f2eee6}
.sl-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.sl-pin{--pin:#b45309;position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:var(--pin);cursor:pointer;
  transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s,color .18s}
.sl-pin svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(28,25,23,.3))}
.sl-pin span{position:absolute;top:6px;left:0;right:0;text-align:center;color:#fff;font-size:.8rem;font-weight:800;line-height:20px}
.sl-pin:hover{transform:translate(-50%,-100%) scale(1.08)}
.sl-pin[aria-pressed="true"]{--pin:#1c1917;z-index:2;transform:translate(-50%,-100%) scale(1.2)}
.sl-pin[aria-pressed="true"]::after{content:attr(data-label);position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);
  padding:4px 9px;border-radius:8px;background:#1c1917;color:#fff;font-size:.7rem;font-weight:700;white-space:nowrap}
.sl-pin:focus-visible{outline:3px solid #1c1917;outline-offset:2px;border-radius:8px}
.sl-pin[hidden]{display:none}
.sl-dot{position:absolute;width:18px;height:18px;border-radius:50%;background:#78716c;border:3px solid #fff;box-shadow:0 2px 5px rgba(28,25,23,.35);transform:translate(-50%,-50%);transition:transform .18s,background-color .18s}
.sl-you{position:absolute;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 6px rgba(37,99,235,.25);transform:translate(-50%,-50%)}
@media (prefers-reduced-motion:reduce){.sl-pin,.sl-dot{transition:none}}
.sl{--sl-ink:#1c1917;--sl-muted:#57534e;--sl-line:#e7e5e4;--sl-accent:#b45309;background:#fff;border:1px solid var(--sl-line);border-radius:22px;padding:24px;color:var(--sl-ink)}
.sl-grid{display:grid;gap:22px}
@media (min-width:880px){.sl-grid{grid-template-columns:350px minmax(0,1fr);align-items:start}}
.sl h2{font-size:1.35rem;letter-spacing:-.02em;line-height:1.2}
.sl-sub{margin-top:4px;color:var(--sl-muted);font-size:.92rem}
.sl-search{margin-top:16px}
.sl-search label{display:block;font-size:.82rem;font-weight:700;margin-bottom:6px}
.sl-search input{width:100%;font:inherit;font-size:.95rem;padding:11px 14px 11px 40px;border:1px solid #a8a29e;border-radius:12px;background:#fafaf9;color:inherit}
.sl-field{position:relative}
.sl-field svg{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:#57534e;pointer-events:none}
.sl-search input:focus-visible{outline:3px solid var(--sl-accent);outline-offset:1px;border-color:var(--sl-accent)}
.sl-count{margin-top:10px;font-size:.8rem;color:var(--sl-muted)}
.sl-list{list-style:none;margin-top:10px;display:grid;gap:8px;max-height:430px;overflow:auto;padding:2px 4px 2px 2px}
.sl-list li[hidden]{display:none}
.sl-item{display:grid;grid-template-columns:30px 1fr;gap:2px 12px;width:100%;padding:12px 14px;border:1px solid var(--sl-line);border-radius:14px;background:#fff;
  font:inherit;color:inherit;text-align:left;cursor:pointer;transition:border-color .15s,background-color .15s}
.sl-item:hover{border-color:#d6b08a;background:#fffbf5}
.sl-item[aria-pressed="true"]{border-color:#1c1917;background:#fafaf9;box-shadow:inset 0 0 0 1px #1c1917}
.sl-item:focus-visible{outline:3px solid var(--sl-accent);outline-offset:2px}
.sl-num{grid-row:span 3;width:28px;height:28px;border-radius:50%;background:var(--sl-accent);color:#fff;display:grid;place-items:center;font-size:.8rem;font-weight:800}
.sl-item[aria-pressed="true"] .sl-num{background:#1c1917}
.sl-item strong{font-size:.98rem}
.sl-item small{font-size:.83rem;color:var(--sl-muted)}
.sl-meta{display:flex;flex-wrap:wrap;gap:4px 12px;padding:0 14px 0 56px;margin-top:-6px;font-size:.8rem}
.sl-meta a{color:#9a3412;font-weight:700;text-underline-offset:3px;border-radius:4px}
.sl-meta a:focus-visible{outline:3px solid var(--sl-accent);outline-offset:2px}
.sl-meta span{color:var(--sl-muted)}
.sl-row{display:grid;gap:8px}
.sl-empty{padding:16px;border:1px dashed #a8a29e;border-radius:14px;font-size:.9rem;color:var(--sl-muted)}
.sl-empty[hidden]{display:none}
.sl-note{margin-top:10px;font-size:.75rem;color:var(--sl-muted)}
.pp{display:grid;gap:20px}
@media (min-width:760px){.pp{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start}}
.pp fieldset{border:0;display:grid;gap:8px}
.pp legend{font-weight:800;font-size:1.05rem;margin-bottom:8px}
.pp-opt{display:grid;grid-template-columns:auto 1fr;gap:2px 12px;align-items:start;padding:12px 14px;border:1px solid var(--sl-line);border-radius:14px;cursor:pointer}
.pp-opt input{grid-row:span 2;width:18px;height:18px;margin-top:2px;accent-color:#1c1917}
.pp-opt input:focus-visible{outline:3px solid var(--sl-accent);outline-offset:2px}
.pp-opt small{font-size:.83rem;color:var(--sl-muted)}
.pp-opt:has(input:checked){border-color:#1c1917;box-shadow:inset 0 0 0 1px #1c1917;background:#fafaf9}
.pp:has(#pp-p1:checked) [data-dot="p1"],.pp:has(#pp-p2:checked) [data-dot="p2"],.pp:has(#pp-p3:checked) [data-dot="p3"],.pp:has(#pp-p4:checked) [data-dot="p4"]{background:#b45309;transform:translate(-50%,-50%) scale(1.45)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media (max-width:640px){.sl{padding:16px}}
@media (prefers-reduced-motion:reduce){.sl-item{transition:none}}
JavaScript
// Store locator: list and pins select each other; the search box filters both.
document.querySelectorAll('[data-locator]').forEach(function (loc) {
  var input = loc.querySelector('[data-locator-search]'), count = loc.querySelector('[data-locator-count]'), empty = loc.querySelector('[data-locator-empty]');
  var items = [].slice.call(loc.querySelectorAll('[data-store-item]'));
  function select(id, fromPin) {
    loc.querySelectorAll('[data-store]').forEach(function (el) { el.setAttribute('aria-pressed', String(el.dataset.store === id)); });
    if (fromPin) { var row = loc.querySelector('[data-store-item="' + id + '"]'); if (row) row.scrollIntoView({ block: 'nearest' }); }
  }
  loc.addEventListener('click', function (e) {
    var b = e.target.closest('[data-store]'); if (b) select(b.dataset.store, b.hasAttribute('data-pin'));
  });
  loc.querySelector('form').addEventListener('submit', function (e) { e.preventDefault(); });
  input.addEventListener('input', function () {
    var q = input.value.trim().toLowerCase(), n = 0;
    items.forEach(function (li) {
      var hit = li.textContent.toLowerCase().indexOf(q) > -1, pin = loc.querySelector('[data-pin][data-store="' + li.dataset.storeItem + '"]');
      li.hidden = !hit; if (pin) pin.hidden = !hit; if (hit) n++;
    });
    count.textContent = n + ' of ' + items.length + ' cafés'; empty.hidden = n > 0;
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">List and map that select each other, with search</span>
<section class="sl card rounded-4 p-3 p-sm-4" data-locator aria-labelledby="sl-title">
  <div class="row g-4">
    <div class="col-12 sl-side">
      <h2 id="sl-title" class="h4 fw-bold mb-1">Find a Kaffebar</h2>
      <p class="text-body-secondary mb-0" style="font-size:.92rem">Seven cafés across Copenhagen. Pick one from the list or the map.</p>
      <form class="mt-3" role="search"><label class="form-label small fw-bold mb-1" for="sl-q">Search by area or street</label><div class="position-relative"><svg class="sl-icon" viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input class="form-control" id="sl-q" type="search" placeholder="e.g. Vesterbro" autocomplete="off" data-locator-search></div></form>
      <p class="small text-body-secondary mt-2 mb-2" data-locator-count aria-live="polite">7 of 7 cafés</p>
      <ul class="sl-list list-unstyled d-grid gap-2 mb-0">
        <li class="sl-row d-grid gap-2" data-store-item="nb"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="nb" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">1</span><strong>Kaffebar Nørrebro</strong><small class="text-body-secondary">Jægersborggade 41, 2200 København N</small><small class="text-body-secondary">Mon–Sun 7:00–18:00 · Roastery bar</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6918,12.5440" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="ob"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="ob" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">2</span><strong>Kaffebar Østerbro</strong><small class="text-body-secondary">Nordre Frihavnsgade 31, 2100 København Ø</small><small class="text-body-secondary">Mon–Fri 7:00–18:00 · Pastry counter</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6990,12.5800" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="fb"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="fb" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">3</span><strong>Kaffebar Frederiksberg</strong><small class="text-body-secondary">Værnedamsvej 9, 1819 Frederiksberg C</small><small class="text-body-secondary">Mon–Sun 7:30–18:00 · Step-free entrance</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6735,12.5420" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="ib"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="ib" aria-pressed="true"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">4</span><strong>Kaffebar Indre By</strong><small class="text-body-secondary">Kronprinsensgade 6, 1114 København K</small><small class="text-body-secondary">Mon–Sat 8:00–18:00 · Brew bar and beans</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6800,12.5790" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="ch"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="ch" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">5</span><strong>Kaffebar Christianshavn</strong><small class="text-body-secondary">Overgaden oven Vandet 10, 1415 København K</small><small class="text-body-secondary">Mon–Sun 8:00–17:00 · Canal-side seats</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6730,12.5935" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="vb"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="vb" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">6</span><strong>Kaffebar Vesterbro</strong><small class="text-body-secondary">Istedgade 88, 1650 København V</small><small class="text-body-secondary">Mon–Sun 7:00–19:00 · Open latest</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6695,12.5560" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
        <li class="sl-row d-grid gap-2" data-store-item="is"><button class="sl-item list-group-item list-group-item-action border px-3 py-2" type="button" data-store="is" aria-pressed="false"><span class="sl-num badge rounded-circle d-grid p-0" style="place-items:center;font-size:.8rem" aria-hidden="true">7</span><strong>Kaffebar Islands Brygge</strong><small class="text-body-secondary">Islands Brygge 25, 2300 København S</small><small class="text-body-secondary">Mon–Sun 8:00–18:00 · Harbour terrace</small></button>
          <p class="sl-meta d-flex flex-wrap gap-1 column-gap-3 small mb-0" style="padding-left:56px;margin-top:-6px"><a class="fw-bold" href="https://www.google.com/maps/dir/?api=1&destination=55.6640,12.5770" target="_blank" rel="noopener">Get directions<span class="visually-hidden"> (opens in a new tab)</span></a><span class="text-body-secondary">Opens Google Maps</span></p></li>
      </ul>
      <p class="border border-secondary-subtle rounded-4 p-3 small text-body-secondary mt-2 mb-0" style="border-style:dashed!important" data-locator-empty hidden>No café matches that search. Try an area such as Nørrebro.</p>
    </div>
    <div class="col-12 sl-main">
      <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
      <g fill="#57534e" font-family="inherit" font-size="11" font-weight="700" letter-spacing="1.4" text-anchor="middle">
        <text x="120" y="70">NØRREBRO</text><text x="405" y="122">ØSTERBRO</text><text x="370" y="268">INDRE BY</text><text x="528" y="306">CHRISTIANSHAVN</text>
        <text x="205" y="372">VESTERBRO</text><text x="80" y="194">FREDERIKSBERG</text><text x="470" y="410">ISLANDS BRYGGE</text>
      </g>
    </svg>
      <button class="sl-pin" type="button" data-pin data-store="nb" data-label="Nørrebro" aria-pressed="false" aria-label="Kaffebar Nørrebro" style="left:21.7%;top:28.4%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">1</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ob" data-label="Østerbro" aria-pressed="false" aria-label="Kaffebar Østerbro" style="left:65.8%;top:19.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">2</span></button>
      <button class="sl-pin" type="button" data-pin data-store="fb" data-label="Frederiksberg" aria-pressed="false" aria-label="Kaffebar Frederiksberg" style="left:15.3%;top:55.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">3</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ib" data-label="Indre By" aria-pressed="true" aria-label="Kaffebar Indre By" style="left:63.3%;top:51.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">4</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ch" data-label="Christianshavn" aria-pressed="false" aria-label="Kaffebar Christianshavn" style="left:83.7%;top:62.7%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">5</span></button>
      <button class="sl-pin" type="button" data-pin data-store="vb" data-label="Vesterbro" aria-pressed="false" aria-label="Kaffebar Vesterbro" style="left:35.0%;top:73.3%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">6</span></button>
      <button class="sl-pin" type="button" data-pin data-store="is" data-label="Islands Brygge" aria-pressed="false" aria-label="Kaffebar Islands Brygge" style="left:68.3%;top:87.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">7</span></button>
      </div>
      <p class="text-body-secondary mt-2 mb-0" style="font-size:.75rem">Illustrated map, not to scale. Directions open your maps app.</p>
    </div>
  </div>
</section></div>
<div><span class="lbl">Pick-up point chooser with a mini map (CSS :has, no JavaScript)</span>
<section class="sl pp card rounded-4 p-3 p-sm-4" aria-label="Pick-up point">
  <div class="row g-4 align-items-start">
    <fieldset class="col-md-6 d-grid gap-2">
      <legend class="fs-6 fw-bolder mb-2" style="font-size:1.05rem!important">Choose a pick-up point</legend>
      <label class="pp-opt form-check d-grid border rounded-4 py-2 px-3 m-0" style="grid-template-columns:auto 1fr;column-gap:12px;cursor:pointer"><input class="form-check-input m-0 mt-1 float-none" style="grid-row:span 2" type="radio" name="pp" id="pp-p1" value="p1" checked><strong>Parcel locker, Sankt Hans Torv</strong><small class="text-body-secondary">350 m · open 24 hours</small></label>
      <label class="pp-opt form-check d-grid border rounded-4 py-2 px-3 m-0" style="grid-template-columns:auto 1fr;column-gap:12px;cursor:pointer"><input class="form-check-input m-0 mt-1 float-none" style="grid-row:span 2" type="radio" name="pp" id="pp-p2" value="p2"><strong>Kiosk Elmegade</strong><small class="text-body-secondary">600 m · 7:00–23:00</small></label>
      <label class="pp-opt form-check d-grid border rounded-4 py-2 px-3 m-0" style="grid-template-columns:auto 1fr;column-gap:12px;cursor:pointer"><input class="form-check-input m-0 mt-1 float-none" style="grid-row:span 2" type="radio" name="pp" id="pp-p3" value="p3"><strong>Nørrebro Cykler</strong><small class="text-body-secondary">900 m · 9:00–18:00</small></label>
      <label class="pp-opt form-check d-grid border rounded-4 py-2 px-3 m-0" style="grid-template-columns:auto 1fr;column-gap:12px;cursor:pointer"><input class="form-check-input m-0 mt-1 float-none" style="grid-row:span 2" type="radio" name="pp" id="pp-p4" value="p4"><strong>Post office, Nørrebrogade</strong><small class="text-body-secondary">1.2 km · 10:00–17:30</small></label>
    </fieldset>
    <div class="col-md-6">
      <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
    </svg>
      <span class="sl-dot" data-dot="p1" style="left:34%;top:30%"></span>
      <span class="sl-dot" data-dot="p2" style="left:52%;top:44%"></span>
      <span class="sl-dot" data-dot="p3" style="left:24%;top:62%"></span>
      <span class="sl-dot" data-dot="p4" style="left:68%;top:70%"></span>
      <span class="sl-you" style="left:42%;top:52%"></span>
      </div>
    </div>
  </div>
</section></div>
CSS
/* Store locator: an illustrated map (inline SVG, not to scale) with pins positioned in percent of the frame. */
.sl-frame{position:relative;aspect-ratio:4/3;border-radius:16px;overflow:hidden;background:#f2eee6}
.sl-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.sl-pin{--pin:#b45309;position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:var(--pin);cursor:pointer;
  transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s,color .18s}
.sl-pin svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(28,25,23,.3))}
.sl-pin span{position:absolute;top:6px;left:0;right:0;text-align:center;color:#fff;font-size:.8rem;font-weight:800;line-height:20px}
.sl-pin:hover{transform:translate(-50%,-100%) scale(1.08)}
.sl-pin[aria-pressed="true"]{--pin:#1c1917;z-index:2;transform:translate(-50%,-100%) scale(1.2)}
.sl-pin[aria-pressed="true"]::after{content:attr(data-label);position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);
  padding:4px 9px;border-radius:8px;background:#1c1917;color:#fff;font-size:.7rem;font-weight:700;white-space:nowrap}
.sl-pin:focus-visible{outline:3px solid #1c1917;outline-offset:2px;border-radius:8px}
.sl-pin[hidden]{display:none}
.sl-dot{position:absolute;width:18px;height:18px;border-radius:50%;background:#78716c;border:3px solid #fff;box-shadow:0 2px 5px rgba(28,25,23,.35);transform:translate(-50%,-50%);transition:transform .18s,background-color .18s}
.sl-you{position:absolute;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 6px rgba(37,99,235,.25);transform:translate(-50%,-50%)}
@media (prefers-reduced-motion:reduce){.sl-pin,.sl-dot{transition:none}}
.sl .form-control{padding:11px 14px 11px 40px;font-size:.95rem;border-color:#a8a29e;border-radius:12px;background-color:#fafaf9}
.sl-icon{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:#57534e;pointer-events:none}
.sl-item strong{font-size:.98rem}.sl-item small{font-size:.83rem}
.sl .form-control:focus{border-color:#b45309;box-shadow:none;outline:3px solid #b45309;outline-offset:1px}
.sl-list{--bs-list-group-border-radius:14px;max-height:430px;overflow:auto;padding:2px 4px 2px 2px}
.sl-list .list-group-item{border-radius:14px!important;border-width:1px!important}
.sl-item{--bs-list-group-action-hover-bg:#fffbf5;--bs-list-group-action-active-bg:#fafaf9;display:grid;grid-template-columns:30px 1fr;gap:2px 12px;border-radius:14px}
.sl-item[aria-pressed="true"]{border-color:#1c1917!important;background:#fafaf9;box-shadow:inset 0 0 0 1px #1c1917}
.sl-item:focus-visible{outline:3px solid #b45309;outline-offset:2px;box-shadow:none;z-index:2}
.sl-num{grid-row:span 3;width:28px;height:28px;background:#b45309}
.sl-item[aria-pressed="true"] .sl-num{background:#1c1917}
.sl-meta a{color:#9a3412;text-underline-offset:3px}
.sl-meta a:focus-visible{outline:3px solid #b45309;outline-offset:2px;border-radius:4px}
.sl-row[hidden]{display:none!important}
.pp .form-check-input{width:18px;height:18px;margin-top:2px;border-color:#78716c}
.pp .form-check-input:checked{background-color:#1c1917;border-color:#1c1917}
.pp .form-check-input:focus-visible{box-shadow:none;outline:3px solid #b45309;outline-offset:2px}
.pp-opt:has(input:checked){border-color:#1c1917!important;box-shadow:inset 0 0 0 1px #1c1917;background:#fafaf9}
.pp:has(#pp-p1:checked) [data-dot="p1"],.pp:has(#pp-p2:checked) [data-dot="p2"],.pp:has(#pp-p3:checked) [data-dot="p3"],.pp:has(#pp-p4:checked) [data-dot="p4"]{background:#b45309;transform:translate(-50%,-50%) scale(1.45)}
@media (min-width:880px){.sl-side{flex:0 0 372px;width:auto}.sl-main{flex:1 1 0;width:auto;min-width:0}}
JavaScript
// Store locator: list and pins select each other; the search box filters both.
document.querySelectorAll('[data-locator]').forEach(function (loc) {
  var input = loc.querySelector('[data-locator-search]'), count = loc.querySelector('[data-locator-count]'), empty = loc.querySelector('[data-locator-empty]');
  var items = [].slice.call(loc.querySelectorAll('[data-store-item]'));
  function select(id, fromPin) {
    loc.querySelectorAll('[data-store]').forEach(function (el) { el.setAttribute('aria-pressed', String(el.dataset.store === id)); });
    if (fromPin) { var row = loc.querySelector('[data-store-item="' + id + '"]'); if (row) row.scrollIntoView({ block: 'nearest' }); }
  }
  loc.addEventListener('click', function (e) {
    var b = e.target.closest('[data-store]'); if (b) select(b.dataset.store, b.hasAttribute('data-pin'));
  });
  loc.querySelector('form').addEventListener('submit', function (e) { e.preventDefault(); });
  input.addEventListener('input', function () {
    var q = input.value.trim().toLowerCase(), n = 0;
    items.forEach(function (li) {
      var hit = li.textContent.toLowerCase().indexOf(q) > -1, pin = loc.querySelector('[data-pin][data-store="' + li.dataset.storeItem + '"]');
      li.hidden = !hit; if (pin) pin.hidden = !hit; if (hit) n++;
    });
    count.textContent = n + ' of ' + items.length + ' cafés'; empty.hidden = n > 0;
  });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">List and map that select each other, with search</span>
<section class="rounded-[22px] border border-[#e7e5e4] bg-white p-4 text-[#1c1917] sm:p-6" data-locator aria-labelledby="sl-title">
  <div class="grid gap-[22px] min-[880px]:grid-cols-[350px_minmax(0,1fr)] min-[880px]:items-start">
    <div>
      <h2 id="sl-title" class="text-[1.35rem] leading-tight font-bold tracking-[-.02em]">Find a Kaffebar</h2>
      <p class="mt-1 text-[.92rem] text-[#57534e]">Seven cafés across Copenhagen. Pick one from the list or the map.</p>
      <form class="mt-4" role="search"><label class="mb-1.5 block text-[.82rem] font-bold" for="sl-q">Search by area or street</label><div class="relative"><svg class="pointer-events-none absolute top-1/2 left-[13px] -translate-y-1/2 text-[#57534e]" viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input class="w-full rounded-xl border border-[#a8a29e] bg-[#fafaf9] py-[11px] pr-3.5 pl-10 text-[.95rem] focus-visible:border-[#b45309] focus-visible:outline-3 focus-visible:outline-offset-1 focus-visible:outline-[#b45309]" id="sl-q" type="search" placeholder="e.g. Vesterbro" autocomplete="off" data-locator-search></div></form>
      <p class="mt-2.5 text-[.8rem] text-[#57534e]" data-locator-count aria-live="polite">7 of 7 cafés</p>
      <ul class="mt-2.5 grid max-h-[430px] gap-2 overflow-auto py-0.5 pr-1 pl-0.5">
        <li class="grid gap-2" data-store-item="nb"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="nb" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">1</span><strong class="text-[.98rem]">Kaffebar Nørrebro</strong><small class="text-[.83rem] text-[#57534e]">Jægersborggade 41, 2200 København N</small><small class="text-[.83rem] text-[#57534e]">Mon–Sun 7:00–18:00 · Roastery bar</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6918,12.5440" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="ob"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="ob" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">2</span><strong class="text-[.98rem]">Kaffebar Østerbro</strong><small class="text-[.83rem] text-[#57534e]">Nordre Frihavnsgade 31, 2100 København Ø</small><small class="text-[.83rem] text-[#57534e]">Mon–Fri 7:00–18:00 · Pastry counter</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6990,12.5800" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="fb"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="fb" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">3</span><strong class="text-[.98rem]">Kaffebar Frederiksberg</strong><small class="text-[.83rem] text-[#57534e]">Værnedamsvej 9, 1819 Frederiksberg C</small><small class="text-[.83rem] text-[#57534e]">Mon–Sun 7:30–18:00 · Step-free entrance</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6735,12.5420" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="ib"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="ib" aria-pressed="true"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">4</span><strong class="text-[.98rem]">Kaffebar Indre By</strong><small class="text-[.83rem] text-[#57534e]">Kronprinsensgade 6, 1114 København K</small><small class="text-[.83rem] text-[#57534e]">Mon–Sat 8:00–18:00 · Brew bar and beans</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6800,12.5790" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="ch"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="ch" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">5</span><strong class="text-[.98rem]">Kaffebar Christianshavn</strong><small class="text-[.83rem] text-[#57534e]">Overgaden oven Vandet 10, 1415 København K</small><small class="text-[.83rem] text-[#57534e]">Mon–Sun 8:00–17:00 · Canal-side seats</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6730,12.5935" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="vb"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="vb" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">6</span><strong class="text-[.98rem]">Kaffebar Vesterbro</strong><small class="text-[.83rem] text-[#57534e]">Istedgade 88, 1650 København V</small><small class="text-[.83rem] text-[#57534e]">Mon–Sun 7:00–19:00 · Open latest</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6695,12.5560" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
        <li class="grid gap-2" data-store-item="is"><button class="group grid w-full cursor-pointer grid-cols-[30px_1fr] gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] bg-white px-3.5 py-3 text-left transition-colors hover:border-[#d6b08a] hover:bg-[#fffbf5] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309] aria-pressed:border-[#1c1917] aria-pressed:bg-[#fafaf9] aria-pressed:shadow-[inset_0_0_0_1px_#1c1917] motion-reduce:transition-none" type="button" data-store="is" aria-pressed="false"><span class="row-span-3 grid size-7 place-items-center rounded-full bg-[#b45309] text-[.8rem] font-extrabold text-white group-aria-pressed:bg-[#1c1917]" aria-hidden="true">7</span><strong class="text-[.98rem]">Kaffebar Islands Brygge</strong><small class="text-[.83rem] text-[#57534e]">Islands Brygge 25, 2300 København S</small><small class="text-[.83rem] text-[#57534e]">Mon–Sun 8:00–18:00 · Harbour terrace</small></button>
          <p class="-mt-1.5 flex flex-wrap gap-x-3 gap-y-1 pr-3.5 pl-14 text-[.8rem]"><a class="rounded font-bold text-[#9a3412] underline underline-offset-[3px] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" href="https://www.google.com/maps/dir/?api=1&destination=55.6640,12.5770" target="_blank" rel="noopener">Get directions<span class="sr-only"> (opens in a new tab)</span></a><span class="text-[#57534e]">Opens Google Maps</span></p></li>
      </ul>
      <p class="rounded-[14px] border border-dashed border-[#a8a29e] p-4 text-[.9rem] text-[#57534e]" data-locator-empty hidden>No café matches that search. Try an area such as Nørrebro.</p>
    </div>
    <div>
      <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
      <g fill="#57534e" font-family="inherit" font-size="11" font-weight="700" letter-spacing="1.4" text-anchor="middle">
        <text x="120" y="70">NØRREBRO</text><text x="405" y="122">ØSTERBRO</text><text x="370" y="268">INDRE BY</text><text x="528" y="306">CHRISTIANSHAVN</text>
        <text x="205" y="372">VESTERBRO</text><text x="80" y="194">FREDERIKSBERG</text><text x="470" y="410">ISLANDS BRYGGE</text>
      </g>
    </svg>
      <button class="sl-pin" type="button" data-pin data-store="nb" data-label="Nørrebro" aria-pressed="false" aria-label="Kaffebar Nørrebro" style="left:21.7%;top:28.4%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">1</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ob" data-label="Østerbro" aria-pressed="false" aria-label="Kaffebar Østerbro" style="left:65.8%;top:19.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">2</span></button>
      <button class="sl-pin" type="button" data-pin data-store="fb" data-label="Frederiksberg" aria-pressed="false" aria-label="Kaffebar Frederiksberg" style="left:15.3%;top:55.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">3</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ib" data-label="Indre By" aria-pressed="true" aria-label="Kaffebar Indre By" style="left:63.3%;top:51.6%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">4</span></button>
      <button class="sl-pin" type="button" data-pin data-store="ch" data-label="Christianshavn" aria-pressed="false" aria-label="Kaffebar Christianshavn" style="left:83.7%;top:62.7%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">5</span></button>
      <button class="sl-pin" type="button" data-pin data-store="vb" data-label="Vesterbro" aria-pressed="false" aria-label="Kaffebar Vesterbro" style="left:35.0%;top:73.3%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">6</span></button>
      <button class="sl-pin" type="button" data-pin data-store="is" data-label="Islands Brygge" aria-pressed="false" aria-label="Kaffebar Islands Brygge" style="left:68.3%;top:87.1%"><svg viewBox="0 0 34 42" aria-hidden="true" focusable="false"><path d="M17 41C17 41 32 25.5 32 16A15 15 0 0 0 2 16C2 25.5 17 41 17 41Z" fill="currentColor" stroke="#fff" stroke-width="2.5"/></svg><span aria-hidden="true">7</span></button>
      </div>
      <p class="mt-2.5 text-[.75rem] text-[#57534e]">Illustrated map, not to scale. Directions open your maps app.</p>
    </div>
  </div>
</section></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#57534e] uppercase">Pick-up point chooser with a mini map (CSS :has, no JavaScript)</span>
<section class="rounded-[22px] border border-[#e7e5e4] bg-white p-4 text-[#1c1917] sm:p-6 grid gap-5 min-[760px]:grid-cols-2 min-[760px]:items-start" aria-label="Pick-up point">
  <fieldset class="grid gap-2">
    <legend class="mb-2 text-[1.05rem] font-extrabold">Choose a pick-up point</legend>
    <label class="grid cursor-pointer grid-cols-[auto_1fr] items-start gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] px-3.5 py-3 has-checked:border-[#1c1917] has-checked:bg-[#fafaf9] has-checked:shadow-[inset_0_0_0_1px_#1c1917]"><input class="row-span-2 mt-0.5 size-[18px] accent-[#1c1917] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" type="radio" name="pp" id="pp-p1" value="p1" checked><strong>Parcel locker, Sankt Hans Torv</strong><small class="text-[.83rem] text-[#57534e]">350 m · open 24 hours</small></label>
    <label class="grid cursor-pointer grid-cols-[auto_1fr] items-start gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] px-3.5 py-3 has-checked:border-[#1c1917] has-checked:bg-[#fafaf9] has-checked:shadow-[inset_0_0_0_1px_#1c1917]"><input class="row-span-2 mt-0.5 size-[18px] accent-[#1c1917] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" type="radio" name="pp" id="pp-p2" value="p2"><strong>Kiosk Elmegade</strong><small class="text-[.83rem] text-[#57534e]">600 m · 7:00–23:00</small></label>
    <label class="grid cursor-pointer grid-cols-[auto_1fr] items-start gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] px-3.5 py-3 has-checked:border-[#1c1917] has-checked:bg-[#fafaf9] has-checked:shadow-[inset_0_0_0_1px_#1c1917]"><input class="row-span-2 mt-0.5 size-[18px] accent-[#1c1917] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" type="radio" name="pp" id="pp-p3" value="p3"><strong>Nørrebro Cykler</strong><small class="text-[.83rem] text-[#57534e]">900 m · 9:00–18:00</small></label>
    <label class="grid cursor-pointer grid-cols-[auto_1fr] items-start gap-x-3 gap-y-0.5 rounded-[14px] border border-[#e7e5e4] px-3.5 py-3 has-checked:border-[#1c1917] has-checked:bg-[#fafaf9] has-checked:shadow-[inset_0_0_0_1px_#1c1917]"><input class="row-span-2 mt-0.5 size-[18px] accent-[#1c1917] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#b45309]" type="radio" name="pp" id="pp-p4" value="p4"><strong>Post office, Nørrebrogade</strong><small class="text-[.83rem] text-[#57534e]">1.2 km · 10:00–17:30</small></label>
  </fieldset>
  <div class="sl-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#f2eee6"/>
      <g stroke="#e6dfd2" stroke-width="2.5" fill="none">
        <path d="M0 60h600M0 160h600M0 360h600M0 410h600M60 0v450M180 0v450M290 0v450M520 0v450M0 210l600 40M120 0l90 450M380 0l-60 450"/>
      </g>
      <path d="M420 0H600V170C560 150 520 110 475 72C452 52 434 26 420 0Z" fill="#b9d7ee"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#b9d7ee" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#b9d7ee" stroke-width="9" stroke-linecap="round"/>
      <g fill="#b9d7ee"><rect x="238" y="70" width="26" height="70" rx="13" transform="rotate(28 251 105)"/><rect x="214" y="138" width="26" height="70" rx="13" transform="rotate(28 227 173)"/><rect x="190" y="206" width="26" height="62" rx="13" transform="rotate(28 203 237)"/></g>
      <g fill="#cfe5c4"><rect x="24" y="290" width="86" height="62" rx="14"/><rect x="346" y="168" width="58" height="46" rx="10"/><rect x="292" y="14" width="96" height="44" rx="14"/><circle cx="560" cy="420" r="46"/></g>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path stroke="#ddd5c6" stroke-width="11" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
        <path stroke="#ffffff" stroke-width="7" d="M0 302C150 292 250 270 334 204M118 0C150 100 205 190 334 204M0 128L334 204M334 204C346 262 300 380 262 450M94 0C74 150 58 300 92 450M334 204L600 236M436 0C430 120 410 300 600 330"/>
      </g>
    </svg>
      <span class="sl-dot in-has-[#pp-p1:checked]:scale-145 in-has-[#pp-p1:checked]:bg-[#b45309]" data-dot="p1" style="left:34%;top:30%"></span>
      <span class="sl-dot in-has-[#pp-p2:checked]:scale-145 in-has-[#pp-p2:checked]:bg-[#b45309]" data-dot="p2" style="left:52%;top:44%"></span>
      <span class="sl-dot in-has-[#pp-p3:checked]:scale-145 in-has-[#pp-p3:checked]:bg-[#b45309]" data-dot="p3" style="left:24%;top:62%"></span>
      <span class="sl-dot in-has-[#pp-p4:checked]:scale-145 in-has-[#pp-p4:checked]:bg-[#b45309]" data-dot="p4" style="left:68%;top:70%"></span>
      <span class="sl-you" style="left:42%;top:52%"></span>
  </div>
</section></div>
CSS
@layer components {
/* Store locator: an illustrated map (inline SVG, not to scale) with pins positioned in percent of the frame. */
.sl-frame{position:relative;aspect-ratio:4/3;border-radius:16px;overflow:hidden;background:#f2eee6}
.sl-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.sl-pin{--pin:#b45309;position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:var(--pin);cursor:pointer;
  transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s,color .18s}
.sl-pin svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(28,25,23,.3))}
.sl-pin span{position:absolute;top:6px;left:0;right:0;text-align:center;color:#fff;font-size:.8rem;font-weight:800;line-height:20px}
.sl-pin:hover{transform:translate(-50%,-100%) scale(1.08)}
.sl-pin[aria-pressed="true"]{--pin:#1c1917;z-index:2;transform:translate(-50%,-100%) scale(1.2)}
.sl-pin[aria-pressed="true"]::after{content:attr(data-label);position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);
  padding:4px 9px;border-radius:8px;background:#1c1917;color:#fff;font-size:.7rem;font-weight:700;white-space:nowrap}
.sl-pin:focus-visible{outline:3px solid #1c1917;outline-offset:2px;border-radius:8px}
.sl-pin[hidden]{display:none}
.sl-dot{position:absolute;width:18px;height:18px;border-radius:50%;background:#78716c;border:3px solid #fff;box-shadow:0 2px 5px rgba(28,25,23,.35);transform:translate(-50%,-50%);transition:transform .18s,background-color .18s}
.sl-you{position:absolute;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 6px rgba(37,99,235,.25);transform:translate(-50%,-50%)}
@media (prefers-reduced-motion:reduce){.sl-pin,.sl-dot{transition:none}}
.sl-dot{translate:-50% -50%;transform:none}
}
JavaScript
// Store locator: list and pins select each other; the search box filters both.
document.querySelectorAll('[data-locator]').forEach(function (loc) {
  var input = loc.querySelector('[data-locator-search]'), count = loc.querySelector('[data-locator-count]'), empty = loc.querySelector('[data-locator-empty]');
  var items = [].slice.call(loc.querySelectorAll('[data-store-item]'));
  function select(id, fromPin) {
    loc.querySelectorAll('[data-store]').forEach(function (el) { el.setAttribute('aria-pressed', String(el.dataset.store === id)); });
    if (fromPin) { var row = loc.querySelector('[data-store-item="' + id + '"]'); if (row) row.scrollIntoView({ block: 'nearest' }); }
  }
  loc.addEventListener('click', function (e) {
    var b = e.target.closest('[data-store]'); if (b) select(b.dataset.store, b.hasAttribute('data-pin'));
  });
  loc.querySelector('form').addEventListener('submit', function (e) { e.preventDefault(); });
  input.addEventListener('input', function () {
    var q = input.value.trim().toLowerCase(), n = 0;
    items.forEach(function (li) {
      var hit = li.textContent.toLowerCase().indexOf(q) > -1, pin = loc.querySelector('[data-pin][data-store="' + li.dataset.storeItem + '"]');
      li.hidden = !hit; if (pin) pin.hidden = !hit; if (hit) n++;
    });
    count.textContent = n + ' of ' + items.length + ' cafés'; empty.hidden = n > 0;
  });
});

About this set

A store locator with a search box, a result list and an illustrated city map with numbered pins, plus a pick-up point chooser. The locator lists seven cafés; choosing a café in the list enlarges its pin and labels it on the map, and choosing a pin selects the matching list item and scrolls it into view, so both sides stay in step. Typing in the search box filters the list and the pins together, updates a live result count and shows a message when nothing matches. Every café has a Get directions link that opens a Google Maps directions URL with the café’s coordinates, which hands off to the visitor’s maps app on phones. The map itself is an inline SVG drawing of streets, water and parks, not to scale and with no tiles, and the pins are buttons positioned in percent so they stay put at any width. The pick-up point chooser is a set of radio buttons beside a mini map where the chosen point grows and changes colour through :has(), with no JavaScript. Swap the drawing for a screenshot or the Leaflet set when you need real streets.

What’s included

  • List and pins select each other, with aria-pressed on both
  • Search filters the list and pins and announces the result count
  • Get directions links with coordinates for each store
  • Pins positioned in percent over an inline SVG map
  • Pick-up chooser highlights its map dot with :has() and no JavaScript

Accessibility

The search field has a visible label, and the result count is a polite live region. List items and pins are real buttons with aria-pressed, the pins named after their café, and directions links say they open a new tab. Every control has a visible focus outline, the decorative map is hidden from assistive technology, and pin and list transitions stop under prefers-reduced-motion.

Customise it

Set --pin on .sl-pin for the pin colour and adjust the left and top percentages for each store. The accent and borders of the list come from the --sl-accent and --sl-line custom properties, Bootstrap variables or Tailwind colour classes.