Kompas dat naar je cursor wijst New
A compass whose needle swings springily towards your cursor wherever it is on the page (towards your finger on a phone). Point at a rental location, focus it or tap it, and the needle turns to the real bearing from the station with the distance below. For the contact or route page of a bike rental, water sports school, outdoor shop or business with several locations.
Fietsverhuur rond Zwolle
Gemeten vanaf station Zwolle.
How to use it
- Paste the HTML where the components should appear.
- Add the CSS to your stylesheet. All classes are prefixed, so nothing clashes with your own styles.
- Add the JavaScript and call init() with the element that contains the HTML.
<!-- Kompas dat naar je cursor wijst · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="kmp">
<svg class="kmp__roos" viewBox="0 0 120 120" aria-hidden="true">
<circle cx="60" cy="60" r="57" fill="#fff" stroke="#0a0a0a" stroke-width="2"/>
<g class="kmp__streepjes" stroke="#8a857e" stroke-width="1.2"></g>
<text x="60" y="22" text-anchor="middle" class="kmp__wind kmp__wind--n">N</text>
<text x="101" y="65" text-anchor="middle" class="kmp__wind">O</text>
<text x="60" y="108" text-anchor="middle" class="kmp__wind">Z</text>
<text x="19" y="65" text-anchor="middle" class="kmp__wind">W</text>
<g class="kmp__naald"><path d="M60 24 L66 60 L54 60 Z" fill="#F5480F"/><path d="M60 96 L66 60 L54 60 Z" fill="#0a0a0a"/><circle cx="60" cy="60" r="4.5" fill="#fff" stroke="#0a0a0a" stroke-width="2"/></g>
</svg>
<div class="kmp__info">
<p class="kmp__kop">Fietsverhuur rond Zwolle</p>
<div class="kmp__lijst">
<button type="button" class="kmp__punt" data-graden="356" data-tekst="Meppel ligt 22 km naar het noorden.">Meppel <span>22 km</span></button>
<button type="button" class="kmp__punt" data-graden="282" data-tekst="Kampen ligt 14 km naar het westen.">Kampen <span>14 km</span></button>
<button type="button" class="kmp__punt" data-graden="186" data-tekst="Deventer ligt 32 km naar het zuiden.">Deventer <span>32 km</span></button>
</div>
<p class="kmp__stand" aria-live="polite">Gemeten vanaf station Zwolle.</p>
</div>
</div>
</div>/* Kompas dat naar je cursor wijst · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.kmp{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:20px;padding:14px 18px;box-sizing:border-box;background:#f4f3f1;font-family:Switzer,sans-serif;touch-action:pan-y}
.kmp__roos{flex:0 0 auto;width:min(42%,170px);height:auto;filter:drop-shadow(0 6px 14px rgba(10,10,10,.1))}
.kmp__wind{font-size:14px;font-weight:600;fill:#0a0a0a;font-family:Switzer,sans-serif}
.kmp__wind--n{fill:#F5480F}
.kmp__info{flex:1 1 auto;min-width:0;max-width:200px}
.kmp__kop{margin:0 0 8px;font-size:16px;font-weight:600;color:#0a0a0a;line-height:1.2}
.kmp__lijst{display:flex;flex-direction:column;gap:4px}
.kmp__punt{font:inherit;font-size:14px;font-weight:500;display:flex;justify-content:space-between;align-items:center;min-height:30px;padding:0 10px;border:1px solid #d6d3ce;border-radius:4px;background:#fff;color:#0a0a0a;cursor:pointer;text-align:left;transition:border-color .15s,background-color .15s}
.kmp__punt span{font-size:13px;color:#55524d;font-weight:400}
.kmp__punt:hover,.kmp__punt.is-actief{border-color:#0a0a0a}
.kmp__punt.is-actief{background:#0a0a0a;color:#fff}
.kmp__punt.is-actief span{color:#d6d3ce}
.kmp__punt:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.kmp__stand{margin:8px 0 0;font-size:12px;line-height:1.35;color:#55524d}
@media (max-width:420px){.kmp{gap:12px;padding:10px 12px}.kmp__roos{width:40%}.kmp__kop{font-size:15px;margin-bottom:6px}.kmp__punt{min-height:28px;font-size:13px}}// Kompas dat naar je cursor wijst · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie
// root = het element waarin de HTML staat
function init(root){
const w = root.querySelector('.kmp'); if (!w) return;
const roos = w.querySelector('.kmp__roos'), naald = w.querySelector('.kmp__naald'), stand = w.querySelector('.kmp__stand');
const punten = [...w.querySelectorAll('.kmp__punt')], streep = w.querySelector('.kmp__streepjes');
const rm = matchMedia('(prefers-reduced-motion: reduce)').matches;
for (let i = 0; i < 36; i++){
if (i % 9 === 0) continue;
const a = i * 10 * Math.PI / 180, r1 = i % 3 === 0 ? 46 : 49;
const l = document.createElementNS('http://www.w3.org/2000/svg', 'line');
l.setAttribute('x1', (60 + Math.sin(a) * r1).toFixed(2)); l.setAttribute('y1', (60 - Math.cos(a) * r1).toFixed(2));
l.setAttribute('x2', (60 + Math.sin(a) * 53).toFixed(2)); l.setAttribute('y2', (60 - Math.cos(a) * 53).toFixed(2));
streep.appendChild(l);
}
let a = 0, v = 0, doel = 0, vast = null, raf = 0, zichtbaar = true;
function tik(){
raf = 0; if (!zichtbaar) return;
const d = ((doel - a + 540) % 360) - 180;
if (rm) { a = doel; v = 0; } else { v += d * 0.07; v *= 0.8; a += v; }
naald.setAttribute('transform', 'rotate(' + a.toFixed(2) + ' 60 60)');
if (!rm && Math.abs(v) + Math.abs(d) > 0.05) raf = requestAnimationFrame(tik);
}
const start = () => { if (!raf) raf = requestAnimationFrame(tik); };
function richt(x, y){
if (vast) return;
const r = roos.getBoundingClientRect(); const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
if (Math.hypot(x - cx, y - cy) < 8) return;
doel = Math.atan2(x - cx, -(y - cy)) * 180 / Math.PI; start();
}
document.addEventListener('pointermove', e => { if (zichtbaar && e.pointerType === 'mouse') richt(e.clientX, e.clientY); }, { passive: true });
w.addEventListener('pointerdown', e => { if (!e.target.closest('.kmp__punt')) { kies(null); richt(e.clientX, e.clientY); } });
function kies(p){
vast = p; punten.forEach(q => { q.classList.toggle('is-actief', q === p); q.setAttribute('aria-pressed', String(q === p)); });
if (p) { doel = +p.dataset.graden; stand.textContent = p.dataset.tekst; start(); }
else stand.textContent = 'Gemeten vanaf station Zwolle.';
}
punten.forEach(p => {
p.setAttribute('aria-pressed', 'false');
p.addEventListener('pointerenter', e => { if (e.pointerType === 'mouse') kies(p); });
p.addEventListener('focus', () => kies(p));
p.addEventListener('click', () => kies(p));
});
w.querySelector('.kmp__lijst').addEventListener('pointerleave', e => { if (e.pointerType === 'mouse' && !w.contains(document.activeElement)) kies(null); });
w.addEventListener('focusout', () => setTimeout(() => { if (!w.contains(document.activeElement)) kies(null); }, 0));
if ('IntersectionObserver' in window) new IntersectionObserver(es => { zichtbaar = es[0].isIntersecting; if (zichtbaar && !rm && !vast) { v = 9; start(); } }).observe(w);
tik();
}
init(document.querySelector('.jouw-container'));
More components
Seen something you like?
Add it to your selection and send it over. We build your site around it, in your brand.
Get in touch →