More Loaders Snippets
Gradient Progress Bars — Animated HTML CSS JS Snippet
Gradient Progress Bars · Loaders · Plain HTML, CSS & JS · Live preview
What's included
Features
background-size: 200% 100% + background-position keyframe animation creates a continuous shimmer sweep without JavaScript.repeating-linear-gradient(45deg, ...) layered over a gradient base, animated with background-position — creates diagonal moving stripe pattern.--p custom property on each bar drives width: var(--p) — set fill amount with a single inline style attribute..done, .active, and default states. Active segment pulses with an opacity keyframe animation.width: 0% on init, animated to data-p + '%' via a 200ms-delayed setTimeout — ensures visible animation after first paint.adjustBar(delta) clamps value to 0–100 and updates both the CSS custom property and label text simultaneously.cubic-bezier(.4,0,.2,1) easing on width — smooth deceleration that feels physical rather than linear.About this UI Snippet
Gradient Progress Bars — CSS Shimmer, Stripe Animation, Segmented Steps & Skill Bars

Progress bars are among the most versatile UI components — used for file uploads, page load indicators, onboarding completion, skill showcases, storage usage displays, and interactive data visualisations. This snippet delivers five distinct progress bar styles in a single cohesive component: animated gradient shimmer bars, moving stripe bars, segmented step indicators, animating-in skill bars, and an interactive bar with +/- controls.
Getting progress bars right requires solving several CSS challenges simultaneously: the fill width must animate smoothly, the gradient must feel alive (not static), stripes must animate without background-position glitches, and the on-load animation must feel purposeful rather than mechanical.
Animated gradient shimmer
The shimmer effect uses a wider-than-container gradient: background: linear-gradient(90deg, #6366f1, #8b5cf6, #6366f1) with background-size: 200% 100%. The @keyframes shimmer animates background-position from 200% 0 to -200% 0 over 2 seconds — this creates a continuous sweep of lighter purple across the bar. The background-size: 200% means the gradient repeats: as the right edge exits, the left edge enters, creating a seamless loop.
The fill width is driven by a --p CSS custom property set inline on each bar: style="--p:72%". The width: var(--p, 0%) in CSS reads this property. Animating from 0% to the target width is handled by transition: width .6s cubic-bezier(.4,0,.2,1) — the same Material Design easing used for panel motions.
Striped moving bars
Striped bars layer two backgrounds: a repeating diagonal stripe pattern using repeating-linear-gradient(45deg, ...) over a solid gradient base. The stripe animation uses background-position increments — moving by one stripe-width (24px) per second creates the illusion of movement. The key implementation detail is setting background-position as the animation property (not background-image) since position-only animation avoids repaints.
Segmented step indicator
The step bar uses a flex row of equal-width <div class="seg"> elements with 4px gaps. Completed steps get .done (solid indigo), the active step gets .active (gradient with a pulsing opacity animation at 1.4s ease-in-out), and future steps remain light grey. This pattern is used in multi-step forms and onboarding flows.
Skill bars with load animation
Skill bars start at width: 0% and animate to their target via a setTimeout on DOMContentLoaded — the 200ms delay ensures the page has painted before the animation runs, making it feel like a triggered reveal rather than a flash. The data-p attribute stores the target percentage, read by JS and set as bar.style.width.
Step by step
How to Use
- 1Paste HTML, CSS, and JSFive grouped sections appear: gradient shimmer bars, striped animated bars, segmented steps, skill bars, and an interactive bar with controls.
- 2Watch the animationsGradient bars shimmer continuously. Striped bars have diagonal lines moving right-to-left. Skill bars animate in from 0% to their target width on load.
- 3Use the +/- buttonsThe interactive bar section adjusts "Project completion" in 5% or 10% steps. The bar animates smoothly between values.
- 4Set progress valuesChange
style="--p:72%"on any.prog-barto set its fill width. Values from 0% to 100% work directly. - 5Change the colour variantSwap
bar-indigo,bar-cyan,bar-red,bar-green, orbar-purpleon any.prog-barto change the gradient colour scheme. - 6Update the segmented stepsAdd or remove
<div class="seg">elements. Apply.doneto completed steps and.activeto the current step. The flex layout adjusts automatically.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Use an IntersectionObserver on each .prog-bar. When the bar enters the viewport, set bar.style.setProperty('--p', targetWidth). Start with --p: 0% in CSS so bars don't animate before they're visible. See the reveal on scroll snippet for the observer pattern.
Make the bar position: relative and add a <span class="label"> child with position: absolute; right: 6px; top: 50%; transform: translateY(-50%). Ensure the bar height is at least 18px. Hide the label when --p < 15% to avoid overflow.
Call bar.style.setProperty('--p', value + '%') to update the fill width. For upload progress: read event.loaded / event.total * 100 from an XHR or Fetch progress event and call setProperty on each progress update.
Create a ProgressBar component with {label, value, variant, striped} props. value drives style={{['--p']: value + '%'}}. variant applies the colour class. striped conditionally adds the stripe class. The skill bar animation uses useEffect with a setTimeout(() => setWidth(targetValue), 200).
Gradient Progress Bars — Export as HTML, React, Vue, Angular & Tailwind
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Gradient Progress Bars</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px}
.page{max-width:580px;margin:0 auto;display:flex;flex-direction:column;gap:28px}
.group{background:#fff;border:1.5px solid #e2e8f0;border-radius:16px;padding:20px 22px;display:flex;flex-direction:column;gap:16px}
.group-title{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin-bottom:2px}
.prog-wrap{display:flex;flex-direction:column;gap:6px}
.prog-header{display:flex;justify-content:space-between;align-items:center}
.prog-label{font-size:13px;font-weight:600;color:#334155}
.prog-val{font-size:12px;font-weight:700;color:#64748b}
.prog-track{height:8px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.prog-bar{height:100%;border-radius:20px;width:var(--p,0%);transition:width .6s cubic-bezier(.4,0,.2,1);background-size:200% 100%;animation:shimmer 2s linear infinite}
.bar-indigo{background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)}
.bar-cyan{background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)}
.bar-red{background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)}
.bar-green{background:linear-gradient(90deg,#10b981,#059669,#10b981)}
.bar-purple{background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.bar-stripe{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#10b981,#059669)}
.bar-stripe.bar-purple{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite}
.bar-stripe{animation:stripe-move 1s linear infinite}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
.seg-track{display:flex;gap:4px}
.seg{flex:1;height:6px;border-radius:4px;background:#e2e8f0;transition:background .3s}
.seg.done{background:#6366f1}
.seg.active{background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
.skills{display:flex;flex-direction:column;gap:10px}
.skill-row{display:grid;grid-template-columns:100px 1fr 36px;align-items:center;gap:10px}
.skill-name{font-size:12px;font-weight:600;color:#334155}
.skill-track{height:7px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.skill-bar{height:100%;border-radius:20px;background:linear-gradient(90deg,#6366f1,#8b5cf6);width:0%;transition:width .8s cubic-bezier(.4,0,.2,1)}
.skill-pct{font-size:11px;font-weight:700;color:#6366f1;text-align:right}
.btn-row{display:flex;gap:8px;flex-wrap:wrap}
.ctrl-btn{padding:6px 14px;border-radius:8px;border:1.5px solid #e2e8f0;background:#fff;color:#475569;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}
.ctrl-btn:hover{border-color:#c7d2fe;color:#6366f1}
.ctrl-btn.primary{background:#6366f1;border-color:#6366f1;color:#fff}
.ctrl-btn.primary:hover{background:#4f46e5}
</style>
</head>
<body>
<div class="page">
<section class="group">
<h3 class="group-title">Animated Gradient</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Storage Used</span><span class="prog-val">72%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" style="--p:72%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">CPU Usage</span><span class="prog-val">48%</span></div>
<div class="prog-track"><div class="prog-bar bar-cyan" style="--p:48%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Memory</span><span class="prog-val">89%</span></div>
<div class="prog-track"><div class="prog-bar bar-red" style="--p:89%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Striped Animated</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Uploading files…</span><span class="prog-val">63%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-green" style="--p:63%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Syncing database</span><span class="prog-val">31%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-purple" style="--p:31%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Segmented / Steps</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Onboarding progress</span><span class="prog-val">Step 3 of 5</span></div>
<div class="seg-track">
<div class="seg done"></div>
<div class="seg done"></div>
<div class="seg active"></div>
<div class="seg"></div>
<div class="seg"></div>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Skill Bars</h3>
<div class="skills">
<div class="skill-row">
<span class="skill-name">React</span>
<div class="skill-track"><div class="skill-bar" data-p="90"></div></div>
<span class="skill-pct">90%</span>
</div>
<div class="skill-row">
<span class="skill-name">TypeScript</span>
<div class="skill-track"><div class="skill-bar" data-p="78"></div></div>
<span class="skill-pct">78%</span>
</div>
<div class="skill-row">
<span class="skill-name">Node.js</span>
<div class="skill-track"><div class="skill-bar" data-p="65"></div></div>
<span class="skill-pct">65%</span>
</div>
<div class="skill-row">
<span class="skill-name">CSS / Design</span>
<div class="skill-track"><div class="skill-bar" data-p="85"></div></div>
<span class="skill-pct">85%</span>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Interactive — Click to update</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Project completion</span><span class="prog-val" id="liveVal">55%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" id="liveBar" style="--p:55%"></div></div>
</div>
<div class="btn-row">
<button class="ctrl-btn" onclick="adjustBar(-10)">−10%</button>
<button class="ctrl-btn" onclick="adjustBar(-5)">−5%</button>
<button class="ctrl-btn primary" onclick="adjustBar(5)">+5%</button>
<button class="ctrl-btn primary" onclick="adjustBar(10)">+10%</button>
</div>
</section>
</div>
<script>
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
</script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Gradient Progress Bars</title>
<!-- Tailwind CSS v3+ — arbitrary value classes (e.g. bg-[#0f172a]) are valid -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px
}
.bar-stripe.bar-purple {
background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite
}
.seg.done {
background:#6366f1
}
.seg.active {
background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite
}
.ctrl-btn.primary {
background:#6366f1;border-color:#6366f1;color:#fff
}
.ctrl-btn.primary:hover {
background:#4f46e5
}
</style>
</head>
<body>
<div class="max-w-[580px] my-0 mx-auto flex flex-col gap-7">
<section class="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 class="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Animated Gradient</h3>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Storage Used</span><span class="text-xs font-bold text-[#64748b]">72%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)]" style="--p:72%"></div></div>
</div>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">CPU Usage</span><span class="text-xs font-bold text-[#64748b]">48%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)]" style="--p:48%"></div></div>
</div>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Memory</span><span class="text-xs font-bold text-[#64748b]">89%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)]" style="--p:89%"></div></div>
</div>
</section>
<section class="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 class="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Striped Animated</h3>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Uploading files…</span><span class="text-xs font-bold text-[#64748b]">63%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] bar-stripe [background-image:repeating-linear-gradient(
__45deg,
__transparent,transparent_6px,
__rgba(255,255,255,.18)_6px,rgba(255,255,255,.18)_12px
),linear-gradient(90deg,#10b981,#059669)] [animation:stripe-move_1s_linear_infinite] [background:linear-gradient(90deg,#10b981,#059669,#10b981)]" style="--p:63%"></div></div>
</div>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Syncing database</span><span class="text-xs font-bold text-[#64748b]">31%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] bar-stripe [background-image:repeating-linear-gradient(
__45deg,
__transparent,transparent_6px,
__rgba(255,255,255,.18)_6px,rgba(255,255,255,.18)_12px
),linear-gradient(90deg,#10b981,#059669)] [animation:stripe-move_1s_linear_infinite] bar-purple [background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)]" style="--p:31%"></div></div>
</div>
</section>
<section class="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 class="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Segmented / Steps</h3>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Onboarding progress</span><span class="text-xs font-bold text-[#64748b]">Step 3 of 5</span></div>
<div class="flex gap-1">
<div class="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] done"></div>
<div class="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] done"></div>
<div class="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] active"></div>
<div class="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s]"></div>
<div class="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s]"></div>
</div>
</div>
</section>
<section class="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 class="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Skill Bars</h3>
<div class="flex flex-col gap-2.5">
<div class="grid grid-cols-1 items-center gap-2.5">
<span class="text-xs font-semibold text-[#334155]">React</span>
<div class="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="90"></div></div>
<span class="text-[11px] font-bold text-[#6366f1] text-right">90%</span>
</div>
<div class="grid grid-cols-1 items-center gap-2.5">
<span class="text-xs font-semibold text-[#334155]">TypeScript</span>
<div class="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="78"></div></div>
<span class="text-[11px] font-bold text-[#6366f1] text-right">78%</span>
</div>
<div class="grid grid-cols-1 items-center gap-2.5">
<span class="text-xs font-semibold text-[#334155]">Node.js</span>
<div class="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="65"></div></div>
<span class="text-[11px] font-bold text-[#6366f1] text-right">65%</span>
</div>
<div class="grid grid-cols-1 items-center gap-2.5">
<span class="text-xs font-semibold text-[#334155]">CSS / Design</span>
<div class="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="85"></div></div>
<span class="text-[11px] font-bold text-[#6366f1] text-right">85%</span>
</div>
</div>
</section>
<section class="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 class="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Interactive — Click to update</h3>
<div class="flex flex-col gap-1.5">
<div class="flex justify-between items-center"><span class="text-[13px] font-semibold text-[#334155]">Project completion</span><span class="text-xs font-bold text-[#64748b]" id="liveVal">55%</span></div>
<div class="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div class="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)]" id="liveBar" style="--p:55%"></div></div>
</div>
<div class="flex gap-2 flex-wrap">
<button class="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1]" onclick="adjustBar(-10)">−10%</button>
<button class="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1]" onclick="adjustBar(-5)">−5%</button>
<button class="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1] primary" onclick="adjustBar(5)">+5%</button>
<button class="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1] primary" onclick="adjustBar(10)">+10%</button>
</div>
</section>
</div>
<script>
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
</script>
</body>
</html>import React, { useEffect } from 'react';
// CSS — optionally move to GradientProgressBars.module.css
const css = `
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px}
.page{max-width:580px;margin:0 auto;display:flex;flex-direction:column;gap:28px}
.group{background:#fff;border:1.5px solid #e2e8f0;border-radius:16px;padding:20px 22px;display:flex;flex-direction:column;gap:16px}
.group-title{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin-bottom:2px}
.prog-wrap{display:flex;flex-direction:column;gap:6px}
.prog-header{display:flex;justify-content:space-between;align-items:center}
.prog-label{font-size:13px;font-weight:600;color:#334155}
.prog-val{font-size:12px;font-weight:700;color:#64748b}
.prog-track{height:8px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.prog-bar{height:100%;border-radius:20px;width:var(--p,0%);transition:width .6s cubic-bezier(.4,0,.2,1);background-size:200% 100%;animation:shimmer 2s linear infinite}
.bar-indigo{background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)}
.bar-cyan{background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)}
.bar-red{background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)}
.bar-green{background:linear-gradient(90deg,#10b981,#059669,#10b981)}
.bar-purple{background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.bar-stripe{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#10b981,#059669)}
.bar-stripe.bar-purple{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite}
.bar-stripe{animation:stripe-move 1s linear infinite}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
.seg-track{display:flex;gap:4px}
.seg{flex:1;height:6px;border-radius:4px;background:#e2e8f0;transition:background .3s}
.seg.done{background:#6366f1}
.seg.active{background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
.skills{display:flex;flex-direction:column;gap:10px}
.skill-row{display:grid;grid-template-columns:100px 1fr 36px;align-items:center;gap:10px}
.skill-name{font-size:12px;font-weight:600;color:#334155}
.skill-track{height:7px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.skill-bar{height:100%;border-radius:20px;background:linear-gradient(90deg,#6366f1,#8b5cf6);width:0%;transition:width .8s cubic-bezier(.4,0,.2,1)}
.skill-pct{font-size:11px;font-weight:700;color:#6366f1;text-align:right}
.btn-row{display:flex;gap:8px;flex-wrap:wrap}
.ctrl-btn{padding:6px 14px;border-radius:8px;border:1.5px solid #e2e8f0;background:#fff;color:#475569;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}
.ctrl-btn:hover{border-color:#c7d2fe;color:#6366f1}
.ctrl-btn.primary{background:#6366f1;border-color:#6366f1;color:#fff}
.ctrl-btn.primary:hover{background:#4f46e5}
`;
export default function GradientProgressBars() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
// Expose handlers used by inline JSX events to the global scope
if (typeof adjustBar === 'function') window.adjustBar = adjustBar;
}, []);
return (
<>
<style>{css}</style>
<div className="page">
<section className="group">
<h3 className="group-title">Animated Gradient</h3>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Storage Used</span><span className="prog-val">72%</span></div>
<div className="prog-track"><div className="prog-bar bar-indigo" style={{ '--p': '72%' }}></div></div>
</div>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">CPU Usage</span><span className="prog-val">48%</span></div>
<div className="prog-track"><div className="prog-bar bar-cyan" style={{ '--p': '48%' }}></div></div>
</div>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Memory</span><span className="prog-val">89%</span></div>
<div className="prog-track"><div className="prog-bar bar-red" style={{ '--p': '89%' }}></div></div>
</div>
</section>
<section className="group">
<h3 className="group-title">Striped Animated</h3>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Uploading files…</span><span className="prog-val">63%</span></div>
<div className="prog-track"><div className="prog-bar bar-stripe bar-green" style={{ '--p': '63%' }}></div></div>
</div>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Syncing database</span><span className="prog-val">31%</span></div>
<div className="prog-track"><div className="prog-bar bar-stripe bar-purple" style={{ '--p': '31%' }}></div></div>
</div>
</section>
<section className="group">
<h3 className="group-title">Segmented / Steps</h3>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Onboarding progress</span><span className="prog-val">Step 3 of 5</span></div>
<div className="seg-track">
<div className="seg done"></div>
<div className="seg done"></div>
<div className="seg active"></div>
<div className="seg"></div>
<div className="seg"></div>
</div>
</div>
</section>
<section className="group">
<h3 className="group-title">Skill Bars</h3>
<div className="skills">
<div className="skill-row">
<span className="skill-name">React</span>
<div className="skill-track"><div className="skill-bar" data-p="90"></div></div>
<span className="skill-pct">90%</span>
</div>
<div className="skill-row">
<span className="skill-name">TypeScript</span>
<div className="skill-track"><div className="skill-bar" data-p="78"></div></div>
<span className="skill-pct">78%</span>
</div>
<div className="skill-row">
<span className="skill-name">Node.js</span>
<div className="skill-track"><div className="skill-bar" data-p="65"></div></div>
<span className="skill-pct">65%</span>
</div>
<div className="skill-row">
<span className="skill-name">CSS / Design</span>
<div className="skill-track"><div className="skill-bar" data-p="85"></div></div>
<span className="skill-pct">85%</span>
</div>
</div>
</section>
<section className="group">
<h3 className="group-title">Interactive — Click to update</h3>
<div className="prog-wrap">
<div className="prog-header"><span className="prog-label">Project completion</span><span className="prog-val" id="liveVal">55%</span></div>
<div className="prog-track"><div className="prog-bar bar-indigo" id="liveBar" style={{ '--p': '55%' }}></div></div>
</div>
<div className="btn-row">
<button className="ctrl-btn" onClick={(event) => { adjustBar(-10) }}>−10%</button>
<button className="ctrl-btn" onClick={(event) => { adjustBar(-5) }}>−5%</button>
<button className="ctrl-btn primary" onClick={(event) => { adjustBar(5) }}>+5%</button>
<button className="ctrl-btn primary" onClick={(event) => { adjustBar(10) }}>+10%</button>
</div>
</section>
</div>
</>
);
}import React, { useEffect } from 'react';
// Requires Tailwind CSS v3+ — https://tailwindcss.com/docs/installation
// Arbitrary value classes (e.g. bg-[#0f172a]) are valid Tailwind v3+
export default function GradientProgressBars() {
// Auto-generated escape hatch: the original snippet's vanilla JS runs once
// after mount and queries the rendered DOM. For idiomatic React, lift this
// into state + handlers.
useEffect(() => {
const _listeners = [];
const _originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
_listeners.push({ target: this, type, listener, options });
return _originalAddEventListener.call(this, type, listener, options);
};
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
// Cleanup: restore addEventListener and remove all listeners
return () => {
EventTarget.prototype.addEventListener = _originalAddEventListener;
for (const { target, type, listener, options } of _listeners) {
target.removeEventListener(type, listener, options);
}
};
// Expose handlers used by inline JSX events to the global scope
if (typeof adjustBar === 'function') window.adjustBar = adjustBar;
}, []);
return (
<>
<style>{`
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
* {
box-sizing:border-box;margin:0;padding:0
}
body {
font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px
}
.bar-stripe.bar-purple {
background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite
}
.seg.done {
background:#6366f1
}
.seg.active {
background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite
}
.ctrl-btn.primary {
background:#6366f1;border-color:#6366f1;color:#fff
}
.ctrl-btn.primary:hover {
background:#4f46e5
}
`}</style>
<div className="max-w-[580px] my-0 mx-auto flex flex-col gap-7">
<section className="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 className="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Animated Gradient</h3>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Storage Used</span><span className="text-xs font-bold text-[#64748b]">72%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)]" style={{ '--p': '72%' }}></div></div>
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">CPU Usage</span><span className="text-xs font-bold text-[#64748b]">48%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)]" style={{ '--p': '48%' }}></div></div>
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Memory</span><span className="text-xs font-bold text-[#64748b]">89%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)]" style={{ '--p': '89%' }}></div></div>
</div>
</section>
<section className="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 className="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Striped Animated</h3>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Uploading files…</span><span className="text-xs font-bold text-[#64748b]">63%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] bar-stripe [background-image:repeating-linear-gradient(
__45deg,
__transparent,transparent_6px,
__rgba(255,255,255,.18)_6px,rgba(255,255,255,.18)_12px
),linear-gradient(90deg,#10b981,#059669)] [animation:stripe-move_1s_linear_infinite] [background:linear-gradient(90deg,#10b981,#059669,#10b981)]" style={{ '--p': '63%' }}></div></div>
</div>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Syncing database</span><span className="text-xs font-bold text-[#64748b]">31%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] bar-stripe [background-image:repeating-linear-gradient(
__45deg,
__transparent,transparent_6px,
__rgba(255,255,255,.18)_6px,rgba(255,255,255,.18)_12px
),linear-gradient(90deg,#10b981,#059669)] [animation:stripe-move_1s_linear_infinite] bar-purple [background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)]" style={{ '--p': '31%' }}></div></div>
</div>
</section>
<section className="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 className="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Segmented / Steps</h3>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Onboarding progress</span><span className="text-xs font-bold text-[#64748b]">Step 3 of 5</span></div>
<div className="flex gap-1">
<div className="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] done"></div>
<div className="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] done"></div>
<div className="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s] active"></div>
<div className="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s]"></div>
<div className="seg flex-1 h-1.5 rounded bg-[#e2e8f0] [transition:background_.3s]"></div>
</div>
</div>
</section>
<section className="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 className="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Skill Bars</h3>
<div className="flex flex-col gap-2.5">
<div className="grid grid-cols-1 items-center gap-2.5">
<span className="text-xs font-semibold text-[#334155]">React</span>
<div className="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="90"></div></div>
<span className="text-[11px] font-bold text-[#6366f1] text-right">90%</span>
</div>
<div className="grid grid-cols-1 items-center gap-2.5">
<span className="text-xs font-semibold text-[#334155]">TypeScript</span>
<div className="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="78"></div></div>
<span className="text-[11px] font-bold text-[#6366f1] text-right">78%</span>
</div>
<div className="grid grid-cols-1 items-center gap-2.5">
<span className="text-xs font-semibold text-[#334155]">Node.js</span>
<div className="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="65"></div></div>
<span className="text-[11px] font-bold text-[#6366f1] text-right">65%</span>
</div>
<div className="grid grid-cols-1 items-center gap-2.5">
<span className="text-xs font-semibold text-[#334155]">CSS / Design</span>
<div className="h-[7px] bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="skill-bar h-full rounded-[20px] [background:linear-gradient(90deg,#6366f1,#8b5cf6)] w-0 [transition:width_.8s_cubic-bezier(.4,0,.2,1)]" data-p="85"></div></div>
<span className="text-[11px] font-bold text-[#6366f1] text-right">85%</span>
</div>
</div>
</section>
<section className="bg-[#fff] border rounded-2xl py-5 px-[22px] flex flex-col gap-4">
<h3 className="text-xs font-bold tracking-[.06em] uppercase text-[#94a3b8] mb-0.5">Interactive — Click to update</h3>
<div className="flex flex-col gap-1.5">
<div className="flex justify-between items-center"><span className="text-[13px] font-semibold text-[#334155]">Project completion</span><span className="text-xs font-bold text-[#64748b]" id="liveVal">55%</span></div>
<div className="h-2 bg-[#f1f5f9] rounded-[20px] overflow-hidden"><div className="h-full rounded-[20px] w-[var(--p,0%)] [transition:width_.6s_cubic-bezier(.4,0,.2,1)] bg-[size:200%_100%] [animation:shimmer_2s_linear_infinite] [background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)]" id="liveBar" style={{ '--p': '55%' }}></div></div>
</div>
<div className="flex gap-2 flex-wrap">
<button className="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1]" onClick={(event) => { adjustBar(-10) }}>−10%</button>
<button className="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1]" onClick={(event) => { adjustBar(-5) }}>−5%</button>
<button className="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1] primary" onClick={(event) => { adjustBar(5) }}>+5%</button>
<button className="ctrl-btn py-1.5 px-3.5 rounded-lg border bg-[#fff] text-[#475569] text-xs font-semibold cursor-pointer [transition:all_.15s] font-[inherit] hover:border-[#c7d2fe] hover:text-[#6366f1] primary" onClick={(event) => { adjustBar(10) }}>+10%</button>
</div>
</section>
</div>
</>
);
}<template>
<div class="page">
<section class="group">
<h3 class="group-title">Animated Gradient</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Storage Used</span><span class="prog-val">72%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" style="--p:72%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">CPU Usage</span><span class="prog-val">48%</span></div>
<div class="prog-track"><div class="prog-bar bar-cyan" style="--p:48%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Memory</span><span class="prog-val">89%</span></div>
<div class="prog-track"><div class="prog-bar bar-red" style="--p:89%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Striped Animated</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Uploading files…</span><span class="prog-val">63%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-green" style="--p:63%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Syncing database</span><span class="prog-val">31%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-purple" style="--p:31%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Segmented / Steps</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Onboarding progress</span><span class="prog-val">Step 3 of 5</span></div>
<div class="seg-track">
<div class="seg done"></div>
<div class="seg done"></div>
<div class="seg active"></div>
<div class="seg"></div>
<div class="seg"></div>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Skill Bars</h3>
<div class="skills">
<div class="skill-row">
<span class="skill-name">React</span>
<div class="skill-track"><div class="skill-bar" data-p="90"></div></div>
<span class="skill-pct">90%</span>
</div>
<div class="skill-row">
<span class="skill-name">TypeScript</span>
<div class="skill-track"><div class="skill-bar" data-p="78"></div></div>
<span class="skill-pct">78%</span>
</div>
<div class="skill-row">
<span class="skill-name">Node.js</span>
<div class="skill-track"><div class="skill-bar" data-p="65"></div></div>
<span class="skill-pct">65%</span>
</div>
<div class="skill-row">
<span class="skill-name">CSS / Design</span>
<div class="skill-track"><div class="skill-bar" data-p="85"></div></div>
<span class="skill-pct">85%</span>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Interactive — Click to update</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Project completion</span><span class="prog-val" id="liveVal">55%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" id="liveBar" style="--p:55%"></div></div>
</div>
<div class="btn-row">
<button class="ctrl-btn" @click="adjustBar(-10)">−10%</button>
<button class="ctrl-btn" @click="adjustBar(-5)">−5%</button>
<button class="ctrl-btn primary" @click="adjustBar(5)">+5%</button>
<button class="ctrl-btn primary" @click="adjustBar(10)">+10%</button>
</div>
</section>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
onMounted(() => {
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
});
</script>
<style scoped>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px}
.page{max-width:580px;margin:0 auto;display:flex;flex-direction:column;gap:28px}
.group{background:#fff;border:1.5px solid #e2e8f0;border-radius:16px;padding:20px 22px;display:flex;flex-direction:column;gap:16px}
.group-title{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin-bottom:2px}
.prog-wrap{display:flex;flex-direction:column;gap:6px}
.prog-header{display:flex;justify-content:space-between;align-items:center}
.prog-label{font-size:13px;font-weight:600;color:#334155}
.prog-val{font-size:12px;font-weight:700;color:#64748b}
.prog-track{height:8px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.prog-bar{height:100%;border-radius:20px;width:var(--p,0%);transition:width .6s cubic-bezier(.4,0,.2,1);background-size:200% 100%;animation:shimmer 2s linear infinite}
.bar-indigo{background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)}
.bar-cyan{background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)}
.bar-red{background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)}
.bar-green{background:linear-gradient(90deg,#10b981,#059669,#10b981)}
.bar-purple{background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.bar-stripe{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#10b981,#059669)}
.bar-stripe.bar-purple{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite}
.bar-stripe{animation:stripe-move 1s linear infinite}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
.seg-track{display:flex;gap:4px}
.seg{flex:1;height:6px;border-radius:4px;background:#e2e8f0;transition:background .3s}
.seg.done{background:#6366f1}
.seg.active{background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
.skills{display:flex;flex-direction:column;gap:10px}
.skill-row{display:grid;grid-template-columns:100px 1fr 36px;align-items:center;gap:10px}
.skill-name{font-size:12px;font-weight:600;color:#334155}
.skill-track{height:7px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.skill-bar{height:100%;border-radius:20px;background:linear-gradient(90deg,#6366f1,#8b5cf6);width:0%;transition:width .8s cubic-bezier(.4,0,.2,1)}
.skill-pct{font-size:11px;font-weight:700;color:#6366f1;text-align:right}
.btn-row{display:flex;gap:8px;flex-wrap:wrap}
.ctrl-btn{padding:6px 14px;border-radius:8px;border:1.5px solid #e2e8f0;background:#fff;color:#475569;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}
.ctrl-btn:hover{border-color:#c7d2fe;color:#6366f1}
.ctrl-btn.primary{background:#6366f1;border-color:#6366f1;color:#fff}
.ctrl-btn.primary:hover{background:#4f46e5}
</style>// @ts-nocheck
// Note: vanilla JS DOM manipulation is preserved as-is inside ngAfterViewInit().
// For idiomatic Angular, replace document.getElementById() with @ViewChild() refs
// and move state into component properties with two-way binding.
import { Component, AfterViewInit, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-gradient-progress-bars',
standalone: true,
imports: [CommonModule],
encapsulation: ViewEncapsulation.None,
template: `
<div class="page">
<section class="group">
<h3 class="group-title">Animated Gradient</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Storage Used</span><span class="prog-val">72%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" style="--p:72%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">CPU Usage</span><span class="prog-val">48%</span></div>
<div class="prog-track"><div class="prog-bar bar-cyan" style="--p:48%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Memory</span><span class="prog-val">89%</span></div>
<div class="prog-track"><div class="prog-bar bar-red" style="--p:89%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Striped Animated</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Uploading files…</span><span class="prog-val">63%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-green" style="--p:63%"></div></div>
</div>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Syncing database</span><span class="prog-val">31%</span></div>
<div class="prog-track"><div class="prog-bar bar-stripe bar-purple" style="--p:31%"></div></div>
</div>
</section>
<section class="group">
<h3 class="group-title">Segmented / Steps</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Onboarding progress</span><span class="prog-val">Step 3 of 5</span></div>
<div class="seg-track">
<div class="seg done"></div>
<div class="seg done"></div>
<div class="seg active"></div>
<div class="seg"></div>
<div class="seg"></div>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Skill Bars</h3>
<div class="skills">
<div class="skill-row">
<span class="skill-name">React</span>
<div class="skill-track"><div class="skill-bar" data-p="90"></div></div>
<span class="skill-pct">90%</span>
</div>
<div class="skill-row">
<span class="skill-name">TypeScript</span>
<div class="skill-track"><div class="skill-bar" data-p="78"></div></div>
<span class="skill-pct">78%</span>
</div>
<div class="skill-row">
<span class="skill-name">Node.js</span>
<div class="skill-track"><div class="skill-bar" data-p="65"></div></div>
<span class="skill-pct">65%</span>
</div>
<div class="skill-row">
<span class="skill-name">CSS / Design</span>
<div class="skill-track"><div class="skill-bar" data-p="85"></div></div>
<span class="skill-pct">85%</span>
</div>
</div>
</section>
<section class="group">
<h3 class="group-title">Interactive — Click to update</h3>
<div class="prog-wrap">
<div class="prog-header"><span class="prog-label">Project completion</span><span class="prog-val" id="liveVal">55%</span></div>
<div class="prog-track"><div class="prog-bar bar-indigo" id="liveBar" style="--p:55%"></div></div>
</div>
<div class="btn-row">
<button class="ctrl-btn" (click)="adjustBar(-10)">−10%</button>
<button class="ctrl-btn" (click)="adjustBar(-5)">−5%</button>
<button class="ctrl-btn primary" (click)="adjustBar(5)">+5%</button>
<button class="ctrl-btn primary" (click)="adjustBar(10)">+10%</button>
</div>
</section>
</div>
`,
styles: [`
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:#f8fafc;min-height:100vh;padding:28px 20px}
.page{max-width:580px;margin:0 auto;display:flex;flex-direction:column;gap:28px}
.group{background:#fff;border:1.5px solid #e2e8f0;border-radius:16px;padding:20px 22px;display:flex;flex-direction:column;gap:16px}
.group-title{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin-bottom:2px}
.prog-wrap{display:flex;flex-direction:column;gap:6px}
.prog-header{display:flex;justify-content:space-between;align-items:center}
.prog-label{font-size:13px;font-weight:600;color:#334155}
.prog-val{font-size:12px;font-weight:700;color:#64748b}
.prog-track{height:8px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.prog-bar{height:100%;border-radius:20px;width:var(--p,0%);transition:width .6s cubic-bezier(.4,0,.2,1);background-size:200% 100%;animation:shimmer 2s linear infinite}
.bar-indigo{background:linear-gradient(90deg,#6366f1,#8b5cf6,#6366f1)}
.bar-cyan{background:linear-gradient(90deg,#0ea5e9,#06b6d4,#0ea5e9)}
.bar-red{background:linear-gradient(90deg,#f59e0b,#ef4444,#f59e0b)}
.bar-green{background:linear-gradient(90deg,#10b981,#059669,#10b981)}
.bar-purple{background:linear-gradient(90deg,#8b5cf6,#6366f1,#8b5cf6)}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.bar-stripe{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#10b981,#059669)}
.bar-stripe.bar-purple{background-image:repeating-linear-gradient(
45deg,
transparent,transparent 6px,
rgba(255,255,255,.18) 6px,rgba(255,255,255,.18) 12px
),linear-gradient(90deg,#8b5cf6,#6366f1);animation:stripe-move 1s linear infinite}
.bar-stripe{animation:stripe-move 1s linear infinite}
@keyframes stripe-move{0%{background-position:0 0}100%{background-position:24px 0}}
.seg-track{display:flex;gap:4px}
.seg{flex:1;height:6px;border-radius:4px;background:#e2e8f0;transition:background .3s}
.seg.done{background:#6366f1}
.seg.active{background:linear-gradient(90deg,#6366f1,#8b5cf6);animation:pulse-seg 1.4s ease-in-out infinite}
@keyframes pulse-seg{0%,100%{opacity:1}50%{opacity:.6}}
.skills{display:flex;flex-direction:column;gap:10px}
.skill-row{display:grid;grid-template-columns:100px 1fr 36px;align-items:center;gap:10px}
.skill-name{font-size:12px;font-weight:600;color:#334155}
.skill-track{height:7px;background:#f1f5f9;border-radius:20px;overflow:hidden}
.skill-bar{height:100%;border-radius:20px;background:linear-gradient(90deg,#6366f1,#8b5cf6);width:0%;transition:width .8s cubic-bezier(.4,0,.2,1)}
.skill-pct{font-size:11px;font-weight:700;color:#6366f1;text-align:right}
.btn-row{display:flex;gap:8px;flex-wrap:wrap}
.ctrl-btn{padding:6px 14px;border-radius:8px;border:1.5px solid #e2e8f0;background:#fff;color:#475569;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;font-family:inherit}
.ctrl-btn:hover{border-color:#c7d2fe;color:#6366f1}
.ctrl-btn.primary{background:#6366f1;border-color:#6366f1;color:#fff}
.ctrl-btn.primary:hover{background:#4f46e5}
`]
})
export class GradientProgressBarsComponent implements AfterViewInit {
ngAfterViewInit(): void {
// Animate skill bars on load
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.skill-bar').forEach(bar => {
setTimeout(() => { bar.style.width = bar.dataset.p + '%'; }, 200);
});
});
// Interactive progress bar
let liveP = 55;
function adjustBar(delta) {
liveP = Math.min(100, Math.max(0, liveP + delta));
const bar = document.getElementById('liveBar');
bar.style.setProperty('--p', liveP + '%');
document.getElementById('liveVal').textContent = liveP + '%';
}
// Bind inline-handler functions to the component so the template can call them
Object.assign(this, { adjustBar });
}
}