Uploaden met voortgang per bestand New
A drop zone that lights up when you hold files over it, with a progress bar, cancel button and done state per file. For quote requests with photos and drawings.
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.
<!-- Uploaden met voortgang per bestand · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="upl"><label class="upl__zone"><input type="file" multiple class="upl__inp"><span class="upl__ic" aria-hidden="true"><svg viewBox="0 0 20 20" width="18" height="18"><path d="M10 13V3M6 7l4-4 4 4M3 13v3h14v-3" fill="none" stroke="currentColor" stroke-width="1.6"/></svg></span><span class="upl__t"><span class="upl__t1"><b>Sleep bestanden hierheen</b> of <u>kies</u></span><span class="upl__t2">Laat los om te uploaden</span></span><span class="upl__lim">PDF, JPG, DWG · max 25 MB</span></label>
<ul class="upl__list" aria-live="polite"></ul></div>
</div>/* Uploaden met voortgang per bestand · NedDev · eigen werk. */
.upl{position:absolute;inset:0;background:#f4f3f1;font-family:'Switzer',system-ui,sans-serif;color:#0a0a0a;padding:12px;display:flex;flex-direction:column;gap:8px;container-type:size}
.upl__zone{position:relative;display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto auto;column-gap:10px;align-items:center;padding:10px 12px;border:1.5px dashed #bdb7af;border-radius:6px;background:#fff;cursor:pointer;transition:border-color .2s,background .2s,transform .2s}
.upl__zone:hover{border-color:#0a0a0a}
.upl__zone:focus-within{outline:2px solid #F5480F;outline-offset:2px}
.upl__zone.is-over{border-color:#F5480F;border-style:solid;background:#fff4ef;transform:scale(1.015)}
.upl__inp{position:absolute;width:1px;height:1px;opacity:0}
.upl__ic{grid-row:span 2;width:32px;height:32px;border-radius:50%;background:#0a0a0a;color:#fff;display:grid;place-items:center;transition:background .2s,transform .3s}
.upl__zone.is-over .upl__ic{background:#F5480F;transform:translateY(-2px)}
.upl__t{position:relative;display:grid;font-size:12.5px;overflow:hidden;height:1.3em;line-height:1.3}
.upl__t1,.upl__t2{transition:transform .3s cubic-bezier(.7,0,.2,1)}
.upl__t2{position:absolute;inset:0;transform:translateY(110%);color:#F5480F;font-weight:700}
.upl__zone.is-over .upl__t1{transform:translateY(-110%)}.upl__zone.is-over .upl__t2{transform:none}
.upl__t u{text-decoration-color:#F5480F;text-underline-offset:2px}
.upl__lim{font:500 9.5px 'JetBrains Mono',monospace;color:#8a857f}
.upl__list{list-style:none;margin:0;padding:0;flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:5px;scrollbar-width:thin}
.upl__row{display:grid;grid-template-columns:30px 1fr auto 22px;gap:8px;align-items:center;background:#fff;border-radius:5px;padding:6px 6px 6px 7px;animation:upl-in .35s cubic-bezier(.2,.8,.2,1) both}
@keyframes upl-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.upl__ty{font:700 8.5px 'JetBrains Mono',monospace;background:#f4f3f1;border-radius:3px;padding:6px 0;text-align:center;color:#0a0a0a}
.upl__ty--pdf{background:#0a0a0a;color:#fff}.upl__ty--jpg{background:#F5480F;color:#fff}
.upl__mid{min-width:0;display:flex;flex-direction:column;gap:4px}
.upl__nm{font-size:11.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.upl__bar{height:3px;background:#eeebe7;border-radius:3px;overflow:hidden}
.upl__bar span{display:block;height:100%;width:0;background:#F5480F;border-radius:3px}
.upl__row.is-done .upl__bar span{background:#0a0a0a}
.upl__st{font:500 9.5px 'JetBrains Mono',monospace;color:#6b6762;font-variant-numeric:tabular-nums;white-space:nowrap;display:flex;align-items:center;gap:4px}
.upl__row.is-done .upl__st{color:#0a0a0a}
.upl__st svg{color:#F5480F}
.upl__x{appearance:none;width:22px;height:22px;border:0;border-radius:50%;background:none;color:#8a857f;display:grid;place-items:center;cursor:pointer;transition:background .2s,color .2s}
.upl__x:hover{background:#f4f3f1;color:#0a0a0a}.upl__x:focus-visible{outline:2px solid #F5480F}
@container (max-height:225px){.upl__lim{display:none}.upl__zone{padding:7px 10px}.upl__ic{width:26px;height:26px;grid-row:auto}}// Uploaden met voortgang per bestand · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
root=root.querySelector('.upl')||root;
var zone=root.querySelector('.upl__zone'),inp=root.querySelector('.upl__inp'),list=root.querySelector('.upl__list');
var rm=matchMedia('(prefers-reduced-motion: reduce)').matches,touched=false,demoT=[];
var X='<svg viewBox="0 0 12 12" width="10" height="10" aria-hidden="true"><path d="M2.5 2.5l7 7M9.5 2.5l-7 7" stroke="currentColor" stroke-width="1.5"/></svg>';
var CK='<svg viewBox="0 0 12 12" width="10" height="10" aria-hidden="true"><path d="M2 6.5l2.5 2.5L10 3" fill="none" stroke="currentColor" stroke-width="1.8"/></svg>';
function size(b){return b>=1048576?(b/1048576).toFixed(1).replace('.',',')+' MB':Math.max(1,Math.round(b/1024))+' kB';}
function add(name,bytes){var ext=(name.split('.').pop()||'').toLowerCase().slice(0,4),li=document.createElement('li');li.className='upl__row';
li.innerHTML='<span class="upl__ty upl__ty--'+ext+'">'+ext.toUpperCase()+'</span><span class="upl__mid"><span class="upl__nm"></span><span class="upl__bar"><span></span></span></span><span class="upl__st">0%</span><button class="upl__x" type="button">'+X+'</button>';
li.querySelector('.upl__nm').textContent=name;li.querySelector('.upl__x').setAttribute('aria-label','Verwijder '+name);
list.appendChild(li);list.scrollTop=list.scrollHeight;
var bar=li.querySelector('.upl__bar span'),st=li.querySelector('.upl__st'),done=0,alive=true,rate=(700000+Math.random()*900000)*(rm?20:1);
var last=performance.now();
(function tick(now){if(!alive)return;var dt=(now-last)/1000;last=now;done=Math.min(bytes,done+rate*dt*(.6+Math.random()*.8));var p=done/bytes;bar.style.width=(p*100).toFixed(1)+'%';
if(p<1){st.textContent=Math.floor(p*100)+'%';requestAnimationFrame(tick);}else{li.classList.add('is-done');st.innerHTML=CK+' '+size(bytes);}})(last);
li.querySelector('.upl__x').addEventListener('click',function(){touched=true;alive=false;var h=li.offsetHeight;
if(rm){li.remove();return;}li.animate([{height:h+'px',opacity:1},{height:'0px',opacity:0,paddingTop:'0px',paddingBottom:'0px',marginTop:'-5px'}],{duration:260,easing:'cubic-bezier(.7,0,.2,1)'}).onfinish=function(){li.remove();};});}
function demo(){if(touched)return;list.innerHTML='';[['offerte-dakkapel.pdf',2516582],['foto-gevel-voorzijde.jpg',3250585],['plattegrond-zolder.dwg',860160]].forEach(function(f,i){demoT.push(setTimeout(function(){if(!touched)add(f[0],f[1]);},300+i*350));});demoT.push(setTimeout(demo,9000));}
function stopDemo(){if(touched)return;touched=true;demoT.forEach(clearTimeout);}
['dragenter','dragover'].forEach(function(ev){zone.addEventListener(ev,function(e){e.preventDefault();stopDemo();zone.classList.add('is-over');});});
['dragleave','dragend'].forEach(function(ev){zone.addEventListener(ev,function(e){if(ev==='dragleave'&&zone.contains(e.relatedTarget))return;zone.classList.remove('is-over');});});
zone.addEventListener('drop',function(e){e.preventDefault();zone.classList.remove('is-over');var fs=e.dataTransfer&&e.dataTransfer.files;if(fs)[].forEach.call(fs,function(f){add(f.name,f.size||1);});});
inp.addEventListener('click',stopDemo);
inp.addEventListener('change',function(){stopDemo();[].forEach.call(inp.files,function(f){add(f.name,f.size||1);});inp.value='';});
demo();
}
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 →