Kanbanbord met slepen Nieuw
Takenbord met drie kolommen waarop je kaartjes sleept met muis of vinger (pointer events, werkt ook op touch). Met het toetsenbord pak je een kaart op met Spatie, verplaats je hem met de pijltjes en zet je hem neer met Spatie; Escape zet hem terug. Een live-regio leest elke stap voor. Voor klantportalen, projectoverzichten en interne planning.
Verbouwing keuken · fam. Yilmaz, Utrecht
Sleep een kaart, of pak hem op met Spatie
Te doen 3
- Tegels kiezen
- Offerte stucwerk
- Afvoer verleggen
Bezig 2
- Keuken slopen
- Groep bijplaatsen
Klaar 2
- Keuken inmeten
- Aanbetaling € 2.400
Spatie pakt de kaart op. Pijltjes verplaatsen hem, Spatie zet hem neer, Escape annuleert.
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- Kanbanbord met slepen · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="kbn">
<div class="kbn__hd"><p class="kbn__t">Verbouwing keuken · fam. Yilmaz, Utrecht</p><p class="kbn__hint">Sleep een kaart, of pak hem op met Spatie</p></div>
<div class="kbn__cols">
<div class="kbn__col" data-col="todo"><p class="kbn__ch">Te doen <span class="kbn__n">3</span></p><ul class="kbn__list" aria-label="Te doen"><li class="kbn__card" tabindex="0">Tegels kiezen</li><li class="kbn__card" tabindex="0">Offerte stucwerk</li><li class="kbn__card" tabindex="0">Afvoer verleggen</li></ul></div>
<div class="kbn__col" data-col="bezig"><p class="kbn__ch">Bezig <span class="kbn__n">2</span></p><ul class="kbn__list" aria-label="Bezig"><li class="kbn__card" tabindex="0">Keuken slopen</li><li class="kbn__card" tabindex="0">Groep bijplaatsen</li></ul></div>
<div class="kbn__col" data-col="klaar"><p class="kbn__ch">Klaar <span class="kbn__n">2</span></p><ul class="kbn__list" aria-label="Klaar"><li class="kbn__card" tabindex="0">Keuken inmeten</li><li class="kbn__card" tabindex="0">Aanbetaling € 2.400</li></ul></div>
</div>
<p class="kbn__sr" aria-live="assertive"></p>
<p class="kbn__sr kbn__help">Spatie pakt de kaart op. Pijltjes verplaatsen hem, Spatie zet hem neer, Escape annuleert.</p>
</div>
</div>/* Kanbanbord met slepen · NedDev · eigen werk. */
.kbn{position:absolute;inset:0;box-sizing:border-box;padding:10px 12px;background:#f4f3f1;color:#0a0a0a;font-family:'Switzer',sans-serif;display:flex;flex-direction:column;gap:8px;container-type:inline-size}
.kbn__hd{display:flex;flex-direction:column;gap:1px}
.kbn__t{margin:0;font-size:13px;font-weight:600;line-height:1.3}
.kbn__hint{margin:0;font-size:12px;color:#5f5e5a}
.kbn__cols{display:flex;gap:8px;flex:1;min-height:0}
.kbn__col{flex:1 1 0;min-width:0;background:#e8e6e2;border-radius:8px;padding:6px;display:flex;flex-direction:column;gap:6px;overflow:hidden}
.kbn__ch{margin:0;padding:0 2px;font-size:12px;font-weight:600;color:#3d3c39;display:flex;justify-content:space-between}
.kbn__n{font-weight:500;color:#6b6a66;font-variant-numeric:tabular-nums}
.kbn__list{list-style:none;margin:0;padding:1px;display:flex;flex-direction:column;gap:5px;flex:1;min-height:24px;overflow-y:auto;border-radius:4px;transition:background-color .15s}
.kbn__list.is-over{background:#f9dccf}
.kbn__card{box-sizing:border-box;background:#fff;border:1px solid #d6d3ce;border-radius:4px;padding:6px 7px;font-size:12px;line-height:1.3;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none;outline:none;transition:transform .15s,border-color .15s,background-color .15s}
.kbn__card:hover{border-color:#8a8883}
.kbn__card:focus-visible{outline:2px solid #F5480F;outline-offset:1px}
.kbn__card.is-grab{border-color:#F5480F;background:#fff4ef;transform:rotate(-1.5deg)}
.kbn__col[data-col="klaar"] .kbn__card{color:#6b6a66}
.kbn__ph{box-sizing:border-box;border:1.5px dashed #F5480F;border-radius:4px;background:#fdebe4;flex:none}
.kbn__ghost{position:absolute;z-index:5;margin:0;pointer-events:none;cursor:grabbing;border-color:#0a0a0a;box-shadow:3px 3px 0 #0a0a0a;transform:rotate(-2deg)}
.kbn__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;margin:0}
@container (max-width:380px){.kbn__hint{display:none}.kbn__cols{gap:6px}.kbn__col{padding:5px;gap:5px}.kbn__card{padding:4px 6px;line-height:1.25}.kbn__list{gap:4px}}
@media(prefers-reduced-motion:reduce){.kbn__card,.kbn__list{transition:none}.kbn__card.is-grab,.kbn__ghost{transform:none}}// Kanbanbord met slepen · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.kbn');if(!w)return;
var lists=[].slice.call(w.querySelectorAll('.kbn__list')),live=w.querySelector('.kbn__sr[aria-live]'),help=w.querySelector('.kbn__help');
var hid='kbn-help-'+Math.random().toString(36).slice(2,8);help.id=hid;
function cards(l){return [].slice.call(l.querySelectorAll('.kbn__card:not([hidden])'));}
function naam(l){return l.getAttribute('aria-label');}
function count(){lists.forEach(function(l){l.parentNode.querySelector('.kbn__n').textContent=cards(l).length;});}
function say(t){live.textContent=t;}
function pos(c){var l=c.parentNode,a=cards(l);return naam(l)+', plek '+(a.indexOf(c)+1)+' van '+a.length;}
w.querySelectorAll('.kbn__card').forEach(function(c){c.setAttribute('aria-describedby',hid);c.setAttribute('aria-roledescription','kaart');});
var grab=null,orig=null,moving=false;
function place(c,l,i){var a=cards(l).filter(function(x){return x!==c;});moving=true;l.insertBefore(c,a[i]||null);c.focus();moving=false;count();}
function drop(){if(!grab)return;var c=grab;grab=null;c.classList.remove('is-grab');count();say(c.textContent+' neergezet in '+pos(c));}
w.addEventListener('keydown',function(e){var c=e.target.closest&&e.target.closest('.kbn__card');if(!c)return;
var l=c.parentNode,li=lists.indexOf(l),a=cards(l),i=a.indexOf(c),k=e.key;
if(k===' '||k==='Enter'){e.preventDefault();if(grab){drop();}else{grab=c;orig={l:l,n:c.nextElementSibling};c.classList.add('is-grab');say(c.textContent+' opgepakt, '+pos(c)+'. Pijl links of rechts wisselt van kolom, omhoog of omlaag van plek. Spatie zet neer, Escape annuleert.');}return;}
if(k==='Escape'&&grab){e.preventDefault();var g=grab;grab=null;moving=true;orig.l.insertBefore(g,orig.n&&orig.n.parentNode===orig.l?orig.n:null);g.focus();moving=false;g.classList.remove('is-grab');count();say('Geannuleerd. '+g.textContent+' staat weer in '+pos(g));return;}
var d=k==='ArrowLeft'?-1:k==='ArrowRight'?1:0,v=k==='ArrowUp'?-1:k==='ArrowDown'?1:0;if(!d&&!v)return;e.preventDefault();
if(grab===c){if(d){var nl=lists[li+d];if(!nl)return;place(c,nl,Math.min(i,cards(nl).length));}else{var j=i+v;if(j<0||j>=a.length)return;place(c,l,j);}say(pos(c));}
else{if(d){var t=lists[li+d];if(!t)return;var b=cards(t);if(b.length)b[Math.min(i,b.length-1)].focus();}else{var n=a[i+v];if(n)n.focus();}}});
w.addEventListener('focusout',function(){setTimeout(function(){if(grab&&!moving&&document.activeElement!==grab)drop();},0);});
var dr=null;
function overList(x){for(var i=0;i<lists.length;i++){var r=lists[i].parentNode.getBoundingClientRect();if(x>=r.left-4&&x<=r.right+4)return lists[i];}return null;}
w.addEventListener('pointerdown',function(e){var c=e.target.closest('.kbn__card');if(!c||c.classList.contains('kbn__ghost')||e.button!==0||grab)return;dr={c:c,x:e.clientX,y:e.clientY,on:false,id:e.pointerId};try{c.setPointerCapture(e.pointerId);}catch(_){}});
w.addEventListener('pointermove',function(e){if(!dr)return;var c=dr.c;
if(!dr.on){if(Math.abs(e.clientX-dr.x)+Math.abs(e.clientY-dr.y)<6)return;var r=c.getBoundingClientRect();dr.ox=dr.x-r.left;dr.oy=dr.y-r.top;
var g=document.createElement('div');g.className='kbn__card kbn__ghost';g.setAttribute('aria-hidden','true');g.textContent=c.textContent;g.style.width=r.width+'px';w.appendChild(g);dr.g=g;
var ph=document.createElement('li');ph.className='kbn__ph';ph.style.height=r.height+'px';c.parentNode.insertBefore(ph,c);c.hidden=true;dr.ph=ph;dr.on=true;}
var wr=w.getBoundingClientRect(),gx=Math.max(0,Math.min(wr.width-dr.g.offsetWidth,e.clientX-wr.left-dr.ox)),gy=Math.max(0,Math.min(wr.height-dr.g.offsetHeight,e.clientY-wr.top-dr.oy));
dr.g.style.left=gx+'px';dr.g.style.top=gy+'px';
var l=overList(e.clientX);lists.forEach(function(x){x.classList.toggle('is-over',x===l);});if(!l)return;
var a=cards(l),before=null;for(var i=0;i<a.length;i++){var b=a[i].getBoundingClientRect();if(b.top+b.height/2>e.clientY){before=a[i];break;}}
if(before!==dr.ph.nextElementSibling||dr.ph.parentNode!==l)l.insertBefore(dr.ph,before);});
function end(){if(!dr)return;var d=dr;dr=null;if(!d.on)return;d.ph.parentNode.insertBefore(d.c,d.ph);d.ph.remove();d.c.hidden=false;d.g.remove();lists.forEach(function(x){x.classList.remove('is-over');});count();say(d.c.textContent+' verplaatst naar '+pos(d.c));}
w.addEventListener('pointerup',end);w.addEventListener('pointercancel',end);
}
init(document.querySelector('.jouw-container'));
Meer uit cijfers & grafieken
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →