Slotraster met personen en prijs New
Dialog for a workshop or restaurant: guest stepper, date strip and a time grid where every slot shows remaining places ("3 left", "last place", sold out). If your group no longer fits, the slot is disabled with the reason. After choosing, a bottom bar slides in with the total price. Uses the dialog element (non-modal inside the tile here, with Esc and focus return) and @starting-style for the bar; without support the bar simply appears.
Atelier Klei · Utrecht
Draaien op de schijf
2,5 uur · max. 8 personen · € 45 p.p.
How to use it
- Paste the HTML where the forms & inputs 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.
<!-- Slotraster met personen en prijs · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="bsp"><div class="bsp__card"><p class="bsp__k">Atelier Klei · Utrecht</p><p class="bsp__t">Draaien op de schijf</p><p class="bsp__m">2,5 uur · max. 8 personen · € 45 p.p.</p><button type="button" class="bsp__open" aria-haspopup="dialog">Kies datum en tijd</button></div><div class="bsp__scrim" aria-hidden="true"></div><dialog class="bsp__dlg" aria-labelledby="bsp-h" open><div class="bsp__hd"><h3 class="bsp__h" id="bsp-h">Kies je moment</h3><button type="button" class="bsp__x" aria-label="Sluiten">×</button></div><div class="bsp__ppl"><span id="bsp-pl">Personen</span><div class="bsp__step" role="group" aria-labelledby="bsp-pl"><button type="button" class="bsp__min" aria-label="Eén persoon minder">−</button><output class="bsp__n" aria-live="polite">2</output><button type="button" class="bsp__plus" aria-label="Eén persoon meer">+</button></div></div><div class="bsp__dates" role="radiogroup" aria-label="Datum"></div><div class="bsp__grid" role="radiogroup" aria-label="Tijd"></div><div class="bsp__bar" hidden><p class="bsp__tot" aria-live="polite"></p><button type="button" class="bsp__go">Reserveren</button></div></dialog></div>
</div>/* Slotraster met personen en prijs · NedDev · eigen werk. */
.bsp{position:absolute;inset:0;display:flex;align-items:center;padding:16px;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;background:#f4f3f1;overflow:hidden;container-type:size}
.bsp__card{max-width:210px}
.bsp__k{margin:0;font:500 10px 'JetBrains Mono',monospace;text-transform:uppercase;letter-spacing:.07em;color:#5c5a56}
.bsp__t{margin:4px 0 3px;font-size:18px;font-weight:700;line-height:1.15}
.bsp__m{margin:0 0 10px;font-size:12px;color:#5c5a56}
.bsp__open{height:34px;padding:0 14px;border:1px solid #0a0a0a;border-radius:4px;background:#0a0a0a;color:#fff;font:600 12.5px 'Switzer',system-ui,sans-serif;cursor:pointer;transition:background-color .15s,color .15s}
.bsp__open:hover{background:#F5480F;border-color:#F5480F;color:#0a0a0a}
.bsp__open:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.bsp__scrim{position:absolute;inset:0;background:rgba(10,10,10,.35);opacity:0;pointer-events:none;transition:opacity .2s}
.bsp:has(.bsp__dlg[open]) .bsp__scrim,.bsp.is-open .bsp__scrim{opacity:1;pointer-events:auto}
.bsp__dlg{position:absolute;inset:8px 8px 8px auto;margin:0;width:min(330px,calc(100% - 16px));max-height:none;height:auto;padding:0;border:0;border-radius:6px;background:#fff;color:#0a0a0a;box-shadow:0 18px 40px -18px rgba(10,10,10,.55);display:none;flex-direction:column;overflow:hidden}
.bsp__dlg[open]{display:flex}
.bsp__hd{display:flex;align-items:center;justify-content:space-between;padding:8px 8px 4px 12px}
.bsp__h{margin:0;font-size:14px;font-weight:700}
.bsp__x{width:26px;height:26px;border:0;border-radius:4px;background:none;font-size:19px;line-height:1;cursor:pointer;color:#0a0a0a}
.bsp__x:hover{background:#f4f3f1}.bsp__x:focus-visible{outline:2px solid #0a0a0a}
.bsp__ppl{display:flex;align-items:center;justify-content:space-between;margin:0 12px;padding:3px 0 5px;border-bottom:1px solid rgba(10,10,10,.1);font-size:12.5px;font-weight:600}
.bsp__step{display:flex;align-items:center;gap:6px}
.bsp__step button{width:24px;height:24px;border:1px solid rgba(10,10,10,.3);border-radius:50%;background:#fff;font-size:15px;line-height:1;cursor:pointer;color:#0a0a0a;transition:border-color .15s,background-color .15s}
.bsp__step button:hover:not(:disabled){border-color:#0a0a0a;background:#f4f3f1}
.bsp__step button:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.bsp__step button:disabled{color:#b8b5af;border-color:rgba(10,10,10,.12);cursor:not-allowed}
.bsp__n{min-width:16px;text-align:center;font-variant-numeric:tabular-nums}
.bsp__dates{display:flex;gap:4px;padding:6px 12px 4px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none}
.bsp__dt{flex:1 0 46px;scroll-snap-align:start;padding:3px 0;border:1px solid rgba(10,10,10,.18);border-radius:4px;background:#fff;font:600 12px 'Switzer',system-ui,sans-serif;color:#0a0a0a;cursor:pointer;line-height:1.15;transition:background-color .15s,border-color .15s,color .15s}
.bsp__dt small{display:block;font:500 9px 'JetBrains Mono',monospace;text-transform:uppercase;color:#5c5a56}
.bsp__dt:hover{border-color:#0a0a0a}
.bsp__dt:focus-visible{outline:2px solid #0a0a0a;outline-offset:1px}
.bsp__dt[aria-checked="true"]{background:#0a0a0a;border-color:#0a0a0a;color:#fff}
.bsp__dt[aria-checked="true"] small{color:#d6d3cd}
.bsp__grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px;padding:4px 12px 8px;overflow:auto;flex:1;align-content:start}
.bsp__s{padding:4px 2px;border:1px solid rgba(10,10,10,.22);border-radius:999px;background:#fff;font:600 12.5px 'Switzer',system-ui,sans-serif;color:#0a0a0a;cursor:pointer;line-height:1.1;font-variant-numeric:tabular-nums;transition:background-color .15s,border-color .15s,color .15s}
.bsp__s small{display:block;font-size:9.5px;font-weight:500;color:#5c5a56}
.bsp__s small.is-low{color:#b8340a;font-weight:600}
.bsp__s:hover:not([aria-disabled="true"]){border-color:#0a0a0a;background:#f4f3f1}
.bsp__s:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.bsp__s[aria-checked="true"]{background:#0a0a0a;border-color:#0a0a0a;color:#fff}
.bsp__s[aria-checked="true"] small{color:#fbd3c3}
.bsp__s[aria-disabled="true"]{border-style:dashed;background:#fafaf9;color:#9a978f;cursor:not-allowed}
.bsp__s[aria-disabled="true"] small{color:#8a8781}
.bsp__bar{display:flex;align-items:center;gap:8px;padding:7px 12px;border-top:1px solid rgba(10,10,10,.12);background:#fff}
.bsp__bar[hidden]{display:none}
@supports (transition-behavior:allow-discrete){.bsp__bar{transition:translate .22s cubic-bezier(.2,.7,.2,1),opacity .22s,display .22s allow-discrete}.bsp__bar[hidden]{translate:0 100%;opacity:0}@starting-style{.bsp__bar{translate:0 100%;opacity:0}}}
.bsp__tot{flex:1;margin:0;font-size:11.5px;color:#5c5a56;line-height:1.25;min-width:0}
.bsp__tot b{display:block;font-size:14px;color:#0a0a0a;font-variant-numeric:tabular-nums}
.bsp__go{height:32px;padding:0 14px;border:1px solid #0a0a0a;border-radius:4px;background:#0a0a0a;color:#fff;font:600 12.5px 'Switzer',system-ui,sans-serif;cursor:pointer;transition:background-color .15s,color .15s}
.bsp__go:hover{background:#F5480F;border-color:#F5480F;color:#0a0a0a}
.bsp__go:focus-visible{outline:2px solid #0a0a0a;outline-offset:2px}
.bsp__ok{margin:6px 12px 10px;padding:10px;border-radius:4px;background:#e8f3ec;color:#14532d;font-size:12px;line-height:1.45}
.bsp__ok b{display:block;font-size:13px}
@container (max-width:380px){.bsp__card{display:none}.bsp__dlg{inset:6px;width:auto}.bsp__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (prefers-reduced-motion:reduce){.bsp *{transition:none!important}}// Slotraster met personen en prijs · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.bsp');if(!w)return;
var dlg=w.querySelector('.bsp__dlg'),opener=w.querySelector('.bsp__open'),nOut=w.querySelector('.bsp__n'),dates=w.querySelector('.bsp__dates'),grid=w.querySelector('.bsp__grid'),bar=w.querySelector('.bsp__bar'),tot=w.querySelector('.bsp__tot'),bMin=w.querySelector('.bsp__min'),bPlus=w.querySelector('.bsp__plus');
var DAG=['zo','ma','di','wo','do','vr','za'],MK=['jan','feb','mrt','apr','mei','jun','jul','aug','sep','okt','nov','dec'],PRICE=45,MAX=8;
var T=new Date();T.setHours(0,0,0,0);
function add(d,n){var x=new Date(d);x.setDate(x.getDate()+n);return x}
/* nep-bezetting per dag: in productie uit je reserveringssysteem */
var D=[],d=add(T,1);while(D.length<6){if(d.getDay()!==1)D.push(new Date(d));d=add(d,1)}
var TIMES=['10:00','13:30','16:00','19:30'];
function left(di,ti){var r=(D[di].getDate()*13+ti*7)%10;return [8,3,1,0,5,2,6,0,4,7][r]}
var ppl=2,di=0,sel=null;
function eur(n){return '€ '+n.toLocaleString('nl-NL')}
function renderDates(){dates.innerHTML=D.map(function(x,i){return '<button type="button" role="radio" class="bsp__dt" aria-checked="'+(i===di)+'" tabindex="'+(i===di?0:-1)+'" data-i="'+i+'" aria-label="'+DAG[x.getDay()]+' '+x.getDate()+' '+MK[x.getMonth()]+'"><small>'+DAG[x.getDay()]+'</small>'+x.getDate()+' '+MK[x.getMonth()]+'</button>'}).join('')}
function renderGrid(){var firstFocusable=-1;grid.innerHTML=TIMES.map(function(t,i){var l=left(di,i),off=l<ppl,lab;
if(l===0)lab='uitverkocht';else if(off)lab='nog '+l+', te weinig';else if(l===1)lab='laatste plek';else lab='nog '+l+' plekken';
if(!off&&firstFocusable<0)firstFocusable=i;
return '<button type="button" role="radio" class="bsp__s" data-i="'+i+'" aria-checked="'+(sel===i)+'"'+(off?' aria-disabled="true"':'')+' aria-label="'+t+', '+lab+'"><span>'+t+'</span><small'+(l>0&&l<=3&&!off?' class="is-low"':'')+'>'+lab+'</small></button>'}).join('');
var tab=sel!==null?sel:firstFocusable;grid.querySelectorAll('.bsp__s').forEach(function(b,i){b.tabIndex=i===tab?0:-1})}
function renderBar(){if(sel===null){bar.hidden=true;return}bar.hidden=false;var x=D[di];tot.innerHTML='<b>'+eur(PRICE*ppl)+'</b>'+ppl+(ppl===1?' persoon':' personen')+' · '+DAG[x.getDay()]+' '+x.getDate()+' '+MK[x.getMonth()]+' '+TIMES[sel]}
function all(){bMin.disabled=ppl<=1;bPlus.disabled=ppl>=MAX;nOut.textContent=ppl;if(sel!==null&&left(di,sel)<ppl)sel=null;renderGrid();renderBar()}
function roving(box,sel2,e,cb){var items=[].slice.call(box.querySelectorAll(sel2)).filter(function(b){return b.getAttribute('aria-disabled')!=='true'}),i=items.indexOf(document.activeElement);if(i<0)return;var n=null;
if(e.key==='ArrowRight'||e.key==='ArrowDown')n=(i+1)%items.length;else if(e.key==='ArrowLeft'||e.key==='ArrowUp')n=(i-1+items.length)%items.length;else return;e.preventDefault();cb(items[n])}
bMin.addEventListener('click',function(){if(ppl>1){ppl--;all()}});
bPlus.addEventListener('click',function(){if(ppl<MAX){ppl++;all()}});
dates.addEventListener('click',function(e){var b=e.target.closest('.bsp__dt');if(!b)return;di=+b.dataset.i;sel=null;renderDates();all();dates.querySelector('[data-i="'+di+'"]').focus()});
dates.addEventListener('keydown',function(e){roving(dates,'.bsp__dt',e,function(b){b.click()})});
grid.addEventListener('click',function(e){var b=e.target.closest('.bsp__s');if(!b||b.getAttribute('aria-disabled')==='true')return;sel=+b.dataset.i;renderGrid();renderBar();grid.querySelector('[data-i="'+sel+'"]').focus()});
grid.addEventListener('keydown',function(e){roving(grid,'.bsp__s',e,function(b){b.click()})});
w.querySelector('.bsp__go').addEventListener('click',function(){var x=D[di];bar.hidden=true;grid.innerHTML='';dates.innerHTML='';grid.insertAdjacentHTML('beforeend','<div class="bsp__ok" role="status" style="grid-column:1/-1"><b>Gereserveerd voor '+ppl+(ppl===1?' persoon':' personen')+'</b>'+DAG[x.getDay()]+' '+x.getDate()+' '+MK[x.getMonth()]+' om '+TIMES[sel]+', Atelier Klei. Je betaalt '+eur(PRICE*ppl)+' bij de volgende stap.</div>');var r=w.querySelector('.bsp__ok');r.tabIndex=-1;r.focus()});
function openDlg(){dlg.setAttribute('open','');w.classList.add('is-open');if(!dates.innerHTML){sel=null;renderDates()}all();var f=grid.querySelector('[tabindex="0"]')||dlg.querySelector('.bsp__x');f.focus()}
function closeDlg(){dlg.removeAttribute('open');w.classList.remove('is-open');opener.focus()}
opener.addEventListener('click',openDlg);
w.querySelector('.bsp__x').addEventListener('click',closeDlg);
w.querySelector('.bsp__scrim').addEventListener('click',closeDlg);
dlg.addEventListener('keydown',function(e){if(e.key==='Escape'){e.preventDefault();closeDlg();return}
if(e.key==='Tab'){var f=[].slice.call(dlg.querySelectorAll('button:not(:disabled),[tabindex="0"]')).filter(function(x){return x.offsetParent&&x.tabIndex>=0});if(!f.length)return;var a=f[0],z=f[f.length-1];if(e.shiftKey&&document.activeElement===a){e.preventDefault();z.focus()}else if(!e.shiftKey&&document.activeElement===z){e.preventDefault();a.focus()}}});
w.classList.add('is-open');renderDates();sel=null;for(var i=0;i<TIMES.length;i++){if(left(di,i)>=ppl&&left(di,i)<=3){sel=i;break}}all();
}
init(document.querySelector('.jouw-container'));
More forms & inputs
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 →