Productpagina met galerij, stalen en specs New
Complete product page top: gallery with 1/6 counter, arrows and arrow keys, colour swatches (radio, one sold out and disabled), specs with tooltips (Esc closes), shipping and returns accordions via details name with smooth height (::details-content + interpolate-size behind @supports, otherwise instant) and a sticky buy bar on mobile. Layout via container queries.
1 / 6
Plaids · Merinowol
Plaid Visgraat
Merinowol · 130 x 180 cm · geweven in Tilburg
9,2 uit 148 reviews
€ 129,00 incl. btw · gratis verzending
- Materiaal100% merinowolGesponnen in Biella, geweven en gewassen in onze weverij aan de Piushaven.
- Gewicht890 gZwaar genoeg voor de winter, licht genoeg om mee te nemen naar de bank.
- Afmeting130 x 180 cmInclusief franjes van 8 cm aan beide korte zijden.
- Wassen30 °C wolWolprogramma, niet centrifugeren boven 600 toeren, plat laten drogen.
Verzending
Gratis vanaf € 75,00. Op werkdagen vóór 22.00 uur besteld, de volgende dag in huis met PostNL. Afhalen in onze winkel aan de Piushaven in Tilburg kan ook, meestal na 2 uur.
Retour en ruilen
30 dagen bedenktijd. Meld je retour aan via je account en lever het pakket gratis in bij een PostNL-punt. Je geld staat binnen 5 werkdagen terug.
Onderhoud
Wol reinigt zichzelf grotendeels: hang de plaid een nacht buiten. Pluisjes in de eerste weken zijn normaal en verdwijnen vanzelf.
How to use it
- Paste the HTML where the page sections 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.
<!-- Productpagina met galerij, stalen en specs · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="ppb">
<div class="ppb__in">
<div class="ppb__gal">
<div class="ppb__main" tabindex="0" role="region" aria-roledescription="carrousel" aria-label="Productfoto's, gebruik pijltjestoetsen">
<img class="ppb__img" src="https://neddev.com/lab-media/productpagina-blokken-1.webp" alt="Plaid Visgraat in oker over een eikenhouten stoel" width="900" height="900">
<span class="ppb__cnt" aria-live="polite">1 / 6</span>
<button type="button" class="ppb__nav ppb__nav--prev" aria-label="Vorige foto"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M10 3L5 8l5 5"/></svg></button>
<button type="button" class="ppb__nav ppb__nav--next" aria-label="Volgende foto"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M6 3l5 5-5 5"/></svg></button>
</div>
<div class="ppb__thumbs" role="group" aria-label="Kies een foto"></div>
</div>
<div class="ppb__info">
<p class="ppb__crumb">Plaids · Merinowol</p>
<h2 class="ppb__h">Plaid Visgraat</h2>
<p class="ppb__sub">Merinowol · 130 x 180 cm · geweven in Tilburg</p>
<p class="ppb__rev"><b>9,2</b> uit 148 reviews</p>
<p class="ppb__price"><b>€ 129,00</b> <span>incl. btw · gratis verzending</span></p>
<fieldset class="ppb__sw"><legend>Kleur: <b class="ppb__swname">Oker</b></legend>
<label class="ppb__s" style="--c:#c39a2e"><input type="radio" name="ppb-kleur" value="0" data-n="Oker" data-st="Op voorraad, morgen in huis" checked><span class="ppb__vh">Oker</span></label>
<label class="ppb__s" style="--c:#2f6b62"><input type="radio" name="ppb-kleur" value="1" data-n="Zeegroen" data-st="Op voorraad, morgen in huis"><span class="ppb__vh">Zeegroen</span></label>
<label class="ppb__s" style="--c:#3a3d3f"><input type="radio" name="ppb-kleur" value="2" data-n="Antraciet" data-st="Nog 3 op voorraad"><span class="ppb__vh">Antraciet</span></label>
<label class="ppb__s ppb__s--off" style="--c:#9b4a2c"><input type="radio" name="ppb-kleur" value="-1" data-n="Roest" disabled><span class="ppb__vh">Roest, uitverkocht</span></label>
<p class="ppb__stock"><i></i><span>Op voorraad, morgen in huis</span></p>
<p class="ppb__soon">Roest is uitverkocht, weer binnen rond vr 13 nov. <a href="#">Mail mij</a></p>
</fieldset>
<div class="ppb__buy">
<button type="button" class="ppb__add"><span class="ppb__spin" aria-hidden="true"></span><span class="ppb__add-l">In winkelmand</span></button>
<button type="button" class="ppb__heart" aria-pressed="false" aria-label="Bewaar op verlanglijst"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20s-7.5-4.6-7.5-10.2A4.3 4.3 0 0 1 12 7.2a4.3 4.3 0 0 1 7.5 2.6C19.5 15.4 12 20 12 20z"/></svg></button>
</div>
<p class="ppb__msg" aria-live="polite"></p>
<ul class="ppb__specs">
<li><span class="ppb__sl">Materiaal</span><b>100% merinowol</b><button type="button" class="ppb__tip" aria-describedby="ppb-t1" aria-label="Meer over materiaal">i</button><span class="ppb__tt" role="tooltip" id="ppb-t1">Gesponnen in Biella, geweven en gewassen in onze weverij aan de Piushaven.</span></li>
<li><span class="ppb__sl">Gewicht</span><b>890 g</b><button type="button" class="ppb__tip" aria-describedby="ppb-t2" aria-label="Meer over gewicht">i</button><span class="ppb__tt" role="tooltip" id="ppb-t2">Zwaar genoeg voor de winter, licht genoeg om mee te nemen naar de bank.</span></li>
<li><span class="ppb__sl">Afmeting</span><b>130 x 180 cm</b><button type="button" class="ppb__tip" aria-describedby="ppb-t3" aria-label="Meer over afmeting">i</button><span class="ppb__tt" role="tooltip" id="ppb-t3">Inclusief franjes van 8 cm aan beide korte zijden.</span></li>
<li><span class="ppb__sl">Wassen</span><b>30 °C wol</b><button type="button" class="ppb__tip" aria-describedby="ppb-t4" aria-label="Meer over wassen">i</button><span class="ppb__tt" role="tooltip" id="ppb-t4">Wolprogramma, niet centrifugeren boven 600 toeren, plat laten drogen.</span></li>
</ul>
<div class="ppb__acc">
<details name="ppb-acc" class="ppb__d"><summary>Verzending</summary><div class="ppb__dc"><p>Gratis vanaf € 75,00. Op werkdagen vóór 22.00 uur besteld, de volgende dag in huis met PostNL. Afhalen in onze winkel aan de Piushaven in Tilburg kan ook, meestal na 2 uur.</p></div></details>
<details name="ppb-acc" class="ppb__d"><summary>Retour en ruilen</summary><div class="ppb__dc"><p>30 dagen bedenktijd. Meld je retour aan via je account en lever het pakket gratis in bij een PostNL-punt. Je geld staat binnen 5 werkdagen terug.</p></div></details>
<details name="ppb-acc" class="ppb__d"><summary>Onderhoud</summary><div class="ppb__dc"><p>Wol reinigt zichzelf grotendeels: hang de plaid een nacht buiten. Pluisjes in de eerste weken zijn normaal en verdwijnen vanzelf.</p></div></details>
</div>
</div>
</div>
<div class="ppb__sticky"><span><b>€ 129,00</b><small class="ppb__stname">Oker</small></span><button type="button" class="ppb__add ppb__add--s"><span class="ppb__spin" aria-hidden="true"></span><span class="ppb__add-l">In winkelmand</span></button></div>
</div>
</div>/* Productpagina met galerij, stalen en specs · NedDev · eigen werk. */
/* fonts (Fontshare): erode@400,500,600 + supreme@400,500,700 */
.ppb{--ink:#262421;--mut:#6a645b;--bg:#f5f2ec;--line:#ddd6ca;--acc:#8a5a12;container-type:inline-size;background:var(--bg);color:var(--ink);font-family:'Supreme','Switzer',sans-serif;box-sizing:border-box}
.ppb *,.ppb *::before{box-sizing:border-box}
.ppb__vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.ppb__in{max-width:1180px;margin:0 auto;padding:36px 32px 40px;display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);gap:44px;align-items:start}
.ppb__main{position:relative;aspect-ratio:1;background:#ebe5da;border-radius:4px;overflow:hidden;outline:none}
.ppb__main:focus-visible{box-shadow:0 0 0 3px var(--ink)}
.ppb__img{width:100%;height:100%;object-fit:cover;display:block}
.ppb__cnt{position:absolute;left:14px;bottom:14px;font:500 12px 'JetBrains Mono',monospace;background:rgba(255,255,255,.92);padding:5px 9px;border-radius:999px;font-variant-numeric:tabular-nums}
.ppb__nav{position:absolute;top:50%;width:40px;height:40px;margin-top:-20px;display:grid;place-items:center;border:0;border-radius:50%;background:rgba(255,255,255,.92);color:var(--ink);cursor:pointer;transition:background-color .15s ease}
.ppb__nav svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8}
.ppb__nav--prev{left:12px}.ppb__nav--next{right:12px}
.ppb__nav:hover{background:#fff}
.ppb__nav:active{transform:scale(.96)}
.ppb__nav:focus-visible{outline:3px solid var(--ink);outline-offset:2px}
.ppb__thumbs{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin-top:8px}
.ppb__th{padding:0;border:2px solid transparent;border-radius:3px;background:none;cursor:pointer;aspect-ratio:1;overflow:hidden;transition:border-color .15s ease,opacity .15s ease;opacity:.7}
.ppb__th img{width:100%;height:100%;object-fit:cover;display:block}
.ppb__th:hover{opacity:1}
.ppb__th[aria-current="true"]{border-color:var(--ink);opacity:1}
.ppb__th:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.ppb__crumb{margin:0 0 8px;font:500 11px 'JetBrains Mono',monospace;letter-spacing:.1em;text-transform:uppercase;color:var(--mut)}
.ppb__h{margin:0;font-family:'Erode',Georgia,serif;font-weight:500;font-size:clamp(30px,3.4vw,42px);line-height:1.05}
.ppb__sub{margin:6px 0 8px;font-size:14.5px;color:var(--mut)}
.ppb__rev{margin:0 0 14px;font-size:13.5px;color:var(--mut)}
.ppb__rev b{display:inline-grid;place-items:center;min-width:32px;height:24px;margin-right:4px;border-radius:3px;background:var(--ink);color:#fff;font-size:12.5px;font-variant-numeric:tabular-nums}
.ppb__price{margin:0 0 18px;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.ppb__price b{font-size:26px;font-weight:700;font-variant-numeric:tabular-nums}
.ppb__price span{font-size:13px;color:var(--mut)}
:where(.ppb) fieldset{border:0;margin:0;padding:0;min-width:0}
.ppb__sw{display:flex;flex-wrap:wrap;gap:10px;align-items:center;padding-bottom:16px;border-bottom:1px solid var(--line)}
.ppb__sw legend{float:left;width:100%;margin-bottom:8px;font-size:14px;color:var(--mut)}
.ppb__sw legend b{color:var(--ink)}
.ppb__s{position:relative;width:38px;height:38px;border-radius:50%;background:var(--c);cursor:pointer;box-shadow:0 0 0 1px rgba(0,0,0,.12) inset;transition:box-shadow .15s ease}
.ppb__s input{position:absolute;inset:0;opacity:0;margin:0;cursor:inherit}
.ppb__s:hover{box-shadow:0 0 0 1px rgba(0,0,0,.12) inset,0 0 0 3px var(--bg),0 0 0 4px #b5ac9c}
.ppb__s:has(input:checked){box-shadow:0 0 0 3px var(--bg),0 0 0 5px var(--ink)}
.ppb__s:has(input:focus-visible){outline:2px solid var(--acc);outline-offset:6px}
.ppb__s--off{cursor:not-allowed;opacity:.55}
.ppb__s--off::after{content:'';position:absolute;left:50%;top:-2px;bottom:-2px;width:2px;background:var(--bg);transform:rotate(45deg)}
.ppb__stock{flex-basis:100%;margin:6px 0 0;display:flex;align-items:center;gap:8px;font-size:13.5px}
.ppb__stock i{width:8px;height:8px;border-radius:50%;background:#2f7d4f}
.ppb__stock.is-low i{background:#c27c0e}
.ppb__soon{flex-basis:100%;margin:0;font-size:12.5px;color:var(--mut)}
.ppb__soon a{color:var(--ink)}
.ppb__buy{display:flex;gap:8px;margin-top:16px}
.ppb__add{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:50px;padding:0 20px;border:0;border-radius:3px;background:var(--ink);color:#fff;font:700 15.5px 'Supreme',sans-serif;cursor:pointer;transition:background-color .15s ease}
.ppb__add:hover{background:#000}
.ppb__add:active{transform:translateY(1px)}
.ppb__add:focus-visible,.ppb__heart:focus-visible{outline:3px solid var(--acc);outline-offset:2px}
.ppb__add[aria-busy="true"]{cursor:progress;background:#57524b}
.ppb__add.is-ok{background:#2f6b4a}
.ppb__spin{display:none;width:15px;height:15px;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:ppb-spin .7s linear infinite}
.ppb__add[aria-busy="true"] .ppb__spin{display:inline-block}
@keyframes ppb-spin{to{transform:rotate(360deg)}}
.ppb__heart{flex:none;width:50px;display:grid;place-items:center;border:1.5px solid var(--line);border-radius:3px;background:#fff;cursor:pointer;transition:border-color .15s ease}
.ppb__heart svg{width:22px;height:22px;fill:none;stroke:var(--ink);stroke-width:1.6;transition:fill .15s ease}
.ppb__heart:hover{border-color:var(--ink)}
.ppb__heart[aria-pressed="true"] svg{fill:#b8402a;stroke:#b8402a}
.ppb__msg{margin:8px 0 0;min-height:20px;font-size:13px;color:#2f6b4a;font-weight:500}
.ppb__specs{list-style:none;margin:12px 0 0;padding:0;display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--line)}
.ppb__specs li{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:2px 6px;padding:10px 0;border-bottom:1px solid var(--line)}
.ppb__specs li:nth-child(odd){padding-right:12px;border-right:1px solid var(--line)}
.ppb__specs li:nth-child(even){padding-left:14px}
.ppb__sl{flex-basis:100%;font:500 10.5px 'JetBrains Mono',monospace;letter-spacing:.08em;text-transform:uppercase;color:var(--mut)}
.ppb__specs b{font-size:14.5px;font-weight:600}
.ppb__tip{width:18px;height:18px;padding:0;border-radius:50%;border:1px solid #a39a8a;background:#fff;color:var(--mut);font:600 11px/1 'Erode',serif;font-style:italic;cursor:help}
.ppb__tip:hover,.ppb__tip:focus-visible{background:var(--ink);color:#fff;border-color:var(--ink)}
.ppb__tip:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.ppb__tt{position:absolute;left:0;bottom:calc(100% - 4px);z-index:3;width:min(240px,100%);padding:9px 11px;border-radius:4px;background:var(--ink);color:#fff;font-size:12.5px;line-height:1.45;opacity:0;visibility:hidden;transform:translateY(4px);transition:opacity .15s ease,transform .15s ease,visibility .15s}
.ppb__specs li:nth-child(even) .ppb__tt{left:14px}
.ppb__tip:hover+.ppb__tt,.ppb__tip:focus-visible+.ppb__tt{opacity:1;visibility:visible;transform:none}
.ppb__tip.is-esc+.ppb__tt{opacity:0;visibility:hidden}
.ppb__acc{margin-top:14px}
.ppb__d{border-bottom:1px solid var(--line)}
.ppb__d summary{list-style:none;display:flex;justify-content:space-between;align-items:center;padding:13px 0;font-weight:600;font-size:15px;cursor:pointer}
.ppb__d summary::-webkit-details-marker{display:none}
.ppb__d summary::after{content:'';width:9px;height:9px;border-right:1.6px solid var(--ink);border-bottom:1.6px solid var(--ink);transform:rotate(45deg) translateY(-3px);transition:transform .2s ease}
.ppb__d[open] summary::after{transform:rotate(-135deg) translateY(-2px)}
.ppb__d summary:hover{color:var(--acc)}
.ppb__d summary:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.ppb__dc p{margin:0 0 14px;font-size:14px;line-height:1.6;color:var(--mut)}
@supports (interpolate-size:allow-keywords){
.ppb__acc{interpolate-size:allow-keywords}
.ppb__d::details-content{block-size:0;overflow:hidden;transition:block-size .25s ease,content-visibility .25s allow-discrete}
.ppb__d[open]::details-content{block-size:auto}
}
.ppb__sticky{display:none}
@container (max-width:760px){
.ppb__in{grid-template-columns:1fr;gap:20px;padding:16px 14px 20px}
.ppb__thumbs{gap:5px}
.ppb__sticky{position:sticky;bottom:0;z-index:4;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;background:#fff;border-top:1px solid var(--line);box-shadow:0 -8px 20px -12px rgba(0,0,0,.25)}
.ppb__sticky>span{display:flex;flex-direction:column;line-height:1.2}
.ppb__sticky b{font-size:17px}
.ppb__sticky small{font-size:12px;color:var(--mut)}
.ppb__add--s{flex:0 1 200px;min-height:44px}
.ppb__specs{grid-template-columns:1fr 1fr}
}
@media (prefers-reduced-motion:reduce){.ppb *,.ppb__d::details-content{transition:none!important}.ppb__spin{animation-duration:2s}}// Productpagina met galerij, stalen en specs · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
const w=root.querySelector('.ppb'); if(!w) return;
const IM=[1,2,3,4,5,6].map(i=>'https://neddev.com/lab-media/productpagina-blokken-'+i+'.webp');
const ALT=['Plaid Visgraat in oker over een eikenhouten stoel','Plaid Visgraat in zeegroen over een stoel','Plaid Visgraat in antraciet over een stoel','Close-up van het visgraatweefsel met franjes','Drie opgevouwen plaids in oker, zeegroen en antraciet','Okergele plaid over een linnen bank in de woonkamer'];
const img=w.querySelector('.ppb__img'), cnt=w.querySelector('.ppb__cnt'), th=w.querySelector('.ppb__thumbs'), main=w.querySelector('.ppb__main');
th.innerHTML=IM.map((s,i)=>'<button type="button" class="ppb__th" aria-label="Foto '+(i+1)+' van 6"'+(i===0?' aria-current="true"':'')+'><img src="'+s+'" alt="" loading="lazy"></button>').join('');
const ths=[...th.children]; let cur=0;
const show=i=>{cur=(i+IM.length)%IM.length; img.src=IM[cur]; img.alt=ALT[cur]; cnt.textContent=(cur+1)+' / '+IM.length; ths.forEach((b,k)=>b.setAttribute('aria-current',k===cur?'true':'false'));};
ths.forEach((b,i)=>b.addEventListener('click',()=>show(i)));
w.querySelector('.ppb__nav--prev').addEventListener('click',()=>show(cur-1));
w.querySelector('.ppb__nav--next').addEventListener('click',()=>show(cur+1));
main.addEventListener('keydown',e=>{if(e.key==='ArrowLeft'){e.preventDefault(); show(cur-1);} else if(e.key==='ArrowRight'){e.preventDefault(); show(cur+1);}});
const swn=w.querySelector('.ppb__swname'), stock=w.querySelector('.ppb__stock'), stn=w.querySelector('.ppb__stname');
w.querySelectorAll('input[name="ppb-kleur"]').forEach(r=>r.addEventListener('change',()=>{if(r.disabled) return; swn.textContent=r.dataset.n; stn.textContent=r.dataset.n; stock.querySelector('span').textContent=r.dataset.st; stock.classList.toggle('is-low',/Nog/.test(r.dataset.st)); show(+r.value);
w.querySelectorAll('.ppb__add').forEach(b=>{b.classList.remove('is-ok'); b.querySelector('.ppb__add-l').textContent='In winkelmand';});}));
const msg=w.querySelector('.ppb__msg'), adds=[...w.querySelectorAll('.ppb__add')];
adds.forEach(b=>b.addEventListener('click',()=>{if(b.getAttribute('aria-busy')==='true') return;
adds.forEach(x=>{x.setAttribute('aria-busy','true'); x.querySelector('.ppb__add-l').textContent='Toevoegen...';});
setTimeout(()=>{adds.forEach(x=>{x.removeAttribute('aria-busy'); x.classList.add('is-ok'); x.querySelector('.ppb__add-l').textContent='In je winkelmand';}); msg.textContent='Plaid Visgraat, '+swn.textContent.toLowerCase()+', toegevoegd. Morgen in huis bij betaling vóór 22.00 uur.';},700);}));
const h=w.querySelector('.ppb__heart'); h.addEventListener('click',()=>{const on=h.getAttribute('aria-pressed')!=='true'; h.setAttribute('aria-pressed',on?'true':'false'); msg.textContent=on?'Bewaard op je verlanglijst.':'Verwijderd van je verlanglijst.';});
w.querySelectorAll('.ppb__tip').forEach(t=>{t.addEventListener('keydown',e=>{if(e.key==='Escape') t.classList.add('is-esc');}); t.addEventListener('blur',()=>t.classList.remove('is-esc')); t.addEventListener('mouseleave',()=>t.classList.remove('is-esc'));});
w.querySelectorAll('a[href="#"]').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));
}
init(document.querySelector('.jouw-container'));
More page sections
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 →