Slotraster met personen en prijs Nieuw
Dialoog voor een workshop of restaurant: personenteller, datumstrook en een raster met tijden waarin elk slot laat zien hoeveel plekken er nog zijn ("nog 3", "laatste plek", uitverkocht). Past je groep niet meer in een slot, dan wordt het uitgeschakeld met de reden. Na een keuze schuift een onderbalk in met totaalprijs. Gebruikt het dialog-element (hier niet-modaal binnen de tegel, met Esc en focus terug naar de knop) en @starting-style voor de onderbalk; zonder die ondersteuning verschijnt de balk direct.
Atelier Klei · Utrecht
Draaien op de schijf
2,5 uur · max. 8 personen · € 45 p.p.
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.
<!-- 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'));
Meer uit formulieren & invoer
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 →