Productgrid met filters en winkelwagen Nieuw
Webshopgrid met snelle filters op categorie en prijs, sorteren en een winkelwagenknop met tellende animatie.
Branderij Bonte Boon · Nijmegen
Vers gebrand, deze week
Niets gevonden met deze filters.
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.
<!-- Productgrid met filters en winkelwagen · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="xsg">
<div class="xsg__inner">
<div class="xsg__head">
<div><span class="xsg__eyebrow">Branderij Bonte Boon · Nijmegen</span><h2 class="xsg__h">Vers gebrand, deze week</h2></div>
<button type="button" class="xsg__cart" aria-label="Winkelwagen, 0 artikelen"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 4h2.5l2.2 11h10.6l2-8H7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><circle cx="9.5" cy="19.5" r="1.5" fill="currentColor"/><circle cx="17" cy="19.5" r="1.5" fill="currentColor"/></svg><span class="xsg__count">0</span></button>
</div>
<div class="xsg__bar">
<div class="xsg__chips" role="group" aria-label="Categorie"><button type="button" class="is-on" data-c="alle">Alles</button><button type="button" data-c="bonen">Bonen</button><button type="button" data-c="gemalen">Gemalen</button><button type="button" data-c="acc">Zetten</button></div>
<div class="xsg__sel">
<label><span>Prijs</span><select class="xsg__price"><option value="999">Alle prijzen</option><option value="12">Tot € 12</option><option value="25">Tot € 25</option></select></label>
<label><span>Sorteer</span><select class="xsg__sort"><option value="pop">Populair</option><option value="asc">Prijs laag naar hoog</option><option value="desc">Prijs hoog naar laag</option></select></label>
</div>
</div>
<ul class="xsg__grid"></ul>
<p class="xsg__empty" hidden>Niets gevonden met deze filters. <button type="button" class="xsg__clear">Wis filters</button></p>
</div>
</div>
</div>/* Productgrid met filters en winkelwagen · NedDev · eigen werk. */
.xsg{background:#f3e9dc;color:#2b1d16;font-family:'Switzer',sans-serif;box-sizing:border-box}
.xsg *{box-sizing:border-box}
.xsg__inner{max-width:1160px;margin:0 auto;padding:30px 32px 28px}
.xsg__head{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin-bottom:18px}
.xsg__eyebrow{display:block;font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:#a24f1f;margin-bottom:8px}
.xsg__h{font-size:clamp(24px,3vw,34px);line-height:1.05;letter-spacing:-.02em;font-weight:700;margin:0}
.xsg__cart{position:relative;width:48px;height:48px;border-radius:50%;background:#2b1d16;color:#f3e9dc;border:0;cursor:pointer;display:grid;place-items:center;flex:none}
.xsg__cart svg{width:22px;height:22px}
.xsg__count{position:absolute;top:-4px;right:-4px;min-width:22px;height:22px;border-radius:11px;background:#c0612b;color:#fff;font-size:12px;font-weight:700;display:grid;place-items:center;padding:0 5px;border:2px solid #f3e9dc}
.xsg__cart.is-bump{animation:xsg-bump .45s cubic-bezier(.3,1.6,.5,1)}
.xsg__cart.is-bump .xsg__count{animation:xsg-pop .45s cubic-bezier(.3,1.6,.5,1)}
@keyframes xsg-bump{0%{transform:scale(1)}40%{transform:scale(1.14) rotate(-6deg)}100%{transform:scale(1)}}
@keyframes xsg-pop{0%{transform:scale(.4)}60%{transform:scale(1.35)}100%{transform:scale(1)}}
.xsg__bar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 0;border-top:1px solid rgba(43,29,22,.18);border-bottom:1px solid rgba(43,29,22,.18);margin-bottom:18px}
.xsg__chips{display:flex;gap:6px;flex-wrap:wrap}
.xsg__chips button{font:600 13px 'Switzer',sans-serif;background:transparent;color:#2b1d16;border:1.5px solid rgba(43,29,22,.3);border-radius:999px;padding:7px 14px;cursor:pointer;transition:background .2s,color .2s,border-color .2s}
.xsg__chips button:hover{border-color:#2b1d16}
.xsg__chips button.is-on{background:#2b1d16;color:#f3e9dc;border-color:#2b1d16}
.xsg__sel{display:flex;gap:10px}
.xsg__sel label{display:flex;align-items:center;gap:6px;font-size:12px;color:#7a6556}
.xsg__sel select{font:600 13px 'Switzer',sans-serif;color:#2b1d16;background:#fffaf3;border:1.5px solid rgba(43,29,22,.25);border-radius:4px;padding:7px 8px;cursor:pointer}
.xsg__grid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
.xsg__card{background:#fffaf3;border-radius:6px;padding:12px;display:flex;flex-direction:column;animation:xsg-in .35s ease both}
@keyframes xsg-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.xsg__img{height:104px;border-radius:4px;background:var(--bg);position:relative;overflow:hidden;margin-bottom:10px}
.xsg__img::before{content:'';position:absolute;left:50%;bottom:8px;width:56px;height:84px;margin-left:-28px;background:var(--bag);clip-path:polygon(8% 12%,20% 0,80% 0,92% 12%,100% 100%,0 100%);box-shadow:inset 0 0 0 0 transparent}
.xsg__img::after{content:attr(data-l);position:absolute;left:50%;bottom:26px;width:48px;margin-left:-24px;background:#fffaf3;color:#2b1d16;font:700 8px 'JetBrains Mono',monospace;letter-spacing:.04em;text-align:center;padding:5px 2px;text-transform:uppercase}
.xsg__img--acc::before{width:76px;height:54px;margin-left:-38px;bottom:24px;clip-path:polygon(0 0,100% 0,72% 100%,28% 100%);background:var(--bag)}
.xsg__img--acc::after{bottom:12px;width:70px;margin-left:-35px;height:10px;padding:0;font-size:0;background:var(--bag);border-radius:2px;opacity:.75}
.xsg__badge{position:absolute;top:8px;left:8px;background:#2b1d16;color:#f3e9dc;font-family:'JetBrains Mono',monospace;font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;padding:3px 6px;border-radius:2px;z-index:1}
.xsg__name{font-weight:600;font-size:14.5px;line-height:1.25}
.xsg__meta{font-size:12px;color:#7a6556;margin:3px 0 10px}
.xsg__row{margin-top:auto;display:flex;justify-content:space-between;align-items:center;gap:8px}
.xsg__pr{font-weight:700;font-size:16px;font-variant-numeric:tabular-nums}
.xsg__add{font:600 12.5px 'Switzer',sans-serif;background:#c0612b;color:#fff;border:0;border-radius:4px;padding:8px 10px;cursor:pointer;transition:background .2s,transform .15s;white-space:nowrap}
.xsg__add:hover{background:#a24f1f}
.xsg__add:active{transform:scale(.95)}
.xsg__add.is-done{background:#2f6b45}
.xsg__empty{text-align:center;font-size:14px;color:#7a6556;padding:30px 0}
.xsg__clear{font:600 14px 'Switzer',sans-serif;background:none;border:0;color:#2b1d16;text-decoration:underline;cursor:pointer}
@media(prefers-reduced-motion:reduce){.xsg__card,.xsg__cart.is-bump,.xsg__cart.is-bump .xsg__count{animation:none}}
@media(max-width:900px){.xsg__grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.xsg__inner{padding:28px 14px}.xsg__grid{gap:10px}.xsg__img{height:110px}.xsg__sel{width:100%}.xsg__sel label{flex:1}.xsg__sel select{width:100%}.xsg__sel label span{display:none}.xsg__row{flex-direction:column;align-items:stretch}.xsg__add{width:100%}}// Productgrid met filters en winkelwagen · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.xsg');if(!w)return;
var P=[
{n:'Huismelange Nijmegen',m:'bonen · 500 g · chocolade, noot',c:'bonen',p:12.95,pop:1,bg:'#e3d3bd',bag:'#2b1d16',l:'Huis',b:'Bestseller'},
{n:'Ethiopië Yirgacheffe',m:'bonen · 250 g · bloemig, citrus',c:'bonen',p:11.50,pop:3,bg:'#efd9c4',bag:'#c0612b',l:'Ethiopië'},
{n:'Espresso Donker',m:'bonen · 500 g · cacao, karamel',c:'bonen',p:13.75,pop:2,bg:'#d9c6ae',bag:'#4a2c1e',l:'Espresso'},
{n:'Brazilië Cerrado',m:'bonen · 1 kg · noot, mild',c:'bonen',p:24.95,pop:5,bg:'#e6dccb',bag:'#5d6b3a',l:'Brazilië'},
{n:'Colombia Huila',m:'gemalen filter · 250 g · rood fruit',c:'gemalen',p:9.95,pop:4,bg:'#f0e0cc',bag:'#8a2f2a',l:'Colombia'},
{n:'Decaf Mexico',m:'gemalen filter · 250 g · zacht',c:'gemalen',p:10.50,pop:7,bg:'#e4dccf',bag:'#36506b',l:'Decaf'},
{n:'Handfilter keramiek',m:'zetten · voor 1 tot 2 koppen',c:'acc',p:27.50,pop:6,bg:'#e8e1d6',bag:'#f7f3ec',l:'',acc:1},
{n:'Handmolen RVS',m:'zetten · 38 mm conische maalschijf',c:'acc',p:44.00,pop:8,bg:'#ddd6cb',bag:'#8f8a83',l:'',acc:1,b:'Nieuw'}
];
var st={c:'alle',max:999,sort:'pop'},cart=0,grid=w.querySelector('.xsg__grid'),empty=w.querySelector('.xsg__empty'),cb=w.querySelector('.xsg__cart'),cnt=w.querySelector('.xsg__count');
function eur(n){return '€ '+n.toLocaleString('nl-NL',{minimumFractionDigits:2,maximumFractionDigits:2});}
function render(){
var l=P.filter(function(x){return (st.c==='alle'||x.c===st.c)&&x.p<=st.max;});
l.sort(function(a,b){return st.sort==='asc'?a.p-b.p:st.sort==='desc'?b.p-a.p:a.pop-b.pop;});
grid.innerHTML=l.map(function(x,i){return '<li class="xsg__card" style="animation-delay:'+(i*.03)+'s"><div class="xsg__img'+(x.acc?' xsg__img--acc':'')+'" style="--bg:'+x.bg+';--bag:'+x.bag+'" data-l="'+x.l+'">'+(x.b?'<span class="xsg__badge">'+x.b+'</span>':'')+'</div><span class="xsg__name">'+x.n+'</span><span class="xsg__meta">'+x.m+'</span><div class="xsg__row"><span class="xsg__pr">'+eur(x.p)+'</span><button type="button" class="xsg__add">In winkelwagen</button></div></li>';}).join('');
empty.hidden=l.length>0;
}
grid.addEventListener('click',function(e){var b=e.target.closest('.xsg__add');if(!b)return;cart++;cnt.textContent=cart;cb.setAttribute('aria-label','Winkelwagen, '+cart+' artikelen');
cb.classList.remove('is-bump');void cb.offsetWidth;cb.classList.add('is-bump');
b.classList.add('is-done');b.textContent='Toegevoegd';clearTimeout(b._t);b._t=setTimeout(function(){b.classList.remove('is-done');b.textContent='In winkelwagen';},1400);});
w.querySelector('.xsg__chips').addEventListener('click',function(e){var b=e.target.closest('button');if(!b)return;w.querySelectorAll('.xsg__chips button').forEach(function(x){x.classList.toggle('is-on',x===b);});st.c=b.getAttribute('data-c');render();});
var ps=w.querySelector('.xsg__price'),ss=w.querySelector('.xsg__sort');
ps.addEventListener('change',function(){st.max=+ps.value;render();});
ss.addEventListener('change',function(){st.sort=ss.value;render();});
w.querySelector('.xsg__clear').addEventListener('click',function(){st={c:'alle',max:999,sort:st.sort};ps.value='999';w.querySelectorAll('.xsg__chips button').forEach(function(x,i){x.classList.toggle('is-on',i===0);});render();});
render();
}
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 →