Animated Download Button
This component showcases a high-quality animated download button with a realistic download experience, including:
- Animated arrow and icon morphing
- Percentage-based progress counter
- Smooth 3D button motion
- Success and restart states
- Polished micro-interactions
Perfect for modern dashboards, SaaS products, and premium UI designs.
Preview Images
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <title>Download button animation | CodewithLord</title>
7 <link rel="stylesheet" href="https://public.codepenassets.com/css/reset-2.0.min.css">
8 <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap'>
9 <link rel="stylesheet" href="./style.css">
10</head>
11
12<body>
13 <a class="dl-button" href="">
14 <div>
15 <div class="icon">
16 <div>
17 <svg class="arrow" viewBox="0 0 20 18" fill="currentColor">
18 <polygon points="8 0 12 0 12 9 15 9 10 14 5 9 8 9"></polygon>
19 </svg>
20 <svg class="shape" viewBox="0 0 20 18" fill="currentColor">
21 <path
22 d="M4.82668561,0 L15.1733144,0 C16.0590479,0 16.8392841,0.582583769 17.0909106,1.43182334 L19.7391982,10.369794 C19.9108349,10.9490677 19.9490212,11.5596963 19.8508905,12.1558403 L19.1646343,16.3248465 C19.0055906,17.2910371 18.1703851,18 17.191192,18 L2.80880804,18 C1.82961488,18 0.994409401,17.2910371 0.835365676,16.3248465 L0.149109507,12.1558403 C0.0509788145,11.5596963 0.0891651114,10.9490677 0.260801785,10.369794 L2.90908938,1.43182334 C3.16071592,0.582583769 3.94095214,0 4.82668561,0 Z">
23 </path>
24 </svg>
25 </div><span></span>
26 </div>
27 <div class="label">
28 <div class="show default">Download</div>
29 <div class="state">
30 <div class="counter">
31 <ul>
32 <li></li>
33 <li>1</li>
34 </ul>
35 <ul>
36 <li>0</li>
37 <li>1</li>
38 <li>2</li>
39 <li>3</li>
40 <li>4</li>
41 <li>5</li>
42 <li>6</li>
43 <li>7</li>
44 <li>8</li>
45 <li>9</li>
46 <li>0</li>
47 </ul>
48 <ul>
49 <li>0</li>
50 <li>1</li>
51 <li>2</li>
52 <li>3</li>
53 <li>4</li>
54 <li>5</li>
55 <li>6</li>
56 <li>7</li>
57 <li>8</li>
58 <li>9</li>
59 <li>0</li>
60 <li>1</li>
61 <li>2</li>
62 <li>3</li>
63 <li>4</li>
64 <li>5</li>
65 <li>6</li>
66 <li>7</li>
67 <li>8</li>
68 <li>9</li>
69 <li>0</li>
70 <li>1</li>
71 <li>2</li>
72 <li>3</li>
73 <li>4</li>
74 <li>5</li>
75 <li>6</li>
76 <li>7</li>
77 <li>8</li>
78 <li>9</li>
79 <li>0</li>
80 </ul><span>%</span>
81 </div><span>Done</span>
82 </div>
83 </div>
84 <div class="progress"></div>
85 </div>
86 </a><a class="restart" href="">
87 <svg viewBox="0 0 16 16" fill="currentColor">
88 <path
89 d="M4.5,4.5c1.9-1.9,5.1-1.9,7,0c0.7,0.7,1.2,1.7,1.4,2.7l2-0.3C14.7,5.4,14,4.1,13,3.1c-2.7-2.7-7.1-2.7-9.9,0 L0.9,0.9L0.2,7.3l6.4-0.7L4.5,4.5z">
90 </path>
91 <path
92 d="M15.8,8.7L9.4,9.4l2.1,2.1c-1.9,1.9-5.1,1.9-7,0c-0.7-0.7-1.2-1.7-1.4-2.7l-2,0.3 C1.3,10.6,2,11.9,3,12.9c1.4,1.4,3.1,2,4.9,2c1.8,0,3.6-0.7,4.9-2l2.2,2.2L15.8,8.7z">
93 </path>
94 </svg>Restart</a>
95 <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
96 <script src="./script.js"></script>
97
98</body>
99
100</html>
CSS Code
1html { 2 box-sizing: border-box; 3 -webkit-font-smoothing: antialiased; 4} 5 6* { 7 box-sizing: inherit; 8} 9 10*:before, 11*:after { 12 box-sizing: inherit; 13} 14 15body { 16 min-height: 100vh; 17 font-family: Roboto, Arial; 18 display: flex; 19 justify-content: center; 20 align-items: center; 21 background: #252432; 22 padding: 20px; 23} 24 25.dl-button { 26 --duration: 4000; 27 --success: #16BF78; 28 --grey-light: #99A3BA; 29 --grey: #6C7486; 30 --grey-dark: #3F4656; 31 --light: #CDD9ED; 32 --shadow: rgba(18, 22, 33, .6); 33 --shadow-dark: rgba(18, 22, 33, .85); 34 display: block; 35 text-decoration: none; 36 perspective: 500px; 37} 38 39.dl-button>div { 40 position: relative; 41 background: #fff; 42 border-radius: 5px; 43 overflow: hidden; 44 display: flex; 45 padding: 16px 24px; 46 box-shadow: 0 4px 12px var(--shadow); 47} 48 49.dl-button>div .icon { 50 --color: var(--grey); 51 margin-right: 12px; 52 position: relative; 53 transform: translateZ(8px); 54} 55 56.dl-button>div .icon div { 57 overflow: hidden; 58 position: relative; 59 width: 20px; 60 height: 22px; 61} 62 63.dl-button>div .icon div:before, 64.dl-button>div .icon div:after { 65 content: ""; 66 position: absolute; 67 width: 2px; 68 height: 2px; 69 top: 2px; 70 transition: opacity 0.3s ease; 71} 72 73.dl-button>div .icon div:before { 74 left: 6px; 75 background-image: radial-gradient(circle at 0 100%, var(--color) 2px, #fff 0px); 76} 77 78.dl-button>div .icon div:after { 79 right: 6px; 80 background-image: radial-gradient(circle at 100% 100%, var(--color) 2px, #fff 0px); 81} 82 83.dl-button>div .icon div svg { 84 width: 20px; 85 height: 18px; 86 display: block; 87 margin-top: 2px; 88 position: relative; 89 z-index: 1; 90} 91 92.dl-button>div .icon div svg.arrow { 93 color: #fff; 94 position: absolute; 95 left: 0; 96 top: 0; 97 z-index: 2; 98 transform: translateY(-1px); 99} 100 101.dl-button>div .icon div svg.shape { 102 color: var(--color); 103 transition: color 0.4s ease; 104} 105 106.dl-button>div .icon span { 107 --s: 1; 108 position: absolute; 109 left: 1px; 110 right: 1px; 111 bottom: 2px; 112 background: var(--color); 113 height: 6px; 114 border-radius: 50%; 115 display: block; 116 transform: translateY(0) scale(var(--s)); 117} 118 119.dl-button>div .label { 120 --color: var(--grey-dark); 121 line-height: 22px; 122 font-size: 16px; 123 font-weight: 500; 124 color: var(--color); 125 position: relative; 126 transition: color 0.4s ease; 127 transform: translateZ(8px); 128} 129 130.dl-button>div .label>div { 131 display: flex; 132 transition: opacity 0.25s ease; 133} 134 135.dl-button>div .label>div:not(.show) { 136 position: absolute; 137 left: 0; 138 top: 0; 139 opacity: 0; 140} 141 142.dl-button>div .label>div.hide { 143 opacity: 0; 144} 145 146.dl-button>div .label>div .counter { 147 overflow: hidden; 148 display: flex; 149 height: 18px; 150 line-height: 18px; 151 margin: 2px 0; 152 position: relative; 153 transition: opacity 0.3s ease; 154} 155 156.dl-button>div .label>div .counter:before, 157.dl-button>div .label>div .counter:after { 158 content: ""; 159 display: block; 160 position: absolute; 161 left: 0; 162 right: 0; 163 height: 3px; 164 z-index: 1; 165} 166 167.dl-button>div .label>div .counter:before { 168 top: 0; 169 background: linear-gradient(to bottom, white 0%, rgba(255, 255, 255, 0) 100%); 170} 171 172.dl-button>div .label>div .counter:after { 173 bottom: 0; 174 background: linear-gradient(to top, white 0%, rgba(255, 255, 255, 0) 100%); 175} 176 177.dl-button>div .label>div .counter span { 178 display: inline-block; 179 margin: 0 4px 0 2px; 180} 181 182.dl-button>div .label>div .counter ul { 183 --y: 0; 184 margin: 0; 185 padding: 0; 186 list-style: none; 187 width: 10px; 188 height: 18px; 189 -webkit-backface-visibility: hidden; 190 transform: translateY(var(--y)) translateZ(0); 191} 192 193.dl-button>div .label>div .counter ul:nth-child(1) { 194 transition: transform calc(var(--duration) * .2ms) ease-in-out; 195} 196 197.dl-button>div .label>div .counter ul:nth-child(2) { 198 transition: transform calc(var(--duration) * .8ms) ease-in-out; 199} 200 201.dl-button>div .label>div .counter ul:nth-child(3) { 202 transition: transform calc(var(--duration) * .8ms) ease-in-out; 203} 204 205.dl-button>div .label>div .counter ul li { 206 width: 10px; 207 height: 18px; 208} 209 210.dl-button>div .label>div .counter.hide { 211 opacity: 0; 212} 213 214.dl-button>div .progress { 215 --s: 0; 216 position: absolute; 217 left: 0; 218 right: 0; 219 bottom: 0; 220 height: 3px; 221 transform-origin: 50% 100%; 222 transform: scaleY(var(--s)); 223 transition: transform 0.4s ease; 224} 225 226.dl-button>div .progress:before, 227.dl-button>div .progress:after { 228 --s: 1; 229 content: ""; 230 background: var(--success); 231 position: absolute; 232 left: 0; 233 top: 0; 234 bottom: 0; 235 right: 0; 236 transform-origin: 0 50%; 237 transform: scaleX(var(--s)); 238} 239 240.dl-button>div .progress:before { 241 opacity: 0.35; 242} 243 244.dl-button>div .progress:after { 245 --s: 0; 246 transition: transform calc(var(--duration) * .9ms) ease-in-out; 247} 248 249.dl-button.active>div { 250 -webkit-animation: button calc(var(--duration) * 1ms) linear forwards; 251 animation: button calc(var(--duration) * 1ms) linear forwards; 252} 253 254.dl-button.active>div .icon div:before, 255.dl-button.active>div .icon div:after { 256 opacity: 0; 257 transition-delay: 0.4s; 258} 259 260.dl-button.active>div .icon svg.arrow { 261 -webkit-animation: arrow calc(var(--duration) * .18ms) linear 4 calc(var(--duration) * .2ms); 262 animation: arrow calc(var(--duration) * .18ms) linear 4 calc(var(--duration) * .2ms); 263} 264 265.dl-button.active>div .icon span { 266 -webkit-animation: span calc(var(--duration) * .18ms) linear 4 calc(var(--duration) * .2ms); 267 animation: span calc(var(--duration) * .18ms) linear 4 calc(var(--duration) * .2ms); 268} 269 270.dl-button.active>div .label>div .counter ul:nth-child(1) { 271 --y: -18px; 272 transition-delay: calc(var(--duration) * .72ms); 273} 274 275.dl-button.active>div .label>div .counter ul:nth-child(2) { 276 --y: -180px; 277 transition-delay: calc(var(--duration) * .09ms); 278 -webkit-animation: motion calc(var(--duration) * .5ms) linear forwards calc(var(--duration) * .19ms); 279 animation: motion calc(var(--duration) * .5ms) linear forwards calc(var(--duration) * .19ms); 280} 281 282.dl-button.active>div .label>div .counter ul:nth-child(3) { 283 --y: -540px; 284 transition-delay: calc(var(--duration) * .075ms); 285 -webkit-animation: motion calc(var(--duration) * .8ms) linear forwards calc(var(--duration) * .075ms); 286 animation: motion calc(var(--duration) * .8ms) linear forwards calc(var(--duration) * .075ms); 287} 288 289.dl-button.active>div .progress { 290 --s: 1; 291 transition-delay: 0.4s; 292} 293 294.dl-button.active>div .progress:after { 295 --s: 1; 296 transition-delay: 0.4s; 297} 298 299.dl-button.done>div .icon { 300 --color: var(--success); 301} 302 303.dl-button.done .label { 304 --color: var(--success); 305} 306 307.dl-button.done .label .counter { 308 width: 0; 309} 310 311@-webkit-keyframes arrow { 312 38% { 313 transform: translateY(100%); 314 opacity: 1; 315 } 316 317 39% { 318 transform: translateY(100%); 319 opacity: 0; 320 } 321 322 40% { 323 transform: translateY(-100%); 324 opacity: 0; 325 } 326 327 41% { 328 transform: translateY(-100%); 329 opacity: 1; 330 } 331 332 100% { 333 transform: translateY(-1px); 334 opacity: 1; 335 } 336} 337 338@keyframes arrow { 339 38% { 340 transform: translateY(100%); 341 opacity: 1; 342 } 343 344 39% { 345 transform: translateY(100%); 346 opacity: 0; 347 } 348 349 40% { 350 transform: translateY(-100%); 351 opacity: 0; 352 } 353 354 41% { 355 transform: translateY(-100%); 356 opacity: 1; 357 } 358 359 100% { 360 transform: translateY(-1px); 361 opacity: 1; 362 } 363} 364 365@-webkit-keyframes span { 366 25% { 367 transform: translateY(2px) scale(var(--s)); 368 } 369 370 55% { 371 transform: translateY(2px) scale(var(--s)); 372 } 373 374 80%, 375 100% { 376 transform: translateY(0) scale(var(--s)); 377 } 378} 379 380@keyframes span { 381 25% { 382 transform: translateY(2px) scale(var(--s)); 383 } 384 385 55% { 386 transform: translateY(2px) scale(var(--s)); 387 } 388 389 80%, 390 100% { 391 transform: translateY(0) scale(var(--s)); 392 } 393} 394 395@-webkit-keyframes motion { 396 397 20%, 398 70% { 399 filter: blur(0.4px); 400 } 401} 402 403@keyframes motion { 404 405 20%, 406 70% { 407 filter: blur(0.4px); 408 } 409} 410 411@-webkit-keyframes button { 412 0% { 413 transform: translateX(0) translateZ(0) scale(1) rotateY(0deg); 414 } 415 416 10% { 417 transform: translateX(0) translateZ(0) scale(0.96) rotateY(0deg); 418 box-shadow: 0 4px 8px var(--shadow-dark); 419 } 420 421 20% { 422 transform: translateX(-16px) translateZ(32px) scale(1) rotateY(-16deg); 423 box-shadow: 4px 12px 20px var(--shadow-dark); 424 } 425 426 85% { 427 transform: translateX(16px) translateZ(32px) scale(1) rotateY(16deg); 428 box-shadow: -4px 12px 20px var(--shadow-dark); 429 } 430 431 95% { 432 transform: translateX(0) translateZ(0) scale(1.12) rotateY(0deg); 433 box-shadow: 0 8px 24px var(--shadow-dark); 434 } 435 436 100% { 437 transform: translateX(0) translateZ(0) scale(1) rotateY(0deg); 438 } 439} 440 441@keyframes button { 442 0% { 443 transform: translateX(0) translateZ(0) scale(1) rotateY(0deg); 444 } 445 446 10% { 447 transform: translateX(0) translateZ(0) scale(0.96) rotateY(0deg); 448 box-shadow: 0 4px 8px var(--shadow-dark); 449 } 450 451 20% { 452 transform: translateX(-16px) translateZ(32px) scale(1) rotateY(-16deg); 453 box-shadow: 4px 12px 20px var(--shadow-dark); 454 } 455 456 85% { 457 transform: translateX(16px) translateZ(32px) scale(1) rotateY(16deg); 458 box-shadow: -4px 12px 20px var(--shadow-dark); 459 } 460 461 95% { 462 transform: translateX(0) translateZ(0) scale(1.12) rotateY(0deg); 463 box-shadow: 0 8px 24px var(--shadow-dark); 464 } 465 466 100% { 467 transform: translateX(0) translateZ(0) scale(1) rotateY(0deg); 468 } 469} 470 471.dl-button.done+.restart { 472 opacity: 1; 473 visibility: visible; 474} 475 476.restart { 477 --grey-dark: #3F4656; 478 position: absolute; 479 bottom: 20%; 480 left: 50%; 481 transform: translateX(-50%); 482 color: var(--grey-dark); 483 font-size: 14px; 484 line-height: 16px; 485 text-decoration: none; 486 opacity: 0; 487 visibility: hidden; 488 transition: opacity 0.4s ease; 489} 490 491.restart svg { 492 width: 16px; 493 height: 16px; 494 margin-right: 4px; 495 display: inline-block; 496 vertical-align: top; 497}
Javascript Code
1$('.dl-button').on('click', e => {
2
3 let btn = $(e.currentTarget),
4 label = btn.find('.label'),
5 counter = label.find('.counter');
6
7 if (!btn.hasClass('active') && !btn.hasClass('done')) {
8
9 btn.addClass('active');
10
11 setLabel(label, label.find('.default'), label.find('.state'));
12
13 setTimeout(() => {
14 counter.addClass('hide');
15 counter.animate({
16 width: 0
17 }, 400, function () {
18 label.width(label.find('.state > span').width());
19 counter.removeAttr('style');
20 });
21 btn.removeClass('active').addClass('done');
22 }, getComputedStyle(btn[0]).getPropertyValue('--duration'));
23
24 }
25
26 return false;
27
28});
29
30$('.restart').on('click', e => {
31
32 let btn = $('.dl-button'),
33 label = btn.find('.label'),
34 counter = label.find('.counter');
35
36 setLabel(label, label.find('.state'), label.find('.default'), function () {
37 counter.removeClass('hide');
38 btn.removeClass('done');
39 });
40
41 return false;
42
43});
44
45function setLabel(div, oldD, newD, callback) {
46 oldD.addClass('hide');
47 div.animate({
48 width: newD.outerWidth()
49 }, 200, function () {
50 oldD.removeClass('show hide');
51 newD.addClass('show');
52 div.removeAttr('style');
53 if (typeof callback === 'function') {
54 callback();
55 }
56 });
57}
Explanation of the Code
HTML Structure
Button built using semantic tag for accessibility.
Nested elements allow independent animation control.
SVG icons provide crisp, scalable visuals.
CSS Animations & Styling
CSS variables manage duration, colors, and shadows.
3D transforms and perspective create depth.
Keyframe animations drive arrow motion and progress flow.
Progress Counter Logic
Animated vertical number lists simulate a rolling counter.
Percentage increments are synced with CSS duration.
Smooth transitions ensure premium user experience.
JavaScript Interaction
Handles state changes: idle → downloading → done.
Prevents multiple clicks during animation.
Restart button resets animation state cleanly.
Use Cases
File downloads
SaaS dashboards
Call-to-action buttons
UI animation showcases
