Knikkerbord dat je kantelt New
A board with a marble and three holes: tilt it with your mouse, finger or the arrow keys and the marble rolls, bounces off the bars and drops into a hole; every hole is an ordinary link to that group. Anyone who does not want to play clicks or tabs straight to a hole. For the homepage of a daycare, playground or swimming pool, as a playful choice between groups or departments. With reduced motion the board does not tilt and the marble moves per key press.
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.
<!-- Knikkerbord dat je kantelt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie -->
<div class="jouw-container">
<div class="fyk">
<div class="fyk__bord" tabindex="0" role="group" aria-label="Knikkerbord. Kantel met de pijltjestoetsen, of tab naar een groep.">
<h3 class="fyk__h">Welke groep past bij je kind?</h3>
<p class="fyk__p">Kantel het bord met muis, vinger of pijltjes.</p>
<span class="fyk__lat" data-r=".34,.50,.30,.035" aria-hidden="true"></span>
<span class="fyk__lat" data-r=".67,.58,.025,.32" aria-hidden="true"></span>
<a class="fyk__gat" href="#fy-knikkerbord" data-p=".14,.80" data-t="Baby's, 0 tot 2 jaar: vaste leidster en een eigen slaapkamer."><span class="fyk__put" aria-hidden="true"></span>Baby's</a>
<a class="fyk__gat" href="#fy-knikkerbord" data-p=".42,.78" data-t="Peuters, 2 tot 4 jaar: buiten spelen in de moestuin."><span class="fyk__put" aria-hidden="true"></span>Peuters</a>
<a class="fyk__gat" href="#fy-knikkerbord" data-p=".80,.80" data-t="BSO, 4 tot 12 jaar: ophalen bij vier scholen in Zwolle-Zuid."><span class="fyk__put" aria-hidden="true"></span>BSO</a>
<span class="fyk__knikker" aria-hidden="true"></span>
</div>
<p class="fyk__st" aria-live="polite"></p>
</div>
</div>/* Knikkerbord dat je kantelt · © 2026 NedDev BV · NedDev Lab-licentie (gratis gebruik, niet doorverkopen) · neddev.com/nl/lab/gebruik/#licentie */
.fyk{position:absolute;inset:0;overflow:hidden;background:#e9e7e3;font-family:'Switzer',sans-serif;color:#0a0a0a;text-align:left;perspective:700px}
.fyk *{box-sizing:border-box}
.fyk__bord{position:absolute;inset:8px;background:#f4f3f1;border-radius:8px;box-shadow:inset 0 0 0 3px #0a0a0a;touch-action:none;transform:scale(.985);will-change:transform;outline:none}
.fyk__bord:focus-visible{box-shadow:inset 0 0 0 3px #0a0a0a,0 0 0 3px #F5480F}
.fyk__h{position:absolute;left:14px;top:12px;right:14px;margin:0;font:700 16px/1.2 'Switzer',sans-serif;letter-spacing:-.01em;pointer-events:none}
.fyk__p{position:absolute;left:14px;top:34px;right:14px;margin:0;font:400 12px/1.35 'Switzer',sans-serif;color:#3d3b37;pointer-events:none}
.fyk__lat{position:absolute;background:#0a0a0a;border-radius:2px}
.fyk__gat{position:absolute;display:flex;align-items:center;gap:6px;min-height:28px;padding:2px 4px 2px 2px;border-radius:4px;color:#0a0a0a;text-decoration:none;font:600 13px/1 'Switzer',sans-serif;transform:translate(-14px,-50%);white-space:nowrap}
.fyk__put{flex:0 0 24px;width:24px;height:24px;border-radius:50%;background:#0a0a0a;box-shadow:inset 0 3px 0 rgba(255,255,255,.12)}
.fyk__gat:hover{color:#F5480F}
.fyk__gat:focus-visible{outline:2px solid #F5480F;outline-offset:2px}
.fyk__knikker{position:absolute;left:0;top:0;width:18px;height:18px;border-radius:50%;background:radial-gradient(circle at 35% 35%,#ff8a5c 0 22%,#F5480F 50%,#c73a0b 100%);box-shadow:2px 3px 3px rgba(10,10,10,.3);pointer-events:none;will-change:transform}
.fyk__st{position:absolute;left:20px;right:20px;bottom:14px;margin:0;font:500 12px/1.3 'Switzer',sans-serif;pointer-events:none;text-align:center}// Knikkerbord dat je kantelt · © 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){
var el=root.querySelector('.fyk');if(!el)return;
var RM=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var bord=el.querySelector('.fyk__bord'),kn=el.querySelector('.fyk__knikker'),st=el.querySelector('.fyk__st');
var LAT=[].slice.call(el.querySelectorAll('.fyk__lat')).map(function(n){var r=n.dataset.r.split(',').map(Number);return{n:n,r:r};});
var GAT=[].slice.call(el.querySelectorAll('.fyk__gat')).map(function(n){var p=n.dataset.p.split(',').map(Number);return{n:n,fx:p[0],fy:p[1],x:0,y:0};});
var W=1,H=1,R=9,GR=12,x=0,y=0,vx=0,vy=0,tx=0,ty=0,kx=0,ky=0,px=0,py=0,mode='rol',val=0,raf=0,last=0,vis=true,G=900,START=[.82,.36];
function maat(){var w=bord.clientWidth,h=bord.clientHeight;if(!w)return false;var o=W;W=w;H=h;
LAT.forEach(function(l){l.x=l.r[0]*W;l.y=l.r[1]*H;l.w=Math.max(6,l.r[2]*W);l.h=Math.max(6,l.r[3]*H);l.n.style.left=l.x+'px';l.n.style.top=l.y+'px';l.n.style.width=l.w+'px';l.n.style.height=l.h+'px';});
GAT.forEach(function(g){g.x=g.fx*W;g.y=g.fy*H;g.n.style.left=g.x+'px';g.n.style.top=g.y+'px';});
if(o===1){x=START[0]*W;y=START[1]*H;}else{x=Math.min(Math.max(x,R),W-R);y=Math.min(Math.max(y,R),H-R);}return true;}
function teken(s){kn.style.transform='translate('+(x-R).toFixed(1)+'px,'+(y-R).toFixed(1)+'px) scale('+(s==null?1:s).toFixed(3)+')';kn.style.opacity=s==null?1:Math.min(1,s*1.4);
if(!RM)bord.style.transform='scale(.985) rotateX('+(-ty*3.5).toFixed(2)+'deg) rotateY('+(tx*3.5).toFixed(2)+'deg)';}
function botsLat(){LAT.forEach(function(l){var cx=Math.max(l.x,Math.min(x,l.x+l.w)),cy=Math.max(l.y,Math.min(y,l.y+l.h)),dx=x-cx,dy=y-cy,d2=dx*dx+dy*dy;if(d2>=R*R)return;
var d=Math.sqrt(d2)||.001,nx=d2?dx/d:0,ny=d2?dy/d:-1,p=R-(d2?d:0);x+=nx*p;y+=ny*p;var vn=vx*nx+vy*ny;if(vn<0){vx-=1.5*vn*nx;vy-=1.5*vn*ny;}});}
function rand(){var m=R+3;if(x<m){x=m;vx=Math.abs(vx)*.5;}if(x>W-m){x=W-m;vx=-Math.abs(vx)*.5;}if(y<m){y=m;vy=Math.abs(vy)*.5;}if(y>H-m){y=H-m;vy=-Math.abs(vy)*.5;}}
function gaten(){for(var i=0;i<GAT.length;i++){var g=GAT[i],dx=g.x-x,dy=g.y-y,d=Math.hypot(dx,dy);if(d<GR*.75&&Math.hypot(vx,vy)<520){val={g:g,t:0,sx:x,sy:y};mode='val';st.textContent=g.n.dataset.t;return true;}
if(d<GR*1.6&&!RM){vx+=dx/d*700*(1/60);vy+=dy/d*700*(1/60);}}return false;}
function stap(dt){var ax=(tx+kx),ay=(ty+ky);ax=Math.max(-1,Math.min(1,ax));ay=Math.max(-1,Math.min(1,ay));vx+=ax*G*dt;vy+=ay*G*dt;var f=Math.exp(-.9*dt);vx*=f;vy*=f;x+=vx*dt;y+=vy*dt;rand();botsLat();gaten();}
function tick(t){if(!vis){raf=0;return;}var dt=Math.min(.033,(t-last)/1000);last=t;
if(mode==='rol'){for(var k=0;k<3;k++){stap(dt/3);if(mode!=='rol')break;}teken();}
else if(mode==='val'){val.t+=dt;var u=Math.min(1,val.t/.35);x=val.sx+(val.g.x-val.sx)*u;y=val.sy+(val.g.y-val.sy)*u;teken(1-u*.85);if(val.t>1.8){mode='rol';x=START[0]*W;y=START[1]*H;vx=vy=0;teken(.2);}}
raf=requestAnimationFrame(tick);}
function kick(){if(RM||raf)return;last=performance.now();raf=requestAnimationFrame(tick);}
function tilt(e){var r=bord.getBoundingClientRect();tx=Math.max(-1,Math.min(1,((e.clientX-r.left)/r.width-.5)*2.4));ty=Math.max(-1,Math.min(1,((e.clientY-r.top)/r.height-.5)*2.4));if(Math.abs(tx)<.08)tx=0;if(Math.abs(ty)<.08)ty=0;kick();}
var touch=false;
bord.addEventListener('pointermove',function(e){if(RM)return;if(e.pointerType==='mouse'||touch)tilt(e);});
bord.addEventListener('pointerdown',function(e){if(RM||e.target.closest('a'))return;if(e.pointerType!=='mouse'){touch=true;try{bord.setPointerCapture(e.pointerId);}catch(_){}tilt(e);}});
function stop(){touch=false;tx=ty=0;}
bord.addEventListener('pointerup',function(e){if(e.pointerType!=='mouse')stop();});bord.addEventListener('pointercancel',stop);bord.addEventListener('pointerleave',function(e){if(e.pointerType==='mouse')stop();});
var K={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]};
bord.addEventListener('keydown',function(e){var k=K[e.key];if(!k||e.target!==bord)return;e.preventDefault();
if(RM){if(mode!=='rol')return;x+=k[0]*14;y+=k[1]*14;rand();botsLat();if(gaten()){x=val.g.x;y=val.g.y;teken(.3);setTimeout(function(){mode='rol';x=START[0]*W;y=START[1]*H;teken();},1800);}else teken();return;}
if(k[0])kx=k[0];if(k[1])ky=k[1];kick();});
bord.addEventListener('keyup',function(e){var k=K[e.key];if(!k)return;if(k[0])kx=0;if(k[1])ky=0;});
bord.addEventListener('blur',function(){kx=ky=0;});
if(maat()){teken();if(!RM){vy=-60;kick();}}
if('ResizeObserver' in window)new ResizeObserver(function(){if(maat())teken();}).observe(bord);
if('IntersectionObserver' in window)new IntersectionObserver(function(es){vis=es[0].isIntersecting;if(vis)kick();}).observe(el);
}
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 →