Build a Download Button Full of Micro Interactions

We often see websites and apps that really get our attention throught some UI details and well designed animations. Those little animation pieces that complement the design are also known as micro-interactions, and they often makes the difference between a good experience and an awesome experience.

Today, we would like to show you how to build a functional download button, full of meaningful micro interactions. In the proccess, we will be learning some exciting things like:

Please note that it's a bit complex component, that needs larger amount of code and consume more resources than a simple button, so be careful with performance in production. This is sometimes the cost to have fancy components like this in our apps, so it's a choice you need to make.

It is important to note that the SVG path elements have been drawn 'by hand' to get the result we want. For example, at some point, we want the button border to perform an elastic animation, so we need an SVG path ready for that morphing animation with anime.js (same structure in both paths). Something like this:

With our markup ready, let's see some fundamental pieces of style needed to start getting the look and feel we are looking for. Please note that we are not including the whole stylesheet here to be able to remark only the main parts, but you can find the entire code on the Github repository. Also the code has been fully commented for better understanding.

So, let's see the SCSS variables we have defined, and a helper class to hide elements:

Our button could be in 3 different states: downloading, progressing and completed. So we have defined the styles needed for each state using the following structure:

// Button container.download-button-container {// ...CODE...// Following are the different states for the button: downloading, progressing and completed// We have defined the states in the container to have access to all descendants in CSS// Downloading: The download button has been pressed&.downloading {// ...CODE...}// Progressing: The progress starts&.progressing {// ...CODE...}// Completed: The progress ends&.completed {// ...CODE...}}

And another interesting piece of code, it's the used to achieve the ball animation when the download has finished:

.button-ball {left:50%;transition: none;// CSS animations for the ball. All of them starts at the same time, so we need to take care of delaysanimation:
ball-throw-up 0.5s ease-out forwards, // Throw up the ball for 0.5s
ball-throw-down 0.5s 0.5s ease-in forwards, // Wait 0.5 seconds (throw up), and throw down the ball for 0.5s
ball-rubber 1s forwards;// Move the ball like a rubber deformation during 1s (throw up + throw down)}// Throw up animation@keyframes ball-throw-up{from{transform:translate(-50%, 17.5px);}to {transform:translate(-50%, -60px);background-color:#00FF8D;}}// Throw down animation@keyframes ball-throw-down{from{transform:translate(-50%, -60px);}to {transform:translate(-50%, 80px);}}// Rubber animation@keyframes ball-rubber{from{width:$ball-width;}25% {width:$ball-width*0.75;}50% {width:$ball-width;}to {width:$ball-width/2;}}

As we have said before, all the other styles used can be found on the Github repository. We have just remark here the most exciting pieces of code used.

As we have pointed out at the beggining, we will be using anime.js and segment.js, both lightweight libraries to help with animations. You should have a basic understanding of them to continue, althought the code used is very straighforward.

Also please note that we will not include some variables declarations in the following code snippets, for the sake of clarity. If you have any doubth, please check the Github repository.

So, here is the basic code we are using to capture the click events on the button and perform the behavior we want:

Finally, here is the piece of code used to perform the animation when download has been completed, where the ball animation is triggered and we morph the path elements. Please follow the comments to not get confused in the callback hell :)