Striped progress bar
A determinate progress bar that fills smoothly, with moving diagonal stripes to show activity.
CSSJavaScriptBeginner
<div class="wrap">
<div class="label"><span>Uploading</span><span id="pct">0%</span></div>
<div class="track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" id="track"><div class="fill" id="fill"></div></div>
</div>
.wrap{width:min(360px,88vw)}
.label{display:flex;justify-content:space-between;margin-bottom:10px;font-size:14px;color:#cfc3c1}
.track{height:14px;border-radius:999px;background:#2e1b19;overflow:hidden}
.fill{height:100%;width:100%;transform-origin:left;transform:scaleX(0);border-radius:inherit;transition:transform .25s linear;
background:repeating-linear-gradient(45deg,#f08a5d 0 12px,#d9744a 12px 24px);background-size:34px 34px;animation:move 1s linear infinite}
@keyframes move{to{background-position:34px 0}}
const fill = document.getElementById('fill');
const pct = document.getElementById('pct');
const track = document.getElementById('track');
let v = 0;
const timer = setInterval(() => {
v = Math.min(100, v + Math.random() * 7);
fill.style.transform = `scaleX(${v / 100})`;
pct.textContent = Math.round(v) + '%';
track.setAttribute('aria-valuenow', Math.round(v));
if (v >= 100) { clearInterval(timer); pct.textContent = 'Done'; }
}, 160);
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Progress bar</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
.wrap{width:min(360px,88vw)}
.label{display:flex;justify-content:space-between;margin-bottom:10px;font-size:14px;color:#cfc3c1}
.track{height:14px;border-radius:999px;background:#2e1b19;overflow:hidden}
.fill{height:100%;width:100%;transform-origin:left;transform:scaleX(0);border-radius:inherit;transition:transform .25s linear;
background:repeating-linear-gradient(45deg,#f08a5d 0 12px,#d9744a 12px 24px);background-size:34px 34px;animation:move 1s linear infinite}
@keyframes move{to{background-position:34px 0}}
</style></head>
<body>
<div class="wrap">
<div class="label"><span>Uploading</span><span id="pct">0%</span></div>
<div class="track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" id="track"><div class="fill" id="fill"></div></div>
</div>
<script>
const fill = document.getElementById('fill');
const pct = document.getElementById('pct');
const track = document.getElementById('track');
let v = 0;
const timer = setInterval(() => {
v = Math.min(100, v + Math.random() * 7);
fill.style.transform = `scaleX(${v / 100})`;
pct.textContent = Math.round(v) + '%';
track.setAttribute('aria-valuenow', Math.round(v));
if (v >= 100) { clearInterval(timer); pct.textContent = 'Done'; }
}, 160);
</script>
</body></html>
Good for
Uploads, installs and multi-step processes where you know the percentage.
Watch out for
Use a real progress element or aria-valuenow for accessibility. Animate transform, not width, for smoothness.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.