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.