A free animated download button UI component with gradient shine, realistic progress bar, success checkmark, and real file download via the JavaScript Blob API. Built with pure HTML, CSS, and vanilla JavaScript. Glassmorphism design, zero libraries. Copy-paste ready.
Every state, CSS class, and JavaScript event in the animated download button lifecycle.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Animated Download Button</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="card" role="region" aria-label="Download demo">
<h1>Animated Download Button</h1>
<p>Click the button to simulate a download with progress, finish state, and a lively gradient animation.</p>
<div class="download-wrap">
<!-- Main button -->
<button id="downloadBtn" class="btn" aria-live="polite">
<!-- Download arrow icon -->
<svg class="icon" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 3v12" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.5 10.5L12 14l3.5-3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21 21H3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" opacity="0.9"/>
</svg>
<span class="label">Download</span>
<!-- Success checkmark icon -->
<svg class="check" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M20 6L9 17l-5-5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="shine"></span> <!-- Shine sweep -->
<span class="bar" aria-hidden="true"></span> <!-- Progress bar -->
</button>
<!-- Progress % + file info -->
<div style="flex:1;display:flex;flex-direction:column">
<div style="display:flex;align-items:center;justify-content:flex-end;gap:8px">
<div id="percent" style="font-size:13px;color:var(--muted);min-width:40px;text-align:right">0%</div>
</div>
<div class="note">File: <strong>demo-file.txt</strong> — 14 KB</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
How the progress bar, animations, and Blob download work.
Create a button with a .bar span absolutely positioned at the bottom (height: 4px, width: 0%, transition: width .2s linear). Use CSS custom properties for gradient coloring. In JavaScript, use a recursive step() function with setTimeout to randomly increment the bar's width from 0% to 100%. Add gradient shine via CSS @keyframes on a separate .shine span. On completion, add a success CSS class and use the Blob API to trigger a real file download.
Two techniques create the shine. The ::before pseudo-element creates a blurred ambient glow that slides from left:-40% to left:120% via CSS transition when .pulse is added. A separate .shine span uses @keyframes shine to sweep a white gradient from translateX(-130%) to translateX(130%) with skewX(-12deg) for a diagonal effect. JavaScript adds the .shining class periodically during progress for an organic shimmer feeling.
The simulateDownload function initializes running = true (prevents double-clicks) and calls a recursive step() via setTimeout. Each step adds a random 2–10% delta to p (capped at 100), then updates bar.style.width and percent.textContent. The delay before the next step is randomly 140–500ms, creating organic-looking uneven progress. When p hits 100, finishDownload() is called.
finishDownload() creates a Blob with generated text content, calls URL.createObjectURL(blob) to get a temporary URL, creates a hidden anchor element with href set to that URL and download='demo-file.txt', appends it to the body, clicks it programmatically, then removes the anchor and revokes the URL. This triggers a real browser file download with zero server or backend required — pure client-side JavaScript.
Yes. Replace the setTimeout simulation with XMLHttpRequest or fetch progress events. For XHR: xhr.onprogress = (e) => { if (e.lengthComputable) { const pct = (e.loaded / e.total) * 100; bar.style.width = pct + '%'; percent.textContent = Math.round(pct) + '%'; } }. The CSS, button structure, shine animation, success state styling, and reset logic all work identically with real download progress. The component is keyboard-accessible (Enter/Space) and uses ARIA aria-live for screen reader announcements.
Yes. This project teaches: CSS custom properties and glassmorphism styling, CSS @keyframes for the shine animation, JavaScript class-based state management (.pulse, .shining, .success), recursive setTimeout for animation control, the JavaScript Blob API for client-side file generation, DOM manipulation and inline style updates, and ARIA accessibility attributes (aria-live, aria-hidden). Download the full source code free from Coodeverse.
Free courses in JavaScript, Python, SQL, Kotlin, HTML, CSS, DSA. No account needed.