Skip to content

Store Locator List

A copy-paste maps & locations component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.

Maps & LocationsHTMLJavaScriptCSSany framework

Copy into your project

HTML
<div class="nuda-ml2-storelist">
  <div class="nuda-ml2-storelist__map" aria-hidden="true">
    <span class="nuda-ml2-storelist__dot" style="left:22%;top:65%"></span>
    <span class="nuda-ml2-storelist__dot nuda-ml2-storelist__dot--active" style="left:55%;top:30%"></span>
    <span class="nuda-ml2-storelist__dot" style="left:80%;top:60%"></span>
  </div>
  <ul class="nuda-ml2-storelist__list">
    <li><button type="button" class="nuda-ml2-storelist__item" data-dot="0">Downtown <span>1.2 mi</span></button></li>
    <li><button type="button" class="nuda-ml2-storelist__item nuda-ml2-storelist__item--active" data-dot="1" aria-pressed="true">Midtown <span>0.4 mi</span></button></li>
    <li><button type="button" class="nuda-ml2-storelist__item" data-dot="2">Riverside <span>2.8 mi</span></button></li>
  </ul>
</div>
JavaScript
document.querySelectorAll(".nuda-ml2-storelist__item").forEach((btn) => {
  btn.addEventListener("click", () => {
    const list = btn.closest(".nuda-ml2-storelist");
    list.querySelectorAll(".nuda-ml2-storelist__item").forEach((el) => {
      el.classList.remove("nuda-ml2-storelist__item--active");
      el.setAttribute("aria-pressed", "false");
    });
    btn.classList.add("nuda-ml2-storelist__item--active");
    btn.setAttribute("aria-pressed", "true");

    const dots = list.querySelectorAll(".nuda-ml2-storelist__dot");
    dots.forEach((d) => d.classList.remove("nuda-ml2-storelist__dot--active"));
    dots[Number(btn.dataset.dot)]?.classList.add("nuda-ml2-storelist__dot--active");
  });
});
CSS
.nuda-ml2-storelist {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 260px;
  max-width: 100%;
}

.nuda-ml2-storelist__map {
  position: relative;
  width: 100%;
  height: 90px;
  border-radius: 10px;
  background: linear-gradient(135deg,#111114,#1a1a20);
  border: 1px solid rgba(255,255,255,.08);
  overflow: hidden;
}

.nuda-ml2-storelist__dot {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #63636e;
  transform: translate(-50%,-50%);
  transition: background .25s,box-shadow .25s,transform .25s;
}

.nuda-ml2-storelist__dot--active {
  background: #e4ff54;
  box-shadow: 0 0 10px rgba(228,255,84,.7);
  transform: translate(-50%,-50%) scale(1.3);
}

.nuda-ml2-storelist__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.nuda-ml2-storelist__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,.08);
  background: #161616;
  color: #cfcfcf;
  font-size: 12px;
  cursor: pointer;
  transition: background .2s,border-color .2s,color .2s;
}

.nuda-ml2-storelist__item span {
  color: #777;
  font-size: 11px;
}

.nuda-ml2-storelist__item--active {
  background: rgba(228,255,84,.1);
  border-color: #e4ff54;
  color: #fafafa;
}

.nuda-ml2-storelist__item--active span {
  color: #e4ff54;
}

.nuda-ml2-storelist__item:focus-visible {
  outline: 2px solid #e4ff54;
  outline-offset: 2px;
}

@media (prefers-reduced-motion:reduce) {
  .nuda-ml2-storelist__dot {
    transition: none;
  }
  .nuda-ml2-storelist__item {
    transition: none;
  }
}

How to use Store Locator List

Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.

Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.

More maps & locations components

← Browse all NudaUI components