20 CSS Loaders 04 / 20
CSS Progress Bar Loader
Five animated CSS progress bars — fill sweep, animated stripes, gradient shimmer, indeterminate bounce, and segmented pulse — covering the most common loading-state UI patterns.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="ld-04">
<div class="ld-04__stage">
<div class="ld-04__item">
<div class="ld-04__label"><span>Uploading file…</span><span>78%</span></div>
<div class="ld-04__track"><div class="ld-04__fill"></div></div> <div class="ld-04">
<div class="ld-04__stage">
<div class="ld-04__item">
<div class="ld-04__label"><span>Uploading file…</span><span>78%</span></div>
<div class="ld-04__track"><div class="ld-04__fill"></div></div>.ld-04,.ld-04 *,.ld-04 *::before,.ld-04 *::after{box-sizing:border-box;margin:0;padding:0}
.ld-04{
--bg:#0c0c1d;--c1:#a78bfa;--c2:#06b6d4;--c3:#f472b6;--c4:#34d399;
background:var(--bg);display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:'Segoe UI',sans-serif;padding:40px;
}
.ld-04__stage{display:flex;flex-direction:column;gap:36px;width:100%;max-width:480px}
.ld-04__item{display:flex;flex-direction:column;gap:10px}
.ld-04__label{display:flex;justify-content:space-between;color:rgba(255,255,255,.6);font-size:12px;letter-spacing:.5px}
.ld-04__track{height:8px;background:rgba(255,255,255,.08);border-radius:99px;overflow:hidden;position:relative}
/* Standard fill */
.ld-04__fill{height:100%;border-radius:99px;background:var(--c1);animation:ld-04-fill1 2.5s ease-in-out infinite}
@keyframes ld-04-fill1{0%{width:0%}60%{width:78%}100%{width:78%}}
/* Striped animated */
.ld-04__striped{height:100%;border-radius:99px;background:repeating-linear-gradient(45deg,var(--c2) 0,var(--c2) 10px,rgba(255,255,255,.15) 10px,rgba(255,255,255,.15) 20px);background-size:200% 100%;width:60%;animation:ld-04-stripes 1s linear infinite}
@keyframes ld-04-stripes{to{background-position:-200% 0}}
/* Gradient shimmer */
.ld-04__gfill{height:100%;border-radius:99px;width:55%;background:linear-gradient(90deg,var(--c3),var(--c1),var(--c3));background-size:200% 100%;animation:ld-04-glow 1.5s ease-in-out infinite}
@keyframes ld-04-glow{0%{background-position:100% 0}100%{background-position:-100% 0}}
/* Indeterminate bounce */
.ld-04__bounce{height:100%;border-radius:99px;background:var(--c4);width:40%;animation:ld-04-bounce 1.8s ease-in-out infinite}
@keyframes ld-04-bounce{0%{transform:translateX(-100%)}50%{transform:translateX(150%)}100%{transform:translateX(-100%)}}
/* Segmented */
.ld-04__seg-track{display:flex;gap:4px}
.ld-04__seg{flex:1;height:8px;border-radius:4px;background:rgba(255,255,255,.08);animation:ld-04-seg 2s ease-in-out infinite}
.ld-04__seg:nth-child(1){animation-delay:0s}
.ld-04__seg:nth-child(2){animation-delay:.2s}
.ld-04__seg:nth-child(3){animation-delay:.4s}
.ld-04__seg:nth-child(4){animation-delay:.6s}
.ld-04__seg:nth-child(5){animation-delay:.8s}
@keyframes ld-04-seg{0%,100%{background:rgba(255,255,255,.08)}50%{background:var(--c1)}}
@media(prefers-reduced-motion:reduce){
.ld-04__fill,.ld-04__striped,.ld-04__gfill,.ld-04__bounce,.ld-04__seg{animation:none;width:60%}
} .ld-04,.ld-04 *,.ld-04 *::before,.ld-04 *::after{box-sizing:border-box;margin:0;padding:0}
.ld-04{
--bg:#0c0c1d;--c1:#a78bfa;--c2:#06b6d4;--c3:#f472b6;--c4:#34d399;
background:var(--bg);display:flex;align-items:center;justify-content:center;min-height:100vh;font-family:'Segoe UI',sans-serif;padding:40px;
}
.ld-04__stage{display:flex;flex-direction:column;gap:36px;width:100%;max-width:480px}
.ld-04__item{display:flex;flex-direction:column;gap:10px}
.ld-04__label{display:flex;justify-content:space-between;color:rgba(255,255,255,.6);font-size:12px;letter-spacing:.5px}
.ld-04__track{height:8px;background:rgba(255,255,255,.08);border-radius:99px;overflow:hidden;position:relative}
/* Standard fill */
.ld-04__fill{height:100%;border-radius:99px;background:var(--c1);animation:ld-04-fill1 2.5s ease-in-out infinite}
@keyframes ld-04-fill1{0%{width:0%}60%{width:78%}100%{width:78%}}
/* Striped animated */
.ld-04__striped{height:100%;border-radius:99px;background:repeating-linear-gradient(45deg,var(--c2) 0,var(--c2) 10px,rgba(255,255,255,.15) 10px,rgba(255,255,255,.15) 20px);background-size:200% 100%;width:60%;animation:ld-04-stripes 1s linear infinite}
@keyframes ld-04-stripes{to{background-position:-200% 0}}
/* Gradient shimmer */
.ld-04__gfill{height:100%;border-radius:99px;width:55%;background:linear-gradient(90deg,var(--c3),var(--c1),var(--c3));background-size:200% 100%;animation:ld-04-glow 1.5s ease-in-out infinite}
@keyframes ld-04-glow{0%{background-position:100% 0}100%{background-position:-100% 0}}
/* Indeterminate bounce */
.ld-04__bounce{height:100%;border-radius:99px;background:var(--c4);width:40%;animation:ld-04-bounce 1.8s ease-in-out infinite}
@keyframes ld-04-bounce{0%{transform:translateX(-100%)}50%{transform:translateX(150%)}100%{transform:translateX(-100%)}}
/* Segmented */
.ld-04__seg-track{display:flex;gap:4px}
.ld-04__seg{flex:1;height:8px;border-radius:4px;background:rgba(255,255,255,.08);animation:ld-04-seg 2s ease-in-out infinite}
.ld-04__seg:nth-child(1){animation-delay:0s}
.ld-04__seg:nth-child(2){animation-delay:.2s}
.ld-04__seg:nth-child(3){animation-delay:.4s}
.ld-04__seg:nth-child(4){animation-delay:.6s}
.ld-04__seg:nth-child(5){animation-delay:.8s}
@keyframes ld-04-seg{0%,100%{background:rgba(255,255,255,.08)}50%{background:var(--c1)}}
@media(prefers-reduced-motion:reduce){
.ld-04__fill,.ld-04__striped,.ld-04__gfill,.ld-04__bounce,.ld-04__seg{animation:none;width:60%}
}How this works
The standard fill bar animates width from 0% to 78% using ease-in-out timing to mimic a real upload percentage. The striped bar uses repeating-linear-gradient(45deg, ...) with a background-position animation rather than width changes — the track width stays fixed at 60% while the stripes scroll, giving the illusion of movement at a constant fill level.
The indeterminate bounce bar is a fixed-width fill that translateXes from -100% through 150% and back, staying fully within the overflow:hidden track. The segmented bar uses five sibling divs with a cascading background colour animation from neutral to --c1 and back — no widths change, making it ideal for step-progress indicators. The gradient shimmer bar animates background-position on an oversized gradient for a flowing metallic look.
Customize
- Change the filled percentage of the sweep bar by editing the final
width:78%keyframe value — combine with a CSS custom property for runtime JS control. - Adjust stripe angle from
45degto-45degfor a reversed chevron effect, or90degfor vertical stripes. - Increase track height by editing
height:8pxon.ld-04__track— use4pxfor thin iOS-style bars or16pxfor chunky progress indicators. - Add a label overlay by positioning a
spanabsolutely inside the track withcolor: white; mix-blend-mode: differenceso it inverts against the fill. - Convert to a circular progress ring by swapping track divs for SVG
<circle>elements withstroke-dashoffsetanimation.
Watch out for
- Animating
widthinstead oftransform: scaleX()for the fill bar triggers layout reflow — usetransform-origin: left centerwithscaleXfor better performance on long lists. - The indeterminate bounce requires
overflow:hiddenon the track; without it the bouncing fill element is visible outside the bar bounds. - The striped bar's
background-positionanimation repaints on each frame in some older browsers — test on Android Chrome 80 and below if striped loaders are used in lists.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 49+ | 10+ | 44+ | 49+ |
All techniques use standard transforms and gradients; no modern CSS features required.