Open demo

CSS code

HTML
<div><span class="lbl">Pulsing status markers</span>
<section class="mk-sec" aria-labelledby="mk-live-title">
  <h2 id="mk-live-title">Couriers on the road</h2><p>Five couriers. The pulse shows who is moving; it stops for anyone on a break.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul class="mk-layer" aria-label="Courier positions">
      <li class="mk-at" style="left:30%;top:40%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Ana</span><span class="sr-only">Ana, on time, 4 minutes away</span></li>
      <li class="mk-at" style="left:58%;top:28%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Jonas</span><span class="sr-only">Jonas, on time, 9 minutes away</span></li>
      <li class="mk-at" style="left:46%;top:66%"><span class="mk-pulse late"></span><span class="mk-name" aria-hidden="true">Mette</span><span class="sr-only">Mette, running 6 minutes late</span></li>
      <li class="mk-at" style="left:76%;top:58%"><span class="mk-pulse idle"></span><span class="mk-name" aria-hidden="true">Karim</span><span class="sr-only">Karim, on a break</span></li>
      <li class="mk-at" style="left:20%;top:70%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Sofie</span><span class="sr-only">Sofie, on time, 2 minutes away</span></li>
    </ul>
  </div>
  <ul class="mk-legend"><li><i class="" aria-hidden="true"></i>On time</li><li><i class="late" aria-hidden="true"></i>Running late</li><li><i class="idle" aria-hidden="true"></i>On a break</li></ul>
</section></div>
<div><span class="lbl">Cluster count badges</span>
<section class="mk-sec" aria-labelledby="mk-cluster-title">
  <h2 id="mk-cluster-title">97 venues with outdoor seating</h2><p>Nearby markers are grouped; badge size follows the count.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul class="mk-layer" aria-label="Venues by area">
      <li class="mk-at" style="left:22%;top:48%"><span class="mk-cluster is-lg" aria-hidden="true">42</span><span class="sr-only">Nørrebro: 42 venues</span></li>
      <li class="mk-at" style="left:66%;top:22%"><span class="mk-cluster is-md" aria-hidden="true">18</span><span class="sr-only">Østerbro: 18 venues</span></li>
      <li class="mk-at" style="left:60%;top:52%"><span class="mk-cluster is-sm" aria-hidden="true">7</span><span class="sr-only">Indre By: 7 venues</span></li>
      <li class="mk-at" style="left:82%;top:64%"><span class="mk-cluster is-sm" aria-hidden="true">3</span><span class="sr-only">Christianshavn: 3 venues</span></li>
      <li class="mk-at" style="left:36%;top:74%"><span class="mk-cluster is-lg" aria-hidden="true">26</span><span class="sr-only">Vesterbro: 26 venues</span></li>
      <li class="mk-at" style="left:66%;top:86%"><span class="mk-cluster is-one" aria-hidden="true">1</span><span class="sr-only">Islands Brygge: 1 venue</span></li>
    </ul>
  </div>
</section></div>
<div><span class="lbl">Pins that open a popup card</span>
<section class="mk-sec" data-popups aria-labelledby="mk-pop-title">
  <h2 id="mk-pop-title">Tables free tonight</h2><p>Select a pin to see the restaurant. Press Escape to close the card.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v1" aria-label="Høst Kitchen" style="left:34%;top:42%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v2" aria-label="Bar Lille" style="left:58%;top:30%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v3" aria-label="Pasta Ventura" style="left:50%;top:70%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <div class="mk-card" id="mk-v1" role="group" aria-label="Høst Kitchen" hidden>
        <h3>Høst Kitchen</h3><p>Nordic · ££ · Open until 22:00</p>
        <span class="mk-rate"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.7<span class="sr-only"> out of 5</span></span><br>
        <a href="#">Book a table</a>
        <button class="mk-x" type="button" data-popup-close aria-label="Close Høst Kitchen"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
      <div class="mk-card" id="mk-v2" role="group" aria-label="Bar Lille" hidden>
        <h3>Bar Lille</h3><p>Wine bar · £ · Open until 01:00</p>
        <span class="mk-rate"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.5<span class="sr-only"> out of 5</span></span><br>
        <a href="#">Book a table</a>
        <button class="mk-x" type="button" data-popup-close aria-label="Close Bar Lille"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
      <div class="mk-card" id="mk-v3" role="group" aria-label="Pasta Ventura" hidden>
        <h3>Pasta Ventura</h3><p>Italian · ££ · Opens 17:00</p>
        <span class="mk-rate"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.6<span class="sr-only"> out of 5</span></span><br>
        <a href="#">Book a table</a>
        <button class="mk-x" type="button" data-popup-close aria-label="Close Pasta Ventura"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
  </div>
</section></div>
<div><span class="lbl">Marker style gallery</span>
<section class="mk-sec" aria-labelledby="mk-gal-title">
  <h2 id="mk-gal-title">Six marker shapes</h2><p>Pick one per map and keep it consistent; colour and shape together tell types apart.</p>
  <ul class="mk-gallery">
    <li><svg width="34" height="42" 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="#be123c" stroke="#fff" stroke-width="2.5"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg><span>Teardrop pin</span></li>
    <li><span class="mk-dot"></span><span>Dot with halo</span></li>
    <li><span class="mk-num">3</span><span>Numbered</span></li>
    <li><span class="mk-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 8h13v5a5 5 0 0 1-5 5H9a5 5 0 0 1-5-5zM17 9h1.5a2.5 2.5 0 0 1 0 5H17M8 2v3M12 2v3"/></svg></span><span>Icon pin</span></li>
    <li><span class="mk-price">€24</span><span>Price tag</span></li>
    <li><span class="mk-pill"><i></i>Ferry stop</span><span>Label pill</span></li>
  </ul>
