22 CSS Transition Effects 22 / 22
Progress Bar Animation
Labelled skill bars, shimmer and striped variants, thin page-load indicators, circular SVG rings and step segment indicators with animated fills.
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="page">
<h1>Progress Bar Animation CSS</h1>
<p class="subtitle">Smooth fills, shimmer, radial rings and step indicators</p>
<!-- 1. Standard -->
<section>
<h2>Skill / Label Bars</h2>
<button class="replay-btn" onclick="runBars()">↺ Replay</button>
<div class="bar-list" id="barList">
<div class="bar-row"><div class="bar-meta"><span class="bar-label">TypeScript</span><span class="bar-pct" id="pct0">0%</span></div><div class="bar-track"><div class="bar-fill bf-blue" data-to="92"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">React</span><span class="bar-pct" id="pct1">0%</span></div><div class="bar-track"><div class="bar-fill bf-blue" data-to="88"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Node.js</span><span class="bar-pct" id="pct2">0%</span></div><div class="bar-track"><div class="bar-fill bf-green" data-to="79"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">PostgreSQL</span><span class="bar-pct" id="pct3">0%</span></div><div class="bar-track"><div class="bar-fill bf-purple" data-to="70"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Rust</span><span class="bar-pct" id="pct4">0%</span></div><div class="bar-track"><div class="bar-fill bf-orange" data-to="45"></div></div></div>
</div>
</section>
<!-- 2. Shimmer / stripe -->
<section>
<h2>Shimmer & Striped Variants</h2>
<div class="bar-list" id="barList2">
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Upload progress</span><span class="bar-pct" id="pct5">0%</span></div><div class="bar-track" style="height:12px"><div class="bar-fill bf-blue shimmer" data-to="67"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Installing packages</span><span class="bar-pct" id="pct6">0%</span></div><div class="bar-track" style="height:12px;background:#1e3a2f"><div class="bar-fill bf-green striped" data-to="55"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Syncing data</span><span class="bar-pct" id="pct7">0%</span></div><div class="bar-track" style="height:12px"><div class="bar-fill bf-purple" data-to="83"></div></div></div>
</div>
</section>
<!-- 3. Thin loaders -->
<section>
<h2>Page / Loading Indicators</h2>
<div class="loader-box">
<div class="loader-label">Determinate — fetching data</div>
<div class="loader-track"><div class="loader-fill pulse-glow" id="thinLoader"></div></div>
</div>
<div class="loader-box">
<div class="loader-label">Indeterminate — waiting for server</div>
<div class="loader-track"><div class="loader-fill indeterminate"></div></div>
</div>
</section>
<!-- 4. Circular -->
<section>
<h2>Circular Progress Rings</h2>
<button class="replay-btn" onclick="runCircles()">↺ Replay</button>
<div class="circle-grid" id="circleGrid">
<svg width="0" height="0"><defs><linearGradient id="grad-blue" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#1d6fe6"/><stop offset="100%" stop-color="#58a6ff"/></linearGradient></defs></svg>
</div>
</section>
<!-- 5. Steps -->
<section>
<h2>Step Indicators</h2>
<button class="replay-btn" onclick="runSteps()">↺ Replay</button>
<div id="stepWrap">
<div style="margin-bottom:16px">
<div class="bar-meta" style="margin-bottom:8px"><span class="bar-label">Order progress</span><span class="bar-pct" id="stepLabel">Step 3 of 5</span></div>
<div class="step-track" id="stepTrack"></div>
<div class="step-labels"><span>Placed</span><span>Confirmed</span><span>Packed</span><span>Shipped</span><span>Delivered</span></div>
</div>
</div>
</section>
</div> <div class="page">
<h1>Progress Bar Animation CSS</h1>
<p class="subtitle">Smooth fills, shimmer, radial rings and step indicators</p>
<!-- 1. Standard -->
<section>
<h2>Skill / Label Bars</h2>
<button class="replay-btn" onclick="runBars()">↺ Replay</button>
<div class="bar-list" id="barList">
<div class="bar-row"><div class="bar-meta"><span class="bar-label">TypeScript</span><span class="bar-pct" id="pct0">0%</span></div><div class="bar-track"><div class="bar-fill bf-blue" data-to="92"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">React</span><span class="bar-pct" id="pct1">0%</span></div><div class="bar-track"><div class="bar-fill bf-blue" data-to="88"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Node.js</span><span class="bar-pct" id="pct2">0%</span></div><div class="bar-track"><div class="bar-fill bf-green" data-to="79"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">PostgreSQL</span><span class="bar-pct" id="pct3">0%</span></div><div class="bar-track"><div class="bar-fill bf-purple" data-to="70"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Rust</span><span class="bar-pct" id="pct4">0%</span></div><div class="bar-track"><div class="bar-fill bf-orange" data-to="45"></div></div></div>
</div>
</section>
<!-- 2. Shimmer / stripe -->
<section>
<h2>Shimmer & Striped Variants</h2>
<div class="bar-list" id="barList2">
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Upload progress</span><span class="bar-pct" id="pct5">0%</span></div><div class="bar-track" style="height:12px"><div class="bar-fill bf-blue shimmer" data-to="67"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Installing packages</span><span class="bar-pct" id="pct6">0%</span></div><div class="bar-track" style="height:12px;background:#1e3a2f"><div class="bar-fill bf-green striped" data-to="55"></div></div></div>
<div class="bar-row"><div class="bar-meta"><span class="bar-label">Syncing data</span><span class="bar-pct" id="pct7">0%</span></div><div class="bar-track" style="height:12px"><div class="bar-fill bf-purple" data-to="83"></div></div></div>
</div>
</section>
<!-- 3. Thin loaders -->
<section>
<h2>Page / Loading Indicators</h2>
<div class="loader-box">
<div class="loader-label">Determinate — fetching data</div>
<div class="loader-track"><div class="loader-fill pulse-glow" id="thinLoader"></div></div>
</div>
<div class="loader-box">
<div class="loader-label">Indeterminate — waiting for server</div>
<div class="loader-track"><div class="loader-fill indeterminate"></div></div>
</div>
</section>
<!-- 4. Circular -->
<section>
<h2>Circular Progress Rings</h2>
<button class="replay-btn" onclick="runCircles()">↺ Replay</button>
<div class="circle-grid" id="circleGrid">
<svg width="0" height="0"><defs><linearGradient id="grad-blue" x1="0" y1="0" x2="1" y2="0"><stop offset="0%" stop-color="#1d6fe6"/><stop offset="100%" stop-color="#58a6ff"/></linearGradient></defs></svg>
</div>
</section>
<!-- 5. Steps -->
<section>
<h2>Step Indicators</h2>
<button class="replay-btn" onclick="runSteps()">↺ Replay</button>
<div id="stepWrap">
<div style="margin-bottom:16px">
<div class="bar-meta" style="margin-bottom:8px"><span class="bar-label">Order progress</span><span class="bar-pct" id="stepLabel">Step 3 of 5</span></div>
<div class="step-track" id="stepTrack"></div>
<div class="step-labels"><span>Placed</span><span>Confirmed</span><span>Packed</span><span>Shipped</span><span>Delivered</span></div>
</div>
</div>
</section>
</div>* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0d1117; color: #e6edf3; min-height: 100vh; }
.page { padding: 60px 40px; max-width: 900px; margin: 0 auto; }
h1 { font-size: 2rem; text-align: center; margin-bottom: 8px; color: #fff; }
.subtitle { color: #8b949e; text-align: center; margin-bottom: 50px; }
section { margin-bottom: 60px; }
section > h2 { color: #58a6ff; font-size: .75rem; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 20px; padding-bottom: 8px; border-bottom: 1px solid #21262d; }
.replay-btn {
display: inline-flex; gap: 6px; align-items: center; margin-bottom: 20px;
padding: 8px 18px; border-radius: 8px; border: 1px solid #30363d;
background: #161b22; color: #c9d1d9; font-size: .82rem; cursor: pointer;
transition: background .2s;
}
.replay-btn:hover { background: #21262d; }
/* ─────────────────────────────────────
1. Standard labelled bars
───────────────────────────────────── */
.bar-list { display: flex; flex-direction: column; gap: 18px; }
.bar-row { }
.bar-meta { display: flex; justify-content: space-between; margin-bottom: 8px; }
.bar-label { font-size: .875rem; color: #e6edf3; }
.bar-pct { font-size: .875rem; color: #8b949e; font-variant-numeric: tabular-nums; }
.bar-track {
height: 8px; border-radius: 4px;
background: #21262d; overflow: hidden;
}
.bar-fill {
height: 100%; border-radius: 4px; width: 0;
transition: width 1.2s cubic-bezier(.25,1,.5,1);
}
.bf-blue { background: linear-gradient(90deg, #1d6fe6, #58a6ff); }
.bf-green { background: linear-gradient(90deg, #1a7f37, #3fb950); }
.bf-purple { background: linear-gradient(90deg, #5e2d91, #a78bfa); }
.bf-orange { background: linear-gradient(90deg, #9a4f00, #f0883e); }
.bf-red { background: linear-gradient(90deg, #8a1f1f, #f85149); }
/* ─────────────────────────────────────
2. Striped / animated shimmer bars
───────────────────────────────────── */
.bar-fill.striped {
background-image: repeating-linear-gradient(
45deg,
rgba(255,255,255,.08) 0,
rgba(255,255,255,.08) 10px,
transparent 10px,
transparent 20px
);
background-size: 28px 100%;
animation: stripe-move 1s linear infinite;
}
@keyframes stripe-move {
from { background-position: 0 0; }
to { background-position: 28px 0; }
}
.bar-fill.shimmer::after {
content: ''; display: block; height: 100%;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.25) 50%, transparent 100%);
background-size: 60% 100%; background-repeat: no-repeat;
animation: shimmer-sweep 2s ease-in-out infinite;
}
@keyframes shimmer-sweep {
from { background-position: -60% 0; }
to { background-position: 160% 0; }
}
/* ─────────────────────────────────────
3. Thin loader / page-top bars
───────────────────────────────────── */
.loader-box { background: #161b22; border-radius: 12px; padding: 24px; margin-bottom: 12px; }
.loader-label { color: #8b949e; font-size: .8rem; margin-bottom: 12px; }
.loader-track { height: 3px; background: #21262d; border-radius: 2px; overflow: hidden; }
.loader-fill {
height: 100%; border-radius: 2px;
background: linear-gradient(90deg, #58a6ff, #a78bfa);
}
.loader-fill.indeterminate {
width: 40%; animation: indeterminate 1.8s ease-in-out infinite;
}
@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(350%); }
}
.loader-fill.pulse-glow {
width: 0; transition: width 2s ease;
box-shadow: 0 0 8px rgba(88,166,255,.7);
}
/* ─────────────────────────────────────
4. Circular / radial
───────────────────────────────────── */
.circle-grid { display: flex; flex-wrap: wrap; gap: 24px; }
.circle-wrap { text-align: center; }
.circle-svg { transform: rotate(-90deg); }
.circle-track { fill: none; stroke: #21262d; stroke-width: 6; }
.circle-arc {
fill: none; stroke-width: 6; stroke-linecap: round;
stroke-dashoffset: 283; /* full circumference = 2π×45 ≈ 283 */
transition: stroke-dashoffset 1.4s cubic-bezier(.25,1,.5,1);
}
.ca-blue { stroke: url(#grad-blue); }
.ca-green { stroke: #3fb950; }
.ca-purple { stroke: #a78bfa; }
.ca-orange { stroke: #f0883e; }
.circle-inner {
font-size: 1.1rem; font-weight: 700; color: #fff; margin-top: 8px;
}
.circle-sub { font-size: .75rem; color: #8b949e; }
/* ─────────────────────────────────────
5. Step / multi-segment
───────────────────────────────────── */
.step-track { display: flex; height: 10px; border-radius: 5px; overflow: hidden; gap: 2px; }
.step-seg { flex: 1; background: #21262d; border-radius: 3px; transform-origin: left; transform: scaleX(0); transition: transform .4s ease; }
.step-seg.filled { background: #3fb950; }
.step-seg.done { transform: scaleX(1); }
.step-labels { display: flex; justify-content: space-between; margin-top: 8px; }
.step-labels span { font-size: .72rem; color: #8b949e; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0d1117; color: #e6edf3; min-height: 100vh; }
.page { padding: 60px 40px; max-width: 900px; margin: 0 auto; }
h1 { font-size: 2rem; text-align: center; margin-bottom: 8px; color: #fff; }
.subtitle { color: #8b949e; text-align: center; margin-bottom: 50px; }
section { margin-bottom: 60px; }
section > h2 { color: #58a6ff; font-size: .75rem; text-transform: uppercase; letter-spacing: 3px; margin-bottom: 20px; padding-bottom: 8px; border-bottom: 1px solid #21262d; }
.replay-btn {
display: inline-flex; gap: 6px; align-items: center; margin-bottom: 20px;
padding: 8px 18px; border-radius: 8px; border: 1px solid #30363d;
background: #161b22; color: #c9d1d9; font-size: .82rem; cursor: pointer;
transition: background .2s;
}
.replay-btn:hover { background: #21262d; }
/* ─────────────────────────────────────
1. Standard labelled bars
───────────────────────────────────── */
.bar-list { display: flex; flex-direction: column; gap: 18px; }
.bar-row { }
.bar-meta { display: flex; justify-content: space-between; margin-bottom: 8px; }
.bar-label { font-size: .875rem; color: #e6edf3; }
.bar-pct { font-size: .875rem; color: #8b949e; font-variant-numeric: tabular-nums; }
.bar-track {
height: 8px; border-radius: 4px;
background: #21262d; overflow: hidden;
}
.bar-fill {
height: 100%; border-radius: 4px; width: 0;
transition: width 1.2s cubic-bezier(.25,1,.5,1);
}
.bf-blue { background: linear-gradient(90deg, #1d6fe6, #58a6ff); }
.bf-green { background: linear-gradient(90deg, #1a7f37, #3fb950); }
.bf-purple { background: linear-gradient(90deg, #5e2d91, #a78bfa); }
.bf-orange { background: linear-gradient(90deg, #9a4f00, #f0883e); }
.bf-red { background: linear-gradient(90deg, #8a1f1f, #f85149); }
/* ─────────────────────────────────────
2. Striped / animated shimmer bars
───────────────────────────────────── */
.bar-fill.striped {
background-image: repeating-linear-gradient(
45deg,
rgba(255,255,255,.08) 0,
rgba(255,255,255,.08) 10px,
transparent 10px,
transparent 20px
);
background-size: 28px 100%;
animation: stripe-move 1s linear infinite;
}
@keyframes stripe-move {
from { background-position: 0 0; }
to { background-position: 28px 0; }
}
.bar-fill.shimmer::after {
content: ''; display: block; height: 100%;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.25) 50%, transparent 100%);
background-size: 60% 100%; background-repeat: no-repeat;
animation: shimmer-sweep 2s ease-in-out infinite;
}
@keyframes shimmer-sweep {
from { background-position: -60% 0; }
to { background-position: 160% 0; }
}
/* ─────────────────────────────────────
3. Thin loader / page-top bars
───────────────────────────────────── */
.loader-box { background: #161b22; border-radius: 12px; padding: 24px; margin-bottom: 12px; }
.loader-label { color: #8b949e; font-size: .8rem; margin-bottom: 12px; }
.loader-track { height: 3px; background: #21262d; border-radius: 2px; overflow: hidden; }
.loader-fill {
height: 100%; border-radius: 2px;
background: linear-gradient(90deg, #58a6ff, #a78bfa);
}
.loader-fill.indeterminate {
width: 40%; animation: indeterminate 1.8s ease-in-out infinite;
}
@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(350%); }
}
.loader-fill.pulse-glow {
width: 0; transition: width 2s ease;
box-shadow: 0 0 8px rgba(88,166,255,.7);
}
/* ─────────────────────────────────────
4. Circular / radial
───────────────────────────────────── */
.circle-grid { display: flex; flex-wrap: wrap; gap: 24px; }
.circle-wrap { text-align: center; }
.circle-svg { transform: rotate(-90deg); }
.circle-track { fill: none; stroke: #21262d; stroke-width: 6; }
.circle-arc {
fill: none; stroke-width: 6; stroke-linecap: round;
stroke-dashoffset: 283; /* full circumference = 2π×45 ≈ 283 */
transition: stroke-dashoffset 1.4s cubic-bezier(.25,1,.5,1);
}
.ca-blue { stroke: url(#grad-blue); }
.ca-green { stroke: #3fb950; }
.ca-purple { stroke: #a78bfa; }
.ca-orange { stroke: #f0883e; }
.circle-inner {
font-size: 1.1rem; font-weight: 700; color: #fff; margin-top: 8px;
}
.circle-sub { font-size: .75rem; color: #8b949e; }
/* ─────────────────────────────────────
5. Step / multi-segment
───────────────────────────────────── */
.step-track { display: flex; height: 10px; border-radius: 5px; overflow: hidden; gap: 2px; }
.step-seg { flex: 1; background: #21262d; border-radius: 3px; transform-origin: left; transform: scaleX(0); transition: transform .4s ease; }
.step-seg.filled { background: #3fb950; }
.step-seg.done { transform: scaleX(1); }
.step-labels { display: flex; justify-content: space-between; margin-top: 8px; }
.step-labels span { font-size: .72rem; color: #8b949e; }/* ── Animate % counter ── */
function animatePct(el, to, dur = 1200) {
let start = null;
function step(ts) {
if (!start) start = ts;
const p = Math.min((ts - start) / dur, 1);
el.textContent = Math.round(p * to) + '%';
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
/* ── 1 & 2. Linear bars ── */
function runBars() {
['barList','barList2'].forEach(id => {
document.querySelectorAll(`#${id} .bar-fill`).forEach((fill, i) => {
fill.style.width = '0';
void fill.offsetWidth;
const to = fill.dataset.to;
setTimeout(() => {
fill.style.width = to + '%';
const pctEl = document.getElementById('pct' + (id === 'barList' ? i : i + 5));
if (pctEl) animatePct(pctEl, parseInt(to));
}, i * 150);
});
});
// Thin loader
const tl = document.getElementById('thinLoader');
tl.style.width = '0';
void tl.offsetWidth;
setTimeout(() => { tl.style.width = '78%'; }, 300);
}
/* ── 4. Circular ── */
const CIRCLES = [
{ pct: 87, color: 'ca-blue', label: 'Performance', stroke: 'url(#grad-blue)' },
{ pct: 73, color: 'ca-green', label: 'Accessibility', stroke: '#3fb950' },
{ pct: 94, color: 'ca-purple', label: 'Best Practice', stroke: '#a78bfa' },
{ pct: 61, color: 'ca-orange', label: 'SEO Score', stroke: '#f0883e' },
];
const C = 2 * Math.PI * 45; // ≈ 282.7
function buildCircles() {
const grid = document.getElementById('circleGrid');
// keep the defs svg
const defs = grid.querySelector('svg');
grid.innerHTML = '';
grid.appendChild(defs);
CIRCLES.forEach(d => {
const wrap = document.createElement('div');
wrap.className = 'circle-wrap';
wrap.innerHTML = `
<svg class="circle-svg" width="100" height="100" viewBox="0 0 100 100">
<circle class="circle-track" cx="50" cy="50" r="45"/>
<circle class="circle-arc ${d.color}" cx="50" cy="50" r="45"
stroke-dasharray="${C}" stroke-dashoffset="${C}"/>
</svg>
<div class="circle-inner">0%</div>
<div class="circle-sub">${d.label}</div>`;
grid.appendChild(wrap);
});
}
function runCircles() {
buildCircles();
CIRCLES.forEach((d, i) => {
const arcs = document.querySelectorAll('.circle-arc');
const nums = document.querySelectorAll('.circle-inner');
setTimeout(() => {
arcs[i].style.strokeDashoffset = C * (1 - d.pct / 100);
animatePct(nums[i], d.pct, 1400);
}, i * 180);
});
}
/* ── 5. Steps ── */
const TOTAL_STEPS = 5, FILLED = 3;
function buildSteps() {
const track = document.getElementById('stepTrack');
track.innerHTML = '';
for (let i = 0; i < TOTAL_STEPS; i++) {
const seg = document.createElement('div');
seg.className = 'step-seg' + (i < FILLED ? ' filled' : '');
track.appendChild(seg);
}
}
function runSteps() {
buildSteps();
document.querySelectorAll('.step-seg').forEach((seg, i) => {
setTimeout(() => seg.classList.add('done'), i * 150 + 100);
});
}
// Init
setTimeout(runBars, 300);
setTimeout(runCircles, 500);
setTimeout(runSteps, 700); /* ── Animate % counter ── */
function animatePct(el, to, dur = 1200) {
let start = null;
function step(ts) {
if (!start) start = ts;
const p = Math.min((ts - start) / dur, 1);
el.textContent = Math.round(p * to) + '%';
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
/* ── 1 & 2. Linear bars ── */
function runBars() {
['barList','barList2'].forEach(id => {
document.querySelectorAll(`#${id} .bar-fill`).forEach((fill, i) => {
fill.style.width = '0';
void fill.offsetWidth;
const to = fill.dataset.to;
setTimeout(() => {
fill.style.width = to + '%';
const pctEl = document.getElementById('pct' + (id === 'barList' ? i : i + 5));
if (pctEl) animatePct(pctEl, parseInt(to));
}, i * 150);
});
});
// Thin loader
const tl = document.getElementById('thinLoader');
tl.style.width = '0';
void tl.offsetWidth;
setTimeout(() => { tl.style.width = '78%'; }, 300);
}
/* ── 4. Circular ── */
const CIRCLES = [
{ pct: 87, color: 'ca-blue', label: 'Performance', stroke: 'url(#grad-blue)' },
{ pct: 73, color: 'ca-green', label: 'Accessibility', stroke: '#3fb950' },
{ pct: 94, color: 'ca-purple', label: 'Best Practice', stroke: '#a78bfa' },
{ pct: 61, color: 'ca-orange', label: 'SEO Score', stroke: '#f0883e' },
];
const C = 2 * Math.PI * 45; // ≈ 282.7
function buildCircles() {
const grid = document.getElementById('circleGrid');
// keep the defs svg
const defs = grid.querySelector('svg');
grid.innerHTML = '';
grid.appendChild(defs);
CIRCLES.forEach(d => {
const wrap = document.createElement('div');
wrap.className = 'circle-wrap';
wrap.innerHTML = `
<svg class="circle-svg" width="100" height="100" viewBox="0 0 100 100">
<circle class="circle-track" cx="50" cy="50" r="45"/>
<circle class="circle-arc ${d.color}" cx="50" cy="50" r="45"
stroke-dasharray="${C}" stroke-dashoffset="${C}"/>
</svg>
<div class="circle-inner">0%</div>
<div class="circle-sub">${d.label}</div>`;
grid.appendChild(wrap);
});
}
function runCircles() {
buildCircles();
CIRCLES.forEach((d, i) => {
const arcs = document.querySelectorAll('.circle-arc');
const nums = document.querySelectorAll('.circle-inner');
setTimeout(() => {
arcs[i].style.strokeDashoffset = C * (1 - d.pct / 100);
animatePct(nums[i], d.pct, 1400);
}, i * 180);
});
}
/* ── 5. Steps ── */
const TOTAL_STEPS = 5, FILLED = 3;
function buildSteps() {
const track = document.getElementById('stepTrack');
track.innerHTML = '';
for (let i = 0; i < TOTAL_STEPS; i++) {
const seg = document.createElement('div');
seg.className = 'step-seg' + (i < FILLED ? ' filled' : '');
track.appendChild(seg);
}
}
function runSteps() {
buildSteps();
document.querySelectorAll('.step-seg').forEach((seg, i) => {
setTimeout(() => seg.classList.add('done'), i * 150 + 100);
});
}
// Init
setTimeout(runBars, 300);
setTimeout(runCircles, 500);
setTimeout(runSteps, 700);More from 22 CSS Transition Effects
Slide-In Animation on ScrollFade In Fade Out TransitionLoading Skeleton TransitionModal Open Close TransitionPage Transition EffectRipple Effect on ClickNumber Counter AnimationCard Tilt 3D HoverStaggered List AnimationCursor Trail EffectMagnetic Button EffectSplit Text Reveal Transition
View the full collection →