Afrekenen in twee kolommen Nieuw
Afrekenpagina van een webshop (koffiebranderij): stappenbalk, contact en bezorgadres links, een vaste ordersamenvatting rechts (position:sticky) met aantallen die je nog kunt wijzigen. Verzendkeuze als radiokaarten (standaard, avondlevering, ophalen), drempel voor gratis verzending met voortgangsbalk, btw-splitsing 9% en 21%, totaal loopt live mee. Bij verder gaan: foutsamenvatting met links naar de velden plus inline fouten (aria-invalid, aria-describedby). Mobiel: samenvatting inklapbaar bovenaan. Container queries met media-query-fallback.
Het Ankerkoffiebranderij · Haarlem
- 1Winkelmand
- 2Gegevens en verzending
- 3Betalen
Je bestelling
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.
<!-- Afrekenen in twee kolommen · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="bck"><div class="bck__top"><p class="bck__logo">Het Anker<span>koffiebranderij · Haarlem</span></p><ol class="bck__steps"><li class="is-done"><span>1</span>Winkelmand</li><li aria-current="step"><span>2</span>Gegevens en verzending</li><li><span>3</span>Betalen</li></ol></div><div class="bck__grid"><button type="button" class="bck__stog" aria-expanded="false" aria-controls="bck-sum"><span>Toon bestelling</span><b class="bck__stot"></b></button><form class="bck__form" novalidate><div class="bck__errs" role="alert" tabindex="-1" hidden></div><fieldset class="bck__fs"><legend><span>1</span>Contact</legend><div class="bck__row"><div class="bck__f bck__f--w"><label for="bck-mail">E-mailadres</label><input id="bck-mail" type="email" autocomplete="email" required aria-describedby="bck-mail-e" value=""><p class="bck__e" id="bck-mail-e"></p></div><div class="bck__f bck__f--w"><label for="bck-tel">Telefoon <span>voor de bezorger, optioneel</span></label><input id="bck-tel" type="tel" autocomplete="tel" inputmode="tel"></div></div></fieldset><fieldset class="bck__fs"><legend><span>2</span>Bezorgadres</legend><div class="bck__row"><div class="bck__f bck__f--w"><label for="bck-naam">Voor- en achternaam</label><input id="bck-naam" autocomplete="name" required aria-describedby="bck-naam-e" value="Sem Bakker"><p class="bck__e" id="bck-naam-e"></p></div><div class="bck__f"><label for="bck-pc">Postcode</label><input id="bck-pc" autocomplete="postal-code" required aria-describedby="bck-pc-e" value="2011 TC" maxlength="7"><p class="bck__e" id="bck-pc-e"></p></div><div class="bck__f bck__f--s"><label for="bck-nr">Huisnr.</label><input id="bck-nr" inputmode="numeric" required aria-describedby="bck-nr-e" value=""><p class="bck__e" id="bck-nr-e"></p></div><div class="bck__f bck__f--w"><label for="bck-str">Straat</label><input id="bck-str" autocomplete="address-line1" required aria-describedby="bck-str-e" value="Kruisstraat"><p class="bck__e" id="bck-str-e"></p></div><div class="bck__f bck__f--w"><label for="bck-pl">Plaats</label><input id="bck-pl" autocomplete="address-level2" required aria-describedby="bck-pl-e" value="Haarlem"><p class="bck__e" id="bck-pl-e"></p></div></div></fieldset><fieldset class="bck__fs"><legend><span>3</span>Verzending</legend><div class="bck__ship"><label class="bck__opt"><input type="radio" name="bck-ship" value="std" checked><span class="bck__ot"><b>PostNL, standaard</b>Morgen in huis, als je voor 22:00 bestelt</span><span class="bck__op" data-p="std"></span></label><label class="bck__opt"><input type="radio" name="bck-ship" value="eve"><span class="bck__ot"><b>Avondlevering</b>Tussen 18:00 en 22:00</span><span class="bck__op">€ 6,95</span></label><label class="bck__opt"><input type="radio" name="bck-ship" value="pick"><span class="bck__ot"><b>Ophalen in de branderij</b>Zijlstraat 41, morgen vanaf 10:00 klaar</span><span class="bck__op">gratis</span></label></div></fieldset><div class="bck__act"><a class="bck__back" href="#bck-sum">Terug naar winkelmand</a><button type="submit" class="bck__go">Verder naar betalen</button></div><p class="bck__ok" role="status" aria-live="polite"></p></form><div class="bck__side"><div class="bck__sum" id="bck-sum"><p class="bck__sh">Je bestelling</p><ul class="bck__items"></ul><div class="bck__free"><p class="bck__ft"></p><div class="bck__fb"><span></span></div></div><dl class="bck__tl"></dl></div></div></div></div>
</div>/* Afrekenen in twee kolommen · NedDev · eigen werk. */
/* fonts (Fontshare): cabinet-grotesk@700,800 */
.bck{--ink:#14110e;--mut:#5d564d;--acc:#9a3b12;--bg:#f6f2ec;--line:rgba(20,17,14,.14);--err:#b42318;background:var(--bg);color:var(--ink);font-family:'Switzer',system-ui,sans-serif;container-type:inline-size}
.bck__top{display:flex;align-items:center;justify-content:space-between;gap:24px;max-width:1180px;margin:0 auto;padding:22px 40px;border-bottom:1px solid var(--line)}
.bck__logo{margin:0;font-family:'Cabinet Grotesk','Switzer',sans-serif;font-weight:800;font-size:24px;letter-spacing:-.01em;line-height:1}
.bck__logo span{display:block;margin-top:3px;font:500 10px 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--mut)}
.bck__steps{display:flex;gap:6px;margin:0;padding:0;list-style:none;font-size:13px;color:var(--mut)}
.bck__steps li{display:flex;align-items:center;gap:7px}
.bck__steps li+li::before{content:'';width:28px;height:1px;background:var(--line);margin-right:2px}
.bck__steps span{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;border:1px solid var(--line);font:600 11px 'JetBrains Mono',monospace}
.bck__steps .is-done span{background:var(--ink);color:#fff;border-color:var(--ink)}
.bck__steps [aria-current] {color:var(--ink);font-weight:600}
.bck__steps [aria-current] span{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
.bck__grid{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:48px;max-width:1180px;margin:0 auto;padding:28px 40px 48px;align-items:start}
.bck__stog{display:none}
.bck__form{margin:0}
.bck__errs{margin:0 0 18px;padding:12px 14px;border:1px solid var(--err);border-left-width:4px;border-radius:4px;background:#fdf1f0;font-size:14px;outline:none}
.bck__errs:focus-visible{box-shadow:0 0 0 3px rgba(180,35,24,.25)}
.bck__errs[hidden]{display:none}
.bck__errs p{margin:0 0 4px;font-weight:700}
.bck__errs ul{margin:0;padding-left:18px}
.bck__errs a{color:var(--err);font-weight:600}
.bck__fs{margin:0 0 22px;padding:0;border:0;min-width:0}
.bck__fs legend{display:flex;align-items:center;gap:8px;padding:0;margin-bottom:12px;font-family:'Cabinet Grotesk','Switzer',sans-serif;font-weight:700;font-size:19px}
.bck__fs legend span{font:500 11px 'JetBrains Mono',monospace;color:var(--acc)}
.bck__row{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:12px 12px}
.bck__f{grid-column:span 2;min-width:0}
.bck__f--w{grid-column:span 3}
.bck__f--s{grid-column:span 1}
.bck__f:nth-child(1):last-child{grid-column:1/-1}
.bck__f label{display:block;margin-bottom:5px;font-size:13px;font-weight:600}
.bck__f label span{font-weight:400;color:var(--mut)}
.bck__f input{display:block;width:100%;height:42px;padding:0 11px;border:1px solid rgba(20,17,14,.32);border-radius:4px;background:#fff;font:500 15px 'Switzer',system-ui,sans-serif;color:var(--ink);box-sizing:border-box;transition:border-color .15s,box-shadow .15s}
.bck__f input:hover{border-color:rgba(20,17,14,.6)}
.bck__f input:focus{outline:none;border-color:var(--ink);box-shadow:0 0 0 3px rgba(154,59,18,.25)}
.bck__f input[aria-invalid="true"]{border-color:var(--err);background:#fffafa}
.bck__f input[aria-invalid="true"]:focus{box-shadow:0 0 0 3px rgba(180,35,24,.2)}
.bck__f.is-ok input{border-color:#2f6b3f;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 16 16' fill='none' stroke='%232f6b3f' stroke-width='2'%3E%3Cpath d='m3 8.5 3 3 7-7'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:30px}
.bck__e{margin:4px 0 0;font-size:12.5px;color:var(--err);min-height:0}
.bck__e:empty{display:none}
.bck__ship{display:grid;gap:8px}
.bck__opt{position:relative;display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid rgba(20,17,14,.22);border-radius:6px;background:#fff;cursor:pointer;transition:border-color .15s,box-shadow .15s}
.bck__opt:hover{border-color:var(--ink)}
.bck__opt input{flex:none;width:18px;height:18px;margin:0;accent-color:var(--ink);cursor:pointer}
.bck__opt input:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.bck__opt:has(input:checked){border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
.bck__ot{flex:1;font-size:13px;color:var(--mut);line-height:1.35}
.bck__ot b{display:block;font-size:14.5px;color:var(--ink)}
.bck__op{font-weight:700;font-size:14px;font-variant-numeric:tabular-nums}
.bck__act{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px;padding-top:18px;border-top:1px solid var(--line)}
.bck__back{color:var(--ink);font-size:14px;text-underline-offset:3px}
.bck__back:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.bck__go{height:48px;padding:0 24px;border:0;border-radius:6px;background:var(--ink);color:#fff;font:600 15px 'Switzer',system-ui,sans-serif;cursor:pointer;transition:background-color .15s}
.bck__go:hover{background:var(--acc)}
.bck__go:active{transform:translateY(1px)}
.bck__go:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.bck__go:disabled{opacity:.6;cursor:progress}
.bck__ok{margin:10px 0 0;font-size:14px;font-weight:600;color:#2f6b3f;min-height:20px}
.bck__side{position:sticky;top:16px}
.bck__sum{padding:20px;border:1px solid var(--line);border-radius:8px;background:#fff}
.bck__sh{margin:0 0 12px;font-family:'Cabinet Grotesk','Switzer',sans-serif;font-weight:700;font-size:18px}
.bck__items{margin:0;padding:0;list-style:none}
.bck__it{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:12px;align-items:center;padding:10px 0;border-bottom:1px solid var(--line)}
.bck__th{width:44px;height:52px;border-radius:3px;display:grid;place-items:end center;padding-bottom:6px;box-sizing:border-box;font:700 9px 'JetBrains Mono',monospace;color:#fff;letter-spacing:.04em}
.bck__in b{display:block;font-size:14px;line-height:1.25}
.bck__in span{font-size:12.5px;color:var(--mut)}
.bck__q{display:flex;align-items:center;gap:4px;margin-top:5px}
.bck__q button{width:24px;height:24px;border:1px solid rgba(20,17,14,.28);border-radius:4px;background:#fff;color:var(--ink);font-size:14px;line-height:1;cursor:pointer;transition:border-color .15s,background-color .15s}
.bck__q button:hover:not(:disabled){border-color:var(--ink);background:var(--bg)}
.bck__q button:focus-visible{outline:2px solid var(--ink);outline-offset:1px}
.bck__q button:disabled{color:#b6aea3;cursor:not-allowed}
.bck__q output{min-width:20px;text-align:center;font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.bck__pr{font-weight:600;font-size:14px;font-variant-numeric:tabular-nums;align-self:start;padding-top:2px}
.bck__free{margin:14px 0 4px}
.bck__ft{margin:0 0 6px;font-size:12.5px;color:var(--mut)}
.bck__ft b{color:var(--ink)}
.bck__fb{height:4px;border-radius:2px;background:#ebe5dc;overflow:hidden}
.bck__fb span{display:block;height:100%;width:0;background:var(--acc);transition:width .3s}
.bck__tl{margin:12px 0 0;font-size:14px}
.bck__tl div{display:flex;justify-content:space-between;padding:3px 0}
.bck__tl dt{color:var(--mut)}
.bck__tl dd{margin:0;font-variant-numeric:tabular-nums}
.bck__tl .bck__tot{margin-top:8px;padding-top:10px;border-top:1px solid var(--line);font-weight:700;font-size:18px;align-items:baseline}
.bck__tl .bck__tot dt{color:var(--ink)}
.bck__tl .bck__vat{font-size:12px}
@container (max-width:880px){.bck__top{padding:16px;flex-wrap:wrap;gap:10px}.bck__steps{font-size:12px;flex-wrap:wrap}.bck__steps{gap:12px}.bck__steps li+li::before{display:none}.bck__grid{grid-template-columns:1fr;gap:16px;padding:12px 16px 32px}.bck__side{position:static;order:-1}.bck__stog{order:-2;display:flex;justify-content:space-between;align-items:center;width:100%;height:46px;padding:0 14px;border:1px solid var(--line);border-radius:6px;background:#fff;font:600 14px 'Switzer',system-ui,sans-serif;color:var(--ink);cursor:pointer}.bck__stog:focus-visible{outline:2px solid var(--ink);outline-offset:2px}.bck__stog span::after{content:'';display:inline-block;width:6px;height:6px;margin-left:8px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:translateY(-3px) rotate(45deg);transition:transform .2s}.bck__stog[aria-expanded="true"] span::after{transform:translateY(0) rotate(-135deg)}.bck__sum{display:none}.bck.is-sum .bck__sum{display:block}.bck__row{grid-template-columns:repeat(3,minmax(0,1fr))}.bck__f,.bck__f--w{grid-column:1/-1}.bck__row .bck__f:not(.bck__f--w):not(.bck__f--s){grid-column:span 2}.bck__f--s{grid-column:span 1}.bck__act{flex-direction:column-reverse;align-items:stretch}.bck__go{width:100%}}
@supports not (container-type:inline-size){@media (max-width:880px){.bck__grid{grid-template-columns:1fr}.bck__side{position:static}}}
@media (prefers-reduced-motion:reduce){.bck *{transition:none!important}}// Afrekenen in twee kolommen · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.bck');if(!w)return;
var form=w.querySelector('.bck__form'),items=w.querySelector('.bck__items'),tl=w.querySelector('.bck__tl'),ft=w.querySelector('.bck__ft'),fb=w.querySelector('.bck__fb span'),errs=w.querySelector('.bck__errs'),ok=w.querySelector('.bck__ok'),stog=w.querySelector('.bck__stog'),stot=w.querySelector('.bck__stot');
var FREE=50,SHIP={std:4.95,eve:6.95,pick:0};
/* producten: in productie uit de winkelmand */
var P=[{n:'Huismelange Het Anker',d:'bonen, 1 kg',p:26.5,q:1,vat:9,c:'#3b2a1e',t:'HUIS'},{n:'Ethiopië Yirgacheffe',d:'filtermaling, 250 g',p:11.95,q:1,vat:9,c:'#9a3b12',t:'ETH'},{n:'Filterpapier nr. 4',d:'100 stuks, ongebleekt',p:3.5,q:2,vat:21,c:'#b9a88f',t:'FLT'}];
function eur(n){return '\u20ac\u00a0'+n.toLocaleString('nl-NL',{minimumFractionDigits:2,maximumFractionDigits:2})}
function renderItems(){items.innerHTML=P.map(function(x,i){return '<li class="bck__it"><span class="bck__th" style="background:'+x.c+'" aria-hidden="true">'+x.t+'</span><div class="bck__in"><b>'+x.n+'</b><span>'+x.d+'</span><div class="bck__q" role="group" aria-label="Aantal '+x.n+'"><button type="button" data-i="'+i+'" data-d="-1" aria-label="Eén minder"'+(x.q<=1?' disabled':'')+'>−</button><output aria-live="polite">'+x.q+'</output><button type="button" data-i="'+i+'" data-d="1" aria-label="Eén meer"'+(x.q>=9?' disabled':'')+'>+</button></div></div><span class="bck__pr">'+eur(x.p*x.q)+'</span></li>'}).join('')}
function totals(){var sub=0,v9=0,v21=0;P.forEach(function(x){var s=x.p*x.q;sub+=s;if(x.vat===9)v9+=s-s/1.09;else v21+=s-s/1.21});
var ship=form.querySelector('input[name="bck-ship"]:checked').value,sc=SHIP[ship];if(ship==='std'&&sub>=FREE)sc=0;
if(sc){v21+=sc-sc/1.21}
var tot=sub+sc;w.querySelector('[data-p="std"]').innerHTML=sub>=FREE?'<s style="font-weight:400;color:#5d564d">€ 4,95</s> gratis':'€ 4,95';
var rest=Math.max(0,FREE-sub);ft.innerHTML=rest>0?'Nog <b>'+eur(rest)+'</b> tot gratis verzending':'<b>Gratis verzending</b> met PostNL standaard';fb.style.width=Math.min(100,sub/FREE*100)+'%';
tl.innerHTML='<div><dt>Subtotaal</dt><dd>'+eur(sub)+'</dd></div><div><dt>Verzending</dt><dd>'+(sc?eur(sc):'gratis')+'</dd></div><div class="bck__tot"><dt>Totaal</dt><dd>'+eur(tot)+'</dd></div><div class="bck__vat"><dt>waarvan btw 9% / 21%</dt><dd>'+eur(v9)+' / '+eur(v21)+'</dd></div>';stot.textContent=eur(tot)}
items.addEventListener('click',function(e){var b=e.target.closest('button[data-i]');if(!b)return;var x=P[+b.dataset.i];x.q=Math.max(1,Math.min(9,x.q+ +b.dataset.d));renderItems();totals();var nb=items.querySelector('button[data-i="'+b.dataset.i+'"][data-d="'+b.dataset.d+'"]');(nb.disabled?items.querySelector('button[data-i="'+b.dataset.i+'"]:not(:disabled)'):nb).focus()});
form.addEventListener('change',function(e){if(e.target.name==='bck-ship')totals()});
stog.addEventListener('click',function(){var o=!w.classList.contains('is-sum');w.classList.toggle('is-sum',o);stog.setAttribute('aria-expanded',o);stog.querySelector('span').textContent=o?'Verberg bestelling':'Toon bestelling'});
var RULES={'bck-mail':function(v){return !v?'Vul je e-mailadres in, dan sturen we de track-en-tracecode.':(/^[^@\s]+@[^@\s]+\.[a-z]{2,}$/i.test(v)?'':'Dit e-mailadres klopt niet, controleer de @ en de punt.')},'bck-naam':function(v){return v.trim().length<2?'Vul je naam in.':''},'bck-pc':function(v){return /^[1-9][0-9]{3} ?[a-z]{2}$/i.test(v.trim())?'':'Gebruik een postcode als 2011 TC.'},'bck-nr':function(v){return /^[0-9]{1,5}/.test(v.trim())?'':'Vul je huisnummer in.'},'bck-str':function(v){return v.trim()?'':'Vul je straat in.'},'bck-pl':function(v){return v.trim()?'':'Vul je plaats in.'}};
function check(id,show){var el=w.querySelector('#'+id),m=RULES[id](el.value),e=w.querySelector('#'+id+'-e'),box=el.closest('.bck__f');if(show){e.textContent=m;el.setAttribute('aria-invalid',m?'true':'false')}box.classList.toggle('is-ok',!m&&show!==false&&!!el.value);return m}
Object.keys(RULES).forEach(function(id){var el=w.querySelector('#'+id);el.addEventListener('blur',function(){if(el.value||el.getAttribute('aria-invalid'))check(id,true)});el.addEventListener('input',function(){if(el.getAttribute('aria-invalid')==='true')check(id,true)})});
w.querySelector('#bck-pc').addEventListener('blur',function(e){var v=e.target.value.trim().toUpperCase().replace(/^(\d{4}) ?([A-Z]{2})$/,'$1 $2');e.target.value=v});
form.addEventListener('submit',function(e){e.preventDefault();ok.textContent='';var bad=[];Object.keys(RULES).forEach(function(id){var m=check(id,true);if(m)bad.push([id,m])});
if(bad.length){errs.hidden=false;errs.innerHTML='<p>Controleer '+(bad.length===1?'1 veld':bad.length+' velden')+'</p><ul>'+bad.map(function(b){return '<li><a href="#'+b[0]+'" data-f="'+b[0]+'">'+b[1]+'</a></li>'}).join('')+'</ul>';errs.focus();return}
errs.hidden=true;var go=w.querySelector('.bck__go');go.disabled=true;go.textContent='Even controleren';setTimeout(function(){go.disabled=false;go.textContent='Verder naar betalen';ok.textContent='Alles klopt. Volgende stap: betalen met iDEAL, creditcard of Bancontact.';w.querySelectorAll('.bck__steps li')[1].classList.add('is-done')},700)});
errs.addEventListener('click',function(e){var a=e.target.closest('a[data-f]');if(!a)return;e.preventDefault();w.querySelector('#'+a.dataset.f).focus()});
w.querySelector('.bck__back').addEventListener('click',function(e){e.preventDefault();var s=w.querySelector('.bck__sum');if(!s.offsetParent){stog.click()}s.setAttribute('tabindex','-1');s.focus()});
renderItems();totals();
}
init(document.querySelector('.jouw-container'));
Meer uit paginasecties
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 →