</section></div>
CSS
/* Map markers drawn with CSS and inline SVG over an illustrated map (not to scale). */
.mk-frame{position:relative;aspect-ratio:16/9;min-height:300px;width:100%;border-radius:18px;overflow:hidden;background:#eef1ec}
.mk-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.mk-at{position:absolute;transform:translate(-50%,-50%)}
/* 1. pulsing status markers */
.mk-pulse{--c:#15803d;position:relative;display:block;width:18px;height:18px;border-radius:50%;background:var(--c);border:3px solid #fff;box-shadow:0 2px 6px rgba(15,23,42,.35)}
.mk-pulse::before,.mk-pulse::after{content:"";position:absolute;inset:-3px;border-radius:50%;background:var(--c);opacity:.45;z-index:-1;animation:mk-pulse 2s ease-out infinite}
.mk-pulse::after{animation-delay:1s}
.mk-pulse.late{--c:#b45309}.mk-pulse.idle{--c:#64748b}
.mk-pulse.idle::before,.mk-pulse.idle::after{animation:none;opacity:0}
.mk-name{position:absolute;left:50%;top:calc(100% + 6px);transform:translateX(-50%);padding:2px 8px;border-radius:7px;background:#fff;color:#0f172a;font-size:.72rem;font-weight:700;white-space:nowrap;box-shadow:0 1px 3px rgba(15,23,42,.2)}
@keyframes mk-pulse{from{transform:scale(1);opacity:.5}to{transform:scale(3.2);opacity:0}}
/* 2. cluster badges */
.mk-cluster{display:grid;place-items:center;border-radius:50%;background:#0f766e;color:#fff;font-weight:800;box-shadow:0 0 0 6px rgba(15,118,110,.25),0 0 0 12px rgba(15,118,110,.12)}
.mk-cluster.is-lg{width:58px;height:58px;font-size:1.05rem}
.mk-cluster.is-md{width:46px;height:46px;font-size:.95rem}
.mk-cluster.is-sm{width:36px;height:36px;font-size:.85rem}
.mk-cluster.is-one{width:22px;height:22px;font-size:.7rem;box-shadow:0 0 0 4px rgba(15,118,110,.25)}
/* 3. popup cards */
.mk-btn{position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:#be123c;cursor:pointer;transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s}
.mk-btn svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(15,23,42,.3))}
.mk-btn:hover,.mk-btn[aria-expanded="true"]{transform:translate(-50%,-100%) scale(1.18)}
.mk-btn[aria-expanded="true"]{color:#0f172a}
.mk-btn:focus-visible{outline:3px solid #0f172a;outline-offset:2px;border-radius:8px}
.mk-card{position:absolute;left:12px;bottom:12px;z-index:3;width:min(300px,calc(100% - 24px));padding:16px 16px 14px;border-radius:14px;background:#fff;color:#0f172a;box-shadow:0 18px 40px -16px rgba(15,23,42,.45)}
.mk-card[hidden]{display:none}
@media (prefers-reduced-motion:reduce){.mk-pulse::before,.mk-pulse::after{animation:none;opacity:0}.mk-btn{transition:none}}
.mk-dot{display:block;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 7px rgba(37,99,235,.25),0 2px 5px rgba(15,23,42,.3)}
.mk-num{display:grid;place-items:center;width:30px;height:30px;border-radius:9px 9px 9px 2px;background:#0f172a;color:#fff;font-size:.85rem;font-weight:800;box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon{display:grid;place-items:center;width:38px;height:38px;border-radius:50% 50% 50% 0;background:#7c3aed;transform:rotate(-45deg);box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon svg{transform:rotate(45deg)}
.mk-price{position:relative;display:block;padding:5px 10px;border-radius:999px;background:#fff;color:#0f172a;font-size:.85rem;font-weight:800;box-shadow:0 2px 8px rgba(15,23,42,.25)}
.mk-price::after{content:"";position:absolute;left:50%;bottom:-5px;width:10px;height:10px;background:#fff;transform:translateX(-50%) rotate(45deg)}
.mk-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 11px 5px 6px;border-radius:999px;background:#0f766e;color:#fff;font-size:.8rem;font-weight:700;white-space:nowrap}
.mk-pill i{width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:inset 0 0 0 4px #0f766e,0 0 0 2px #fff}
.mk-sec{--mk-ink:#0f172a;--mk-muted:#475569;background:#fff;border:1px solid #e2e8f0;border-radius:22px;padding:22px;color:var(--mk-ink)}
.mk-sec h2{font-size:1.2rem;letter-spacing:-.01em}
.mk-sec>p{margin-top:4px;margin-bottom:16px;font-size:.9rem;color:var(--mk-muted)}
.mk-layer{list-style:none}
.mk-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:12px;list-style:none;font-size:.82rem;color:var(--mk-muted)}
.mk-legend li{display:flex;align-items:center;gap:7px}
.mk-legend i{width:12px;height:12px;border-radius:50%;background:#15803d}
.mk-legend i.late{background:#b45309}.mk-legend i.idle{background:#64748b}
.mk-card h3{font-size:1.05rem;padding-right:30px}
.mk-card p{font-size:.85rem;color:var(--mk-muted);margin-top:2px}
.mk-card .mk-rate{display:inline-flex;align-items:center;gap:4px;margin-top:8px;font-size:.85rem;font-weight:700;color:#0f172a}
.mk-card a{display:inline-block;margin-top:12px;padding:8px 14px;border-radius:999px;background:#be123c;color:#fff;font-size:.85rem;font-weight:700;text-decoration:none}
.mk-card a:hover{background:#9f1239}
.mk-x{position:absolute;top:10px;right:10px;width:30px;height:30px;display:grid;place-items:center;border:0;border-radius:50%;background:#f1f5f9;color:#0f172a;cursor:pointer}
.mk-x:hover{background:#e2e8f0}
.mk-sec :is(a,button):focus-visible{outline:3px solid #0f172a;outline-offset:2px}
.mk-gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px;list-style:none}
.mk-gallery li{display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:14px;min-height:120px;padding:18px 10px 14px;border-radius:14px;background:#eef1ec;font-size:.8rem;font-weight:600;color:var(--mk-muted)}
.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){.mk-sec{padding:16px}.mk-frame{aspect-ratio:4/3}}
JavaScript
// Marker popups: each pin is a button that opens its card; Escape or the close button returns focus to the pin.
document.querySelectorAll('[data-popups]').forEach(function (box) {
  var pins = [].slice.call(box.querySelectorAll('[aria-controls]'));
  function card(pin) { return document.getElementById(pin.getAttribute('aria-controls')); }
  function closeAll(except) {
    pins.forEach(function (p) { if (p !== except) { p.setAttribute('aria-expanded', 'false'); card(p).hidden = true; } });
  }
  pins.forEach(function (p) {
    p.addEventListener('click', function () {
      var open = p.getAttribute('aria-expanded') === 'true';
      closeAll(p); p.setAttribute('aria-expanded', String(!open)); card(p).hidden = open;
    });
  });
  box.querySelectorAll('[data-popup-close]').forEach(function (c) {
    c.addEventListener('click', function () {
      var pin = box.querySelector('[aria-controls="' + c.closest('[id]').id + '"]'); closeAll(); pin.focus();
    });
  });
  box.addEventListener('keydown', function (e) {
    if (e.key !== 'Escape') return;
    var open = pins.filter(function (p) { return p.getAttribute('aria-expanded') === 'true'; })[0];
    if (open) { closeAll(); open.focus(); }
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Pulsing status markers</span>
<section class="mk-sec card rounded-4 p-3 p-sm-4" aria-labelledby="mk-live-title">
  <h2 id="mk-live-title" class="h5 fw-bold mb-1">Couriers on the road</h2><p class="mk-sub text-body-secondary mb-3">Five couriers. The pulse shows who is moving; it stops for anyone on a break.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul class="list-unstyled m-0" aria-label="Courier positions">
      <li class="mk-at" style="left:30%;top:40%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Ana</span><span class="visually-hidden">Ana, on time, 4 minutes away</span></li>
      <li class="mk-at" style="left:58%;top:28%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Jonas</span><span class="visually-hidden">Jonas, on time, 9 minutes away</span></li>
      <li class="mk-at" style="left:46%;top:66%"><span class="mk-pulse late"></span><span class="mk-name" aria-hidden="true">Mette</span><span class="visually-hidden">Mette, running 6 minutes late</span></li>
      <li class="mk-at" style="left:76%;top:58%"><span class="mk-pulse idle"></span><span class="mk-name" aria-hidden="true">Karim</span><span class="visually-hidden">Karim, on a break</span></li>
      <li class="mk-at" style="left:20%;top:70%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Sofie</span><span class="visually-hidden">Sofie, on time, 2 minutes away</span></li>
    </ul>
  </div>
  <ul class="mk-legend list-inline small text-body-secondary mt-3 mb-0"><li class="list-inline-item d-inline-flex align-items-center gap-2 me-3"><i class="" aria-hidden="true"></i>On time</li><li class="list-inline-item d-inline-flex align-items-center gap-2 me-3"><i class="late" aria-hidden="true"></i>Running late</li><li class="list-inline-item d-inline-flex align-items-center gap-2 me-3"><i class="idle" aria-hidden="true"></i>On a break</li></ul>
</section></div>
<div><span class="lbl">Cluster count badges</span>
<section class="mk-sec card rounded-4 p-3 p-sm-4" aria-labelledby="mk-cluster-title">
  <h2 id="mk-cluster-title" class="h5 fw-bold mb-1">97 venues with outdoor seating</h2><p class="mk-sub text-body-secondary mb-3">Nearby markers are grouped; badge size follows the count.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul class="list-unstyled m-0" aria-label="Venues by area">
      <li class="mk-at" style="left:22%;top:48%"><span class="mk-cluster is-lg" aria-hidden="true">42</span><span class="visually-hidden">Nørrebro: 42 venues</span></li>
      <li class="mk-at" style="left:66%;top:22%"><span class="mk-cluster is-md" aria-hidden="true">18</span><span class="visually-hidden">Østerbro: 18 venues</span></li>
      <li class="mk-at" style="left:60%;top:52%"><span class="mk-cluster is-sm" aria-hidden="true">7</span><span class="visually-hidden">Indre By: 7 venues</span></li>
      <li class="mk-at" style="left:82%;top:64%"><span class="mk-cluster is-sm" aria-hidden="true">3</span><span class="visually-hidden">Christianshavn: 3 venues</span></li>
      <li class="mk-at" style="left:36%;top:74%"><span class="mk-cluster is-lg" aria-hidden="true">26</span><span class="visually-hidden">Vesterbro: 26 venues</span></li>
      <li class="mk-at" style="left:66%;top:86%"><span class="mk-cluster is-one" aria-hidden="true">1</span><span class="visually-hidden">Islands Brygge: 1 venue</span></li>
    </ul>
  </div>
</section></div>
<div><span class="lbl">Pins that open a popup card</span>
<section class="mk-sec card rounded-4 p-3 p-sm-4" data-popups aria-labelledby="mk-pop-title">
  <h2 id="mk-pop-title" class="h5 fw-bold mb-1">Tables free tonight</h2><p class="mk-sub text-body-secondary mb-3">Select a pin to see the restaurant. Press Escape to close the card.</p>
  <div class="mk-frame">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v1" aria-label="Høst Kitchen" style="left:34%;top:42%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v2" aria-label="Bar Lille" style="left:58%;top:30%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v3" aria-label="Pasta Ventura" style="left:50%;top:70%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <div class="mk-card card border-0" id="mk-v1" role="group" aria-label="Høst Kitchen" hidden>
        <h3 class="h6 fw-bold mb-0 pe-4">Høst Kitchen</h3><p class="small text-body-secondary mb-0">Nordic · ££ · Open until 22:00</p>
        <span class="d-inline-flex align-self-start align-items-center gap-1 small fw-bold mt-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.7<span class="visually-hidden"> out of 5</span></span><br>
        <a class="btn mt-2 align-self-start" href="#">Book a table</a>
        <button class="btn-close" type="button" data-popup-close aria-label="Close Høst Kitchen"></button>
      </div>
      <div class="mk-card card border-0" id="mk-v2" role="group" aria-label="Bar Lille" hidden>
        <h3 class="h6 fw-bold mb-0 pe-4">Bar Lille</h3><p class="small text-body-secondary mb-0">Wine bar · £ · Open until 01:00</p>
        <span class="d-inline-flex align-self-start align-items-center gap-1 small fw-bold mt-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.5<span class="visually-hidden"> out of 5</span></span><br>
        <a class="btn mt-2 align-self-start" href="#">Book a table</a>
        <button class="btn-close" type="button" data-popup-close aria-label="Close Bar Lille"></button>
      </div>
      <div class="mk-card card border-0" id="mk-v3" role="group" aria-label="Pasta Ventura" hidden>
        <h3 class="h6 fw-bold mb-0 pe-4">Pasta Ventura</h3><p class="small text-body-secondary mb-0">Italian · ££ · Opens 17:00</p>
        <span class="d-inline-flex align-self-start align-items-center gap-1 small fw-bold mt-2"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.6<span class="visually-hidden"> out of 5</span></span><br>
        <a class="btn mt-2 align-self-start" href="#">Book a table</a>
        <button class="btn-close" type="button" data-popup-close aria-label="Close Pasta Ventura"></button>
      </div>
  </div>
</section></div>
<div><span class="lbl">Marker style gallery</span>
<section class="mk-sec card rounded-4 p-3 p-sm-4" aria-labelledby="mk-gal-title">
  <h2 id="mk-gal-title" class="h5 fw-bold mb-1">Six marker shapes</h2><p class="mk-sub text-body-secondary mb-3">Pick one per map and keep it consistent; colour and shape together tell types apart.</p>
  <ul class="mk-gallery row row-cols-2 row-cols-sm-3 row-cols-lg-6 g-2 list-unstyled mb-0">
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><svg width="34" height="42" 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="#be123c" stroke="#fff" stroke-width="2.5"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg><span>Teardrop pin</span></div></li>
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><span class="mk-dot"></span><span>Dot with halo</span></div></li>
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><span class="mk-num">3</span><span>Numbered</span></div></li>
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><span class="mk-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 8h13v5a5 5 0 0 1-5 5H9a5 5 0 0 1-5-5zM17 9h1.5a2.5 2.5 0 0 1 0 5H17M8 2v3M12 2v3"/></svg></span><span>Icon pin</span></div></li>
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><span class="mk-price">€24</span><span>Price tag</span></div></li>
    <li class="col"><div class="d-flex flex-column align-items-center justify-content-end gap-3 h-100 rounded-4 px-2 pt-4 pb-3 small fw-semibold text-body-secondary" style="background:#eef1ec;min-height:120px"><span class="mk-pill"><i></i>Ferry stop</span><span>Label pill</span></div></li>
  </ul>
</section></div>
CSS
/* Map markers drawn with CSS and inline SVG over an illustrated map (not to scale). */
.mk-frame{position:relative;aspect-ratio:16/9;min-height:300px;width:100%;border-radius:18px;overflow:hidden;background:#eef1ec}
.mk-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.mk-at{position:absolute;transform:translate(-50%,-50%)}
/* 1. pulsing status markers */
.mk-pulse{--c:#15803d;position:relative;display:block;width:18px;height:18px;border-radius:50%;background:var(--c);border:3px solid #fff;box-shadow:0 2px 6px rgba(15,23,42,.35)}
.mk-pulse::before,.mk-pulse::after{content:"";position:absolute;inset:-3px;border-radius:50%;background:var(--c);opacity:.45;z-index:-1;animation:mk-pulse 2s ease-out infinite}
.mk-pulse::after{animation-delay:1s}
.mk-pulse.late{--c:#b45309}.mk-pulse.idle{--c:#64748b}
.mk-pulse.idle::before,.mk-pulse.idle::after{animation:none;opacity:0}
.mk-name{position:absolute;left:50%;top:calc(100% + 6px);transform:translateX(-50%);padding:2px 8px;border-radius:7px;background:#fff;color:#0f172a;font-size:.72rem;font-weight:700;white-space:nowrap;box-shadow:0 1px 3px rgba(15,23,42,.2)}
@keyframes mk-pulse{from{transform:scale(1);opacity:.5}to{transform:scale(3.2);opacity:0}}
/* 2. cluster badges */
.mk-cluster{display:grid;place-items:center;border-radius:50%;background:#0f766e;color:#fff;font-weight:800;box-shadow:0 0 0 6px rgba(15,118,110,.25),0 0 0 12px rgba(15,118,110,.12)}
.mk-cluster.is-lg{width:58px;height:58px;font-size:1.05rem}
.mk-cluster.is-md{width:46px;height:46px;font-size:.95rem}
.mk-cluster.is-sm{width:36px;height:36px;font-size:.85rem}
.mk-cluster.is-one{width:22px;height:22px;font-size:.7rem;box-shadow:0 0 0 4px rgba(15,118,110,.25)}
/* 3. popup cards */
.mk-btn{position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:#be123c;cursor:pointer;transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s}
.mk-btn svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(15,23,42,.3))}
.mk-btn:hover,.mk-btn[aria-expanded="true"]{transform:translate(-50%,-100%) scale(1.18)}
.mk-btn[aria-expanded="true"]{color:#0f172a}
.mk-btn:focus-visible{outline:3px solid #0f172a;outline-offset:2px;border-radius:8px}
.mk-card{position:absolute;left:12px;bottom:12px;z-index:3;width:min(300px,calc(100% - 24px));padding:16px 16px 14px;border-radius:14px;background:#fff;color:#0f172a;box-shadow:0 18px 40px -16px rgba(15,23,42,.45)}
.mk-card[hidden]{display:none}
@media (prefers-reduced-motion:reduce){.mk-pulse::before,.mk-pulse::after{animation:none;opacity:0}.mk-btn{transition:none}}
.mk-dot{display:block;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 7px rgba(37,99,235,.25),0 2px 5px rgba(15,23,42,.3)}
.mk-num{display:grid;place-items:center;width:30px;height:30px;border-radius:9px 9px 9px 2px;background:#0f172a;color:#fff;font-size:.85rem;font-weight:800;box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon{display:grid;place-items:center;width:38px;height:38px;border-radius:50% 50% 50% 0;background:#7c3aed;transform:rotate(-45deg);box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon svg{transform:rotate(45deg)}
.mk-price{position:relative;display:block;padding:5px 10px;border-radius:999px;background:#fff;color:#0f172a;font-size:.85rem;font-weight:800;box-shadow:0 2px 8px rgba(15,23,42,.25)}
.mk-price::after{content:"";position:absolute;left:50%;bottom:-5px;width:10px;height:10px;background:#fff;transform:translateX(-50%) rotate(45deg)}
.mk-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 11px 5px 6px;border-radius:999px;background:#0f766e;color:#fff;font-size:.8rem;font-weight:700;white-space:nowrap}
.mk-pill i{width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:inset 0 0 0 4px #0f766e,0 0 0 2px #fff}
.mk-sec{--bs-card-border-color:#e2e8f0;color:#0f172a}
.mk-sub{font-size:.9rem}
.mk-legend i{width:12px;height:12px;border-radius:50%;background:#15803d}
.mk-legend i.late{background:#b45309}.mk-legend i.idle{background:#64748b}
.mk-card .btn{--bs-btn-padding-y:8px;--bs-btn-padding-x:14px;--bs-btn-font-size:.85rem;--bs-btn-font-weight:700;--bs-btn-border-radius:999px;--bs-btn-color:#fff;--bs-btn-bg:#be123c;--bs-btn-border-color:#be123c;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#9f1239;--bs-btn-hover-border-color:#9f1239}
.mk-card .btn-close{position:absolute;top:10px;right:10px;width:30px;height:30px;padding:0;border-radius:50%;background-color:#f1f5f9;background-size:12px;opacity:1}
.mk-sec :is(a,button):focus-visible{outline:3px solid #0f172a;outline-offset:2px;box-shadow:none}
@media (max-width:640px){.mk-frame{aspect-ratio:4/3}}
JavaScript
// Marker popups: each pin is a button that opens its card; Escape or the close button returns focus to the pin.
document.querySelectorAll('[data-popups]').forEach(function (box) {
  var pins = [].slice.call(box.querySelectorAll('[aria-controls]'));
  function card(pin) { return document.getElementById(pin.getAttribute('aria-controls')); }
  function closeAll(except) {
    pins.forEach(function (p) { if (p !== except) { p.setAttribute('aria-expanded', 'false'); card(p).hidden = true; } });
  }
  pins.forEach(function (p) {
    p.addEventListener('click', function () {
      var open = p.getAttribute('aria-expanded') === 'true';
      closeAll(p); p.setAttribute('aria-expanded', String(!open)); card(p).hidden = open;
    });
  });
  box.querySelectorAll('[data-popup-close]').forEach(function (c) {
    c.addEventListener('click', function () {
      var pin = box.querySelector('[aria-controls="' + c.closest('[id]').id + '"]'); closeAll(); pin.focus();
    });
  });
  box.addEventListener('keydown', function (e) {
    if (e.key !== 'Escape') return;
    var open = pins.filter(function (p) { return p.getAttribute('aria-expanded') === 'true'; })[0];
    if (open) { closeAll(); open.focus(); }
  });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Pulsing status markers</span>
<section class="rounded-[22px] border border-[#e2e8f0] bg-white p-4 text-[#0f172a] sm:p-[22px]" aria-labelledby="mk-live-title">
  <h2 id="mk-live-title" class="text-[1.2rem] font-bold tracking-[-.01em]">Couriers on the road</h2><p class="mt-1 mb-4 text-[.9rem] text-[#475569]">Five couriers. The pulse shows who is moving; it stops for anyone on a break.</p>
  <div class="mk-frame max-sm:aspect-[4/3]">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul aria-label="Courier positions">
      <li class="mk-at" style="left:30%;top:40%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Ana</span><span class="sr-only">Ana, on time, 4 minutes away</span></li>
      <li class="mk-at" style="left:58%;top:28%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Jonas</span><span class="sr-only">Jonas, on time, 9 minutes away</span></li>
      <li class="mk-at" style="left:46%;top:66%"><span class="mk-pulse late"></span><span class="mk-name" aria-hidden="true">Mette</span><span class="sr-only">Mette, running 6 minutes late</span></li>
      <li class="mk-at" style="left:76%;top:58%"><span class="mk-pulse idle"></span><span class="mk-name" aria-hidden="true">Karim</span><span class="sr-only">Karim, on a break</span></li>
      <li class="mk-at" style="left:20%;top:70%"><span class="mk-pulse ok"></span><span class="mk-name" aria-hidden="true">Sofie</span><span class="sr-only">Sofie, on time, 2 minutes away</span></li>
    </ul>
  </div>
  <ul class="mt-3 flex flex-wrap gap-x-[18px] gap-y-1.5 text-[.82rem] text-[#475569]"><li class="flex items-center gap-[7px]"><i class="size-3 rounded-full bg-[#15803d]" aria-hidden="true"></i>On time</li><li class="flex items-center gap-[7px]"><i class="size-3 rounded-full bg-[#b45309]" aria-hidden="true"></i>Running late</li><li class="flex items-center gap-[7px]"><i class="size-3 rounded-full bg-[#64748b]" aria-hidden="true"></i>On a break</li></ul>
</section></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Cluster count badges</span>
<section class="rounded-[22px] border border-[#e2e8f0] bg-white p-4 text-[#0f172a] sm:p-[22px]" aria-labelledby="mk-cluster-title">
  <h2 id="mk-cluster-title" class="text-[1.2rem] font-bold tracking-[-.01em]">97 venues with outdoor seating</h2><p class="mt-1 mb-4 text-[.9rem] text-[#475569]">Nearby markers are grouped; badge size follows the count.</p>
  <div class="mk-frame max-sm:aspect-[4/3]">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
    <ul aria-label="Venues by area">
      <li class="mk-at" style="left:22%;top:48%"><span class="mk-cluster is-lg" aria-hidden="true">42</span><span class="sr-only">Nørrebro: 42 venues</span></li>
      <li class="mk-at" style="left:66%;top:22%"><span class="mk-cluster is-md" aria-hidden="true">18</span><span class="sr-only">Østerbro: 18 venues</span></li>
      <li class="mk-at" style="left:60%;top:52%"><span class="mk-cluster is-sm" aria-hidden="true">7</span><span class="sr-only">Indre By: 7 venues</span></li>
      <li class="mk-at" style="left:82%;top:64%"><span class="mk-cluster is-sm" aria-hidden="true">3</span><span class="sr-only">Christianshavn: 3 venues</span></li>
      <li class="mk-at" style="left:36%;top:74%"><span class="mk-cluster is-lg" aria-hidden="true">26</span><span class="sr-only">Vesterbro: 26 venues</span></li>
      <li class="mk-at" style="left:66%;top:86%"><span class="mk-cluster is-one" aria-hidden="true">1</span><span class="sr-only">Islands Brygge: 1 venue</span></li>
    </ul>
  </div>
</section></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Pins that open a popup card</span>
<section class="rounded-[22px] border border-[#e2e8f0] bg-white p-4 text-[#0f172a] sm:p-[22px]" data-popups aria-labelledby="mk-pop-title">
  <h2 id="mk-pop-title" class="text-[1.2rem] font-bold tracking-[-.01em]">Tables free tonight</h2><p class="mt-1 mb-4 text-[.9rem] text-[#475569]">Select a pin to see the restaurant. Press Escape to close the card.</p>
  <div class="mk-frame max-sm:aspect-[4/3]">
    <svg class="" viewBox="0 0 600 450" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
      <rect width="600" height="450" fill="#eef1ec"/>
      <g stroke="#e3e7df" 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="#bcd9ea"/>
      <path d="M472 74C458 150 446 205 436 250C420 320 382 385 330 450" fill="none" stroke="#bcd9ea" stroke-width="28"/>
      <path d="M528 340C505 306 474 294 440 288" fill="none" stroke="#bcd9ea" stroke-width="9" stroke-linecap="round"/>
      <g fill="#bcd9ea"><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="#d3e6cf"><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="#d9ddd3" 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>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v1" aria-label="Høst Kitchen" style="left:34%;top:42%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v2" aria-label="Bar Lille" style="left:58%;top:30%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <button class="mk-btn" type="button" aria-expanded="false" aria-controls="mk-v3" aria-label="Pasta Ventura" style="left:50%;top:70%"><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"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg></button>
      <div class="mk-card" id="mk-v1" role="group" aria-label="Høst Kitchen" hidden>
        <h3 class="pr-[30px] text-[1.05rem] font-bold">Høst Kitchen</h3><p class="mt-0.5 text-[.85rem] text-[#475569]">Nordic · ££ · Open until 22:00</p>
        <span class="mt-2 inline-flex items-center gap-1 text-[.85rem] font-bold"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.7<span class="sr-only"> out of 5</span></span><br>
        <a class="mt-3 inline-block rounded-full bg-[#be123c] px-3.5 py-2 text-[.85rem] font-bold text-white hover:bg-[#9f1239] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" href="#">Book a table</a>
        <button class="absolute top-2.5 right-2.5 grid size-[30px] cursor-pointer place-items-center rounded-full bg-[#f1f5f9] hover:bg-[#e2e8f0] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" type="button" data-popup-close aria-label="Close Høst Kitchen"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
      <div class="mk-card" id="mk-v2" role="group" aria-label="Bar Lille" hidden>
        <h3 class="pr-[30px] text-[1.05rem] font-bold">Bar Lille</h3><p class="mt-0.5 text-[.85rem] text-[#475569]">Wine bar · £ · Open until 01:00</p>
        <span class="mt-2 inline-flex items-center gap-1 text-[.85rem] font-bold"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.5<span class="sr-only"> out of 5</span></span><br>
        <a class="mt-3 inline-block rounded-full bg-[#be123c] px-3.5 py-2 text-[.85rem] font-bold text-white hover:bg-[#9f1239] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" href="#">Book a table</a>
        <button class="absolute top-2.5 right-2.5 grid size-[30px] cursor-pointer place-items-center rounded-full bg-[#f1f5f9] hover:bg-[#e2e8f0] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" type="button" data-popup-close aria-label="Close Bar Lille"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
      <div class="mk-card" id="mk-v3" role="group" aria-label="Pasta Ventura" hidden>
        <h3 class="pr-[30px] text-[1.05rem] font-bold">Pasta Ventura</h3><p class="mt-0.5 text-[.85rem] text-[#475569]">Italian · ££ · Opens 17:00</p>
        <span class="mt-2 inline-flex items-center gap-1 text-[.85rem] font-bold"><svg width="14" height="14" viewBox="0 0 24 24" fill="#b45309" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>4.6<span class="sr-only"> out of 5</span></span><br>
        <a class="mt-3 inline-block rounded-full bg-[#be123c] px-3.5 py-2 text-[.85rem] font-bold text-white hover:bg-[#9f1239] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" href="#">Book a table</a>
        <button class="absolute top-2.5 right-2.5 grid size-[30px] cursor-pointer place-items-center rounded-full bg-[#f1f5f9] hover:bg-[#e2e8f0] focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#0f172a]" type="button" data-popup-close aria-label="Close Pasta Ventura"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
      </div>
  </div>
</section></div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Marker style gallery</span>
<section class="rounded-[22px] border border-[#e2e8f0] bg-white p-4 text-[#0f172a] sm:p-[22px]" aria-labelledby="mk-gal-title">
  <h2 id="mk-gal-title" class="text-[1.2rem] font-bold tracking-[-.01em]">Six marker shapes</h2><p class="mt-1 mb-4 text-[.9rem] text-[#475569]">Pick one per map and keep it consistent; colour and shape together tell types apart.</p>
  <ul class="grid grid-cols-[repeat(auto-fill,minmax(130px,1fr))] gap-3">
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><svg width="34" height="42" 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="#be123c" stroke="#fff" stroke-width="2.5"/><circle cx="17" cy="16" r="5.5" fill="#fff"/></svg><span>Teardrop pin</span></li>
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><span class="mk-dot"></span><span>Dot with halo</span></li>
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><span class="mk-num">3</span><span>Numbered</span></li>
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><span class="mk-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 8h13v5a5 5 0 0 1-5 5H9a5 5 0 0 1-5-5zM17 9h1.5a2.5 2.5 0 0 1 0 5H17M8 2v3M12 2v3"/></svg></span><span>Icon pin</span></li>
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><span class="mk-price">€24</span><span>Price tag</span></li>
    <li class="flex min-h-[120px] flex-col items-center justify-end gap-3.5 rounded-[14px] bg-[#eef1ec] px-2.5 pt-[18px] pb-3.5 text-[.8rem] font-semibold text-[#475569]"><span class="mk-pill"><i></i>Ferry stop</span><span>Label pill</span></li>
  </ul>
</section></div>
CSS
@layer components {
/* Map markers drawn with CSS and inline SVG over an illustrated map (not to scale). */
.mk-frame{position:relative;aspect-ratio:16/9;min-height:300px;width:100%;border-radius:18px;overflow:hidden;background:#eef1ec}
.mk-frame>svg{position:absolute;inset:0;width:100%;height:100%}
.mk-at{position:absolute;transform:translate(-50%,-50%)}
/* 1. pulsing status markers */
.mk-pulse{--c:#15803d;position:relative;display:block;width:18px;height:18px;border-radius:50%;background:var(--c);border:3px solid #fff;box-shadow:0 2px 6px rgba(15,23,42,.35)}
.mk-pulse::before,.mk-pulse::after{content:"";position:absolute;inset:-3px;border-radius:50%;background:var(--c);opacity:.45;z-index:-1;animation:mk-pulse 2s ease-out infinite}
.mk-pulse::after{animation-delay:1s}
.mk-pulse.late{--c:#b45309}.mk-pulse.idle{--c:#64748b}
.mk-pulse.idle::before,.mk-pulse.idle::after{animation:none;opacity:0}
.mk-name{position:absolute;left:50%;top:calc(100% + 6px);transform:translateX(-50%);padding:2px 8px;border-radius:7px;background:#fff;color:#0f172a;font-size:.72rem;font-weight:700;white-space:nowrap;box-shadow:0 1px 3px rgba(15,23,42,.2)}
@keyframes mk-pulse{from{transform:scale(1);opacity:.5}to{transform:scale(3.2);opacity:0}}
/* 2. cluster badges */
.mk-cluster{display:grid;place-items:center;border-radius:50%;background:#0f766e;color:#fff;font-weight:800;box-shadow:0 0 0 6px rgba(15,118,110,.25),0 0 0 12px rgba(15,118,110,.12)}
.mk-cluster.is-lg{width:58px;height:58px;font-size:1.05rem}
.mk-cluster.is-md{width:46px;height:46px;font-size:.95rem}
.mk-cluster.is-sm{width:36px;height:36px;font-size:.85rem}
.mk-cluster.is-one{width:22px;height:22px;font-size:.7rem;box-shadow:0 0 0 4px rgba(15,118,110,.25)}
/* 3. popup cards */
.mk-btn{position:absolute;width:34px;height:42px;padding:0;border:0;background:none;color:#be123c;cursor:pointer;transform:translate(-50%,-100%);transform-origin:50% 100%;transition:transform .18s}
.mk-btn svg{display:block;width:100%;height:100%;filter:drop-shadow(0 3px 4px rgba(15,23,42,.3))}
.mk-btn:hover,.mk-btn[aria-expanded="true"]{transform:translate(-50%,-100%) scale(1.18)}
.mk-btn[aria-expanded="true"]{color:#0f172a}
.mk-btn:focus-visible{outline:3px solid #0f172a;outline-offset:2px;border-radius:8px}
.mk-card{position:absolute;left:12px;bottom:12px;z-index:3;width:min(300px,calc(100% - 24px));padding:16px 16px 14px;border-radius:14px;background:#fff;color:#0f172a;box-shadow:0 18px 40px -16px rgba(15,23,42,.45)}
.mk-card[hidden]{display:none}
@media (prefers-reduced-motion:reduce){.mk-pulse::before,.mk-pulse::after{animation:none;opacity:0}.mk-btn{transition:none}}
.mk-dot{display:block;width:16px;height:16px;border-radius:50%;background:#2563eb;border:3px solid #fff;box-shadow:0 0 0 7px rgba(37,99,235,.25),0 2px 5px rgba(15,23,42,.3)}
.mk-num{display:grid;place-items:center;width:30px;height:30px;border-radius:9px 9px 9px 2px;background:#0f172a;color:#fff;font-size:.85rem;font-weight:800;box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon{display:grid;place-items:center;width:38px;height:38px;border-radius:50% 50% 50% 0;background:#7c3aed;transform:rotate(-45deg);box-shadow:0 3px 6px rgba(15,23,42,.3)}
.mk-icon svg{transform:rotate(45deg)}
.mk-price{position:relative;display:block;padding:5px 10px;border-radius:999px;background:#fff;color:#0f172a;font-size:.85rem;font-weight:800;box-shadow:0 2px 8px rgba(15,23,42,.25)}
.mk-price::after{content:"";position:absolute;left:50%;bottom:-5px;width:10px;height:10px;background:#fff;transform:translateX(-50%) rotate(45deg)}
.mk-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 11px 5px 6px;border-radius:999px;background:#0f766e;color:#fff;font-size:.8rem;font-weight:700;white-space:nowrap}
.mk-pill i{width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:inset 0 0 0 4px #0f766e,0 0 0 2px #fff}
}
JavaScript
// Marker popups: each pin is a button that opens its card; Escape or the close button returns focus to the pin.
document.querySelectorAll('[data-popups]').forEach(function (box) {
  var pins = [].slice.call(box.querySelectorAll('[aria-controls]'));
  function card(pin) { return document.getElementById(pin.getAttribute('aria-controls')); }
  function closeAll(except) {
    pins.forEach(function (p) { if (p !== except) { p.setAttribute('aria-expanded', 'false'); card(p).hidden = true; } });
  }
  pins.forEach(function (p) {
    p.addEventListener('click', function () {
      var open = p.getAttribute('aria-expanded') === 'true';
      closeAll(p); p.setAttribute('aria-expanded', String(!open)); card(p).hidden = open;
    });
  });
  box.querySelectorAll('[data-popup-close]').forEach(function (c) {
    c.addEventListener('click', function () {
      var pin = box.querySelector('[aria-controls="' + c.closest('[id]').id + '"]'); closeAll(); pin.focus();
    });
  });
  box.addEventListener('keydown', function (e) {
    if (e.key !== 'Escape') return;
    var open = pins.filter(function (p) { return p.getAttribute('aria-expanded') === 'true'; })[0];
    if (open) { closeAll(); open.focus(); }
  });
});

About this set

A set of map markers drawn with CSS and small inline SVGs, shown over an illustrated city map so you can see them in context. Pulsing status markers show five couriers, with a double ripple for moving couriers, an amber colour for one running late and no pulse for one on a break; each marker has a name tag and a text description of its status. Cluster count badges group nearby venues and scale between four sizes with the count, with soft rings around each badge. Popup pins are buttons that open a card docked at the bottom of the map with the restaurant’s cuisine, hours, rating and a booking link; only one card is open at a time, the close button and Escape return focus to the pin, and the card works at phone width because it never has to fit above the pin. A gallery shows six more shapes to choose from: teardrop pin, dot with halo, numbered marker, icon pin, price tag and label pill. The same markers work as Leaflet divIcons, which set 06 uses.

What’s included

  • Pulsing markers that stop for idle items and for reduced motion
  • Cluster badges in four sizes with the count in text
  • Popup cards opened by pin buttons, closed with Escape
  • Docked popup card that fits at phone width
  • Gallery of six marker shapes made with CSS

Accessibility

Status markers and clusters are list items with visually hidden text, such as "Mette, running 6 minutes late", so colour is never the only signal. Popup pins are buttons with an accessible name, aria-expanded and aria-controls; each card is a labelled group, and closing it returns focus to its pin. Pins and buttons show a focus outline, and the pulse animation and pin scaling are switched off under prefers-reduced-motion.

Customise it

Change --c on .mk-pulse for status colours, the background of .mk-cluster for clusters, and the pin colour through the color of .mk-btn. Marker sizes are plain width and height values in each rule.