Datumprikker met kalender New
Date field with a calendar panel following the ARIA grid pattern: arrows, Home/End, PageUp/PageDown (Shift for a year), Enter picks, Esc closes. Typing dd-mm-yyyy also works; closed days (Sun/Mon) and past dates are greyed out with an explanation. Panel enters via @starting-style and transition-behavior:allow-discrete (fallback: shown instantly). On a full page the panel can become popover="auto" with anchor positioning.
Open di t/m za. Typ bv. 12-10-2026 om de foutmelding te zien.
gesloten
| ma | di | wo | do | vr | za | zo |
|---|
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.
<!-- Datumprikker met kalender · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="bdp-q"><div class="bdp"><div class="bdp__field"><label class="bdp__lbl" for="bdp-in">Datum van je afspraak</label><div class="bdp__ctl"><input id="bdp-in" class="bdp__in" type="text" inputmode="numeric" placeholder="dd-mm-jjjj" autocomplete="off" aria-describedby="bdp-msg"><button type="button" class="bdp__open" aria-haspopup="dialog" aria-expanded="false" aria-controls="bdp-pop" aria-label="Kies een datum in de kalender"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="2" y="3" width="12" height="11" rx="1.5"/><path d="M2 6.5h12M5.5 1.5v3M10.5 1.5v3"/></svg></button></div><p class="bdp__msg" id="bdp-msg" aria-live="polite">Open di t/m za. Typ bv. 12-10-2026 om de foutmelding te zien.</p><p class="bdp__leg"><span class="bdp__sw"></span>gesloten</p></div><div class="bdp__pop" id="bdp-pop" role="dialog" aria-modal="false" aria-labelledby="bdp-m" hidden><div class="bdp__head"><button type="button" class="bdp__nav bdp__prev" aria-label="Vorige maand">‹</button><h3 class="bdp__month" id="bdp-m" aria-live="polite"></h3><button type="button" class="bdp__nav bdp__next" aria-label="Volgende maand">›</button><button type="button" class="bdp__nav bdp__x" aria-label="Kalender sluiten">×</button></div><table class="bdp__grid" role="grid" aria-labelledby="bdp-m"><thead><tr><th scope="col" abbr="maandag">ma</th><th scope="col" abbr="dinsdag">di</th><th scope="col" abbr="woensdag">wo</th><th scope="col" abbr="donderdag">do</th><th scope="col" abbr="vrijdag">vr</th><th scope="col" abbr="zaterdag">za</th><th scope="col" abbr="zondag">zo</th></tr></thead><tbody class="bdp__body"></tbody></table></div></div></div>
</div>/* Datumprikker met kalender · NedDev · eigen werk. */
.bdp-q{position:absolute;inset:0;container-type:inline-size}
.bdp{position:absolute;inset:0;display:grid;grid-template-columns:150px 222px;justify-content:center;align-content:center;gap:12px;padding:8px;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;overflow:auto}
.bdp__field{align-self:center}
.bdp__lbl{display:block;font-weight:600;font-size:12.5px;margin-bottom:6px}
.bdp__ctl{display:flex;background:#fff;border:1px solid rgba(10,10,10,.3);border-radius:4px;transition:border-color .15s,box-shadow .15s}
.bdp__ctl:hover{border-color:rgba(10,10,10,.55)}
.bdp__ctl:focus-within{border-color:#0a0a0a;box-shadow:0 0 0 3px rgba(245,72,15,.3)}
.bdp__in{flex:1;min-width:0;border:0;background:none;padding:9px 10px;font:500 14px 'Switzer',system-ui,sans-serif;font-variant-numeric:tabular-nums;color:#0a0a0a;outline:none}
.bdp__in::placeholder{color:#76736d}
.bdp__open{flex:none;width:38px;display:grid;place-items:center;border:0;border-left:1px solid rgba(10,10,10,.14);background:none;color:#0a0a0a;cursor:pointer;border-radius:0 3px 3px 0;transition:background-color .15s,color .15s}
.bdp__open:hover{background:#f4f3f1}
.bdp__open:active{background:#e9e7e3}
.bdp__open:focus-visible{outline:2px solid #0a0a0a;outline-offset:-4px}
.bdp__open[aria-expanded="true"]{background:#0a0a0a;color:#fff}
.bdp__msg{margin:7px 0 0;font-size:11.5px;line-height:1.4;color:#5c5a56;min-height:32px}
.bdp.is-err .bdp__ctl{border-color:#c4231b}
.bdp.is-err .bdp__ctl:focus-within{box-shadow:0 0 0 3px rgba(196,35,27,.22)}
.bdp.is-err .bdp__msg{color:#c4231b}
.bdp.is-ok .bdp__msg{color:#1f7a3d}
.bdp__leg{margin:6px 0 0;display:flex;align-items:center;gap:6px;font:500 10px 'JetBrains Mono',monospace;color:#5c5a56;text-transform:uppercase;letter-spacing:.06em}
.bdp__sw{width:14px;height:10px;border-radius:2px;background:repeating-linear-gradient(135deg,#efede9 0 3px,#dcd9d3 3px 4px)}
.bdp__pop{background:#fff;border:1px solid rgba(10,10,10,.16);border-radius:6px;box-shadow:0 12px 28px -14px rgba(10,10,10,.4);padding:7px 8px 8px}
.bdp__pop[hidden]{display:none}
@supports (transition-behavior:allow-discrete){
.bdp__pop{transition:opacity .18s ease,translate .18s ease,display .18s allow-discrete}
.bdp__pop[hidden]{opacity:0;translate:-4px 0}
@starting-style{.bdp__pop{opacity:0;translate:-4px 0}}
}
.bdp__head{display:flex;align-items:center;gap:2px;margin-bottom:2px}
.bdp__month{flex:1;margin:0 0 0 4px;font-size:13px;font-weight:700;text-transform:capitalize}
.bdp__nav{width:26px;height:26px;border:0;border-radius:4px;background:none;color:#0a0a0a;font-size:17px;line-height:1;cursor:pointer;transition:background-color .15s}
.bdp__nav:hover{background:#f4f3f1}
.bdp__nav:active{background:#e9e7e3}
.bdp__nav:focus-visible{outline:2px solid #0a0a0a;outline-offset:1px}
.bdp__nav:disabled{color:#b8b5af;cursor:default;background:none}
.bdp__x{display:none}
.bdp__grid{width:100%;border-collapse:separate;border-spacing:1px;table-layout:fixed}
.bdp__grid th{font:500 9.5px 'JetBrains Mono',monospace;color:#5c5a56;text-transform:uppercase;height:16px;padding:0}
.bdp__d{height:24px;padding:0;text-align:center;font-size:12.5px;font-variant-numeric:tabular-nums;border-radius:4px;cursor:pointer;transition:background-color .12s,color .12s;user-select:none}
.bdp__d:hover{background:#f4f3f1}
.bdp__d:focus-visible{outline:2px solid #0a0a0a;outline-offset:-2px}
.bdp__d.is-out{color:#8a8781}
.bdp__d.is-today{box-shadow:inset 0 -2px 0 #F5480F;font-weight:700}
.bdp__d[aria-disabled="true"]{color:#aeaba5;cursor:not-allowed;background:none}
.bdp__d.is-closed{background:repeating-linear-gradient(135deg,#f6f5f2 0 3px,#e6e3de 3px 4px);color:#9a978f}
.bdp__d[aria-selected="true"]{background:#0a0a0a;color:#fff;font-weight:600}
@container (max-width:380px){.bdp{grid-template-columns:minmax(0,220px)}.bdp__pop{position:absolute;inset:6px;overflow:auto;z-index:2;box-shadow:0 0 0 100vmax rgba(244,243,241,.9)}.bdp__x{display:block}}
@media (prefers-reduced-motion:reduce){.bdp *{transition:none!important}}// Datumprikker met kalender · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.bdp');if(!w)return;
var inp=w.querySelector('.bdp__in'),btn=w.querySelector('.bdp__open'),pop=w.querySelector('.bdp__pop'),body=w.querySelector('.bdp__body'),title=w.querySelector('.bdp__month'),msg=w.querySelector('.bdp__msg'),prev=w.querySelector('.bdp__prev'),next=w.querySelector('.bdp__next');
var DAG=['zondag','maandag','dinsdag','woensdag','donderdag','vrijdag','zaterdag'],MND=['januari','februari','maart','april','mei','juni','juli','augustus','september','oktober','november','december'];
var T=new Date();T.setHours(0,0,0,0);var MAX=new Date(T);MAX.setMonth(MAX.getMonth()+3);
var sel=null,foc=null,view=null;
function add(d,n){var x=new Date(d);x.setDate(x.getDate()+n);return x}
function same(a,b){return !!a&&!!b&&a.getTime()===b.getTime()}
function closed(d){return d.getDay()===0||d.getDay()===1}
function why(d){if(d<T)return 'Deze datum ligt in het verleden.';if(d>MAX)return 'Je kunt tot drie maanden vooruit boeken.';if(closed(d))return 'Op '+DAG[d.getDay()]+' zijn we gesloten. Kies dinsdag t/m zaterdag.';return ''}
function nice(d){return DAG[d.getDay()]+' '+d.getDate()+' '+MND[d.getMonth()]+' '+d.getFullYear()}
function pad(n){return (n<10?'0':'')+n}
function firstOk(d){var x=new Date(d);for(var i=0;i<14&&why(x);i++)x=add(x,1);return x}
function hint(){var m=add(T,1);while(m.getDay()!==1)m=add(m,1);return 'Open di t/m za. Typ '+pad(m.getDate())+'-'+pad(m.getMonth()+1)+'-'+m.getFullYear()+' (een maandag) om de foutmelding te zien.'}
foc=firstOk(T);view=new Date(foc.getFullYear(),foc.getMonth(),1);
function state(s,t){w.classList.toggle('is-err',s==='err');w.classList.toggle('is-ok',s==='ok');msg.textContent=t;if(s==='err')inp.setAttribute('aria-invalid','true');else inp.removeAttribute('aria-invalid')}
function render(){
title.textContent=MND[view.getMonth()]+' '+view.getFullYear();
var s=add(view,-((view.getDay()+6)%7)),h='';
for(var r=0;r<6;r++){h+='<tr>';for(var c=0;c<7;c++){var d=add(s,r*7+c),bad=why(d);
h+='<td role="gridcell" class="bdp__d'+(d.getMonth()!==view.getMonth()?' is-out':'')+(same(d,T)?' is-today':'')+(closed(d)&&d>=T?' is-closed':'')+'" data-t="'+d.getTime()+'" tabindex="'+(same(d,foc)?0:-1)+'" aria-selected="'+(same(d,sel)?'true':'false')+'"'+(bad?' aria-disabled="true"':'')+(same(d,T)?' aria-current="date"':'')+' aria-label="'+nice(d)+(closed(d)?', gesloten':'')+'">'+d.getDate()+'</td>'}h+='</tr>'}
body.innerHTML=h;
prev.disabled=view.getFullYear()*12+view.getMonth()<=T.getFullYear()*12+T.getMonth();
next.disabled=view.getFullYear()*12+view.getMonth()>=MAX.getFullYear()*12+MAX.getMonth();
}
function focusCell(){var c=body.querySelector('[tabindex="0"]');if(c)c.focus()}
function move(d){if(d<new Date(T.getFullYear(),T.getMonth(),1))d=new Date(T);if(d>MAX)d=new Date(MAX);foc=d;if(d.getMonth()!==view.getMonth()||d.getFullYear()!==view.getFullYear())view=new Date(d.getFullYear(),d.getMonth(),1);render();focusCell()}
function shiftMonth(d,n){var x=new Date(d),day=x.getDate();x.setDate(1);x.setMonth(x.getMonth()+n);var last=new Date(x.getFullYear(),x.getMonth()+1,0).getDate();x.setDate(Math.min(day,last));return x}
function open(withFocus){if(sel){foc=new Date(sel);view=new Date(sel.getFullYear(),sel.getMonth(),1)}pop.hidden=false;btn.setAttribute('aria-expanded','true');render();if(withFocus)focusCell()}
function close(ret){if(pop.hidden)return;pop.hidden=true;btn.setAttribute('aria-expanded','false');if(ret)btn.focus()}
function choose(d){var b=why(d);if(b){state('err',b);return}sel=new Date(d);foc=new Date(d);inp.value=pad(d.getDate())+'-'+pad(d.getMonth()+1)+'-'+d.getFullYear();state('ok','Gekozen: '+nice(d)+'.');close(true)}
btn.addEventListener('click',function(){pop.hidden?open(true):close(true)});
w.querySelector('.bdp__x').addEventListener('click',function(){close(true)});
prev.addEventListener('click',function(){view=shiftMonth(view,-1);foc=firstOk(new Date(Math.max(view.getTime(),T.getTime())));if(foc.getMonth()!==view.getMonth())foc=new Date(Math.max(view.getTime(),T.getTime()));render()});
next.addEventListener('click',function(){view=shiftMonth(view,1);foc=firstOk(view);if(foc.getMonth()!==view.getMonth())foc=new Date(view);render()});
body.addEventListener('click',function(e){var c=e.target.closest('.bdp__d');if(!c)return;var d=new Date(+c.dataset.t);foc=d;if(c.getAttribute('aria-disabled')){state('err',why(d));render();focusCell();return}choose(d)});
body.addEventListener('keydown',function(e){var k=e.key,d=new Date(foc),dow=(d.getDay()+6)%7;
if(k==='ArrowLeft')d=add(d,-1);else if(k==='ArrowRight')d=add(d,1);else if(k==='ArrowUp')d=add(d,-7);else if(k==='ArrowDown')d=add(d,7);
else if(k==='Home')d=add(d,-dow);else if(k==='End')d=add(d,6-dow);
else if(k==='PageUp')d=shiftMonth(d,e.shiftKey?-12:-1);else if(k==='PageDown')d=shiftMonth(d,e.shiftKey?12:1);
else if(k==='Enter'||k===' '){e.preventDefault();choose(foc);return}else return;
e.preventDefault();move(d)});
pop.addEventListener('keydown',function(e){if(e.key==='Escape'){e.preventDefault();close(true)}});
document.addEventListener('pointerdown',function(e){if(!w.contains(e.target))close(false)});
function parse(){var v=inp.value.trim();if(!v){sel=null;state('',hint());return}
var m=v.match(/^(\d{1,2})[-/. ](\d{1,2})[-/. ](\d{4})$/);if(!m){state('err','Gebruik dd-mm-jjjj, bijvoorbeeld '+pad(foc.getDate())+'-'+pad(foc.getMonth()+1)+'-'+foc.getFullYear()+'.');return}
var d=new Date(+m[3],+m[2]-1,+m[1]);if(d.getDate()!==+m[1]||d.getMonth()!==+m[2]-1){state('err','Die datum bestaat niet.');return}
var b=why(d);if(b){state('err',b);return}sel=d;foc=new Date(d);view=new Date(d.getFullYear(),d.getMonth(),1);inp.value=pad(d.getDate())+'-'+pad(d.getMonth()+1)+'-'+d.getFullYear();state('ok','Gekozen: '+nice(d)+'.');if(!pop.hidden)render()}
inp.addEventListener('change',parse);
inp.addEventListener('keydown',function(e){if(e.key==='Enter'){e.preventDefault();parse()}if(e.key==='ArrowDown'&&e.altKey){e.preventDefault();open(true)}});
msg.textContent=hint();if(w.clientWidth>380)open(false);
}
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 →