Animated Icons Tutorial

This tutorial is about animating icons. An icon usually refers to a small illustration. It is used instead of words on controls. Typical icons you encounter include:

(I have used a free icon library called Friconix to provide these examples. If you want, you can start with an icon from a library, but you need to add animation and other properties, so be careful what you use. Believe me, it is better if you draw it yourself. Plus I only ask for 2, so…)

More elaborate icons are also used to identify apps: the blue ‘Ps in a square’ of Photoshop (the icon was much more detailed before, look it up), Chrome’s colourful poké ball, etc. Emojis are icons too. Icons are everywhere.

File Formats

First we need to talk about file formats. There are several options. And depending on what you select, your possibilities and the way you will deal with the states will be different.


The most obvious option, perhaps, is to draw your icons as SVG. You already know this format, and you know you can animate it easily in CSS and using libraries, now that you have completed the first assignment. (You see, there is a logic in the way I build my course.) SVGs are cool because you can scale them without trouble, they are easy to animate, and they are super light.

A fun thing about using SVGs is that you can include loop animations (not just transitions) within your icons.


You can also draw your icons as image files. When you select this option though, keep in mind that you will need to either create several different images for the states, or (even better) you will need to make a sprite sheet. Again, you now know how this works. (Wow, could I have included more Ws in that sentence?) If you do your icons as images, select one format – png, gif, jpeg – and do all of them in the same format. Otherwise they will not work well together.

In this tutorial, I will focus on using SVG because I think it is the simplest and most robust solution. But you can select a different path.

Basic SVG Icon

This is an example of an icon drawn in SVG. If it were used in an app, it would probably be smaller, but I want to make this presentation clear (hint). This icon has three states: stand, hover, and click. (OMG, these are the three states that the teacher is asking us to create, OMG!) If you put your mouse over it, you will have a surprise. And if you click it, you will see something else happen. Let’s see how it’s done.

This is the SVG code for this icon. You can also look at the source of this page… You don’t have to worry about the details, but notice the IDs of the two groups: smoke and house. We’ll use these in the code later.

<svg version="1.1" xmlns="" xmlns:xlink="" x="0px" y="0px"
   viewBox="0 0 200 200" xml:space="preserve">
<g id="smoke">
  <path class="st0" d="M129.5,66.5c0,0-4-9,1-22s3-26.6,0-29.3c-2.2-2,11.2,8.2,13.5,28.1c0.8,6.7,0.2,14.4-2.5,23.2H129.5z"/>
<g id="house">
  <polygon class="st1" points="146.5,104.7 146.5,66.5 124.5,66.5 124.5,86.8 99.5,66.5 24.5,127.5 49.8,127.5 49.4,180 87,180 
    87,143.5 112,143.5 112,180 148.7,180 149.1,127.5 174.5,127.5"/>

Alright, now let’s look at the states. You have probably figured out by now, that the states are often defined in CSS, using pseudo classes like :hover and :active. For this icon, we will use JavaScript (JS) as well to define the states. In fact, I designed this icon using an animation library other than GSAP that we used in the previous assignment. The transitions are animated in CSS, but the smoke flicker uses Anime.js. (More about this later.) I did this just to show you how to use another library. Something you need to get used to. Of course, you can use GSAP in your icons. Or you can also use no library at all and just animate the transitions using CSS. It’s your work.


The stand state is just the drawing as is. But we need to hide the smoke. So the CSS looks like this. Notice that the opacity of the smoke path is 0 (zero). Both house and smoke have transitions setup, so when properties change, CSS will animate the transition.

Also notice that the selectors apply these properties to the object inside the group rather than to the group. In this case here, we want to manipulate the fill and other properties of the path and polygon. The group <g> elements have no such properties. So we need to manipulate the vector objects, not the groups.

#house polygon {
  transition: all 0.2s; 
#smoke path {
  opacity: 0;
  transition: opacity 0.5s;


I will talk about the click state first because it is less complicated and more familiar. The only thing is that the event trigger is managed by JS instead of CSS.

So what happens is that we change the value of the fill property of the house polygon from white to green. The JS code looks like this. (This code would need to be inside a <script> tag at the bottom of your HTML file to work.)

document.querySelector("#svgIcon").addEventListener("mousedown",function() {
	document.querySelector("#house polygon").style.fill = "#393";
document.querySelector("#svgIcon").addEventListener("mouseup",function() {
	document.querySelector("#house polygon").style.fill = "#FFF";

You notice right away that I do not use the click event. Why not? The thing is that I want to set the fill colour to green only when the mouse is down, when the user is cliking. And then, I want the colour to be back to white when the mouse is lifted. So I need to handle the mouse down event (to make the fill green) and then the mouse up event (to make it white again). If you want something like a like button, you can use the click event. Once the icon is clicked, it will remain as is. (Don’t forget the mandate though!)

So the first line selects the #svgIcon element, and attaches to it an eventListener for the mousedown event. When this event is triggered, we select the #house polygon element, and set its fill property to #393 (green).

The other event listener is similar, but it turns the fill white on the mouseup event.


Now let’s handle the case when the mouse moves over the icon, the smoke animation appears. There are two thing happening here: a transition (the smoke appears) and an animation (the smoke moves around). The transition is achieved by changing the opacity of the smoke path. The animation, like mentioned above is done using the Anime.js library. So the trigger of the state change event is managed by JS.

The code looks like this. Notice the two separate parts.

let smokeAnim = anime.timeline({ autoplay: false, loop: true, duration: 8000, easing: "easeInOutQuint" });
	targets: "#smoke path",
	d: [
		{ value: 'M142.8,63.5c0,0,4-9-1-22s-3-26.6,0-29.3c2.2-2-11.2,8.2-13.5,28.1c-0.8,6.7-0.2,14.4,2.5,23.2H142.8z'},
		{ value: 'M129.5,70.5c0,0-4-9,1-22s3-26.6,0-29.3c-2.2-2,11.2,8.2,13.5,28.1c0.8,6.7,0.2,14.4-2.5,23.2H129.5z'},
		{ value: 'M142.8,64c0,0,4-9-1-22s-3-26.6,0-29.3c2.2-2-11.2,8.2-13.5,28.1c-0.8,6.7-0.2,14.4,2.5,23.2H142.8z'},
		{ value: 'M129.5,66.5c0,0-4-9,1-22s3-26.6,0-29.3c-2.2-2,11.2,8.2,13.5,28.1c0.8,6.7,0.2,14.4-2.5,23.2H129.5z'},

document.querySelector("#svgIcon").addEventListener("mouseenter",function() {
	document.querySelector("#smoke path").style.opacity = "1";;
document.querySelector("#svgIcon").addEventListener("mouseleave",function() {
	document.querySelector("#smoke path").style.opacity = "0";

The first part is the animation. I will not get into too much details about Anime.js in this tutorial. Just notice that the variable reference to the animation is called smokeAnim.

Like I said above, you do not need to use a library for this assignment. And if you want, you can use the GSAP library you already know a little bit. I leave the Anime code there though if you are curious and would like to try it out. Imporant: Do not venture into this if you are struggling with code. An important feature of becoming a designer/creator is to know your limits. I include this here for the more advanced and curious among you. It is perfectly ok to ignore this part.

So focus on the second part: the transition. Here again we have two events: mouseenter and mouseout. These are triggered respectively when the mouse enters the vicinity of the element, and when it moves away from it. It is possible that you are already familiar with these two events, they are quite common.

On mouseenter the #svgIcon element, we select the #smoke path and set its opacity to 1. This renders it visible. Then we play the smokeAnim animation. This play() function comes from the Anime.js library.

On mouseleave now, we reverse this. We set the opacity of #smoke path back to 0 (hide it again), and we pause the smokeAnim.


Another Icon Example

I thought I would give you one more example. And I made it a bit different so you have options. Obviously there are tons of other possibilities.

So here we have an info icon. Put your mouse over it, and click it. You’ll see the three states. Here is the SVG code. You will notice th athere are two different circles. One it the black one that we see by default, the other is the green one that is drawn on hover. We’ll see how to make this happen below.

<svg xmlns="" viewbox="-32 -32 200 200">
<g id="circle">
  <circle cx="76" cy="76" r="74"></circle>
<g id="circleTop">
  <circle cx="76" cy="76" r="74"></circle>
<g id="letter">
  <path d="M117.63,148H81.37V131h5.44V100.08H81.37V83.24h30.05V131h6.21ZM85,64.33a12.53,12.53,0,0,1,.91-4.53,13.74,13.74,0,0,1,2.66-4.28A13.41,13.41,0,0,1,93,52.35a15.37,15.37,0,0,1,12.05,0,14.18,14.18,0,0,1,7.25,7.45,11.66,11.66,0,0,1,1,4.53,13,13,0,0,1-3.63,9.26Q106,77.41,99,77.41a13.37,13.37,0,0,1-5.63-1.16,15.25,15.25,0,0,1-4.4-3A13.68,13.68,0,0,1,86,69,11.4,11.4,0,0,1,85,64.33Z" transform="translate(-24 -24)"></path>

Here again, don’t worrry about the details of the SVG code. (Also, you’ll see that I had to adjust my viewbox a bit because I did not draw this thing properly. 🙃) But notice the #circle group which is the black outline circle; the #circleTop group which is the green circle we will make appear; and the #letter group wich is the ‘i’ letter path.

The Stand and Hover states in CSS

The CSS for these elements handles the stand or default and hover states. It is as follows.

#svgInfo #letter {
  transform-origin: 40% 15%;
  transition: all 0.2s;
#svgInfo #circle circle {
#svgInfo #circleTop circle {
  stroke-dasharray: 465;
  transition: all 0.5s ease-in;
#svgInfo:hover #circleTop circle {

The first rule here sets the transform-origin of the #letter. This is to control the scale that we will be doing on the letter on mousedown. This is just an æsthetic adjustment. We also set the transition property to handle the animation between states.

The second rule defines the default look of the black circle: #svgInfo #circle circle. Nothing fancy here.

The next one defines the default properties of the second circle: #circleTop. Here there are two properties that you probably have never seen before: stroke-dasharray and stroke-dashoffset. These are SVG properties that create a dashed line. You’ve probably seen in Illustrator how to make dashed strokes using the stroke panel. Well in SVG, this is the code you use to make such lines. Here we are using these properties to create a huge gap between dashes in the outline of the circle. This gap is so large in fact that the path becomes invisible (the gap is a long as the whole stroke itself). And then we offset the dash, basically we move it, so it starts outside of the length of the path. (See next paragraph for what we do with this.) Finally, in this rule, we set the transition to animate the changes.

The last rule sets the stroke-dashoffset back to 0 (zero) on hover. This slides back the dash of this line to the start of the path, effectively drawing in the stroke. Magic.

FYI, the used the technique described in the following video for this trick. You may want to check out the video if you are not sure you understand all the details.

The Click state in JS

Lastly, we need to deal with the click state. For this we will use JS again. The code is like so.

//Info Icon click state
document.querySelector("#svgInfo").addEventListener("mousedown",function() {
	document.querySelector("#letter").style.transform = "scale(0.9)";
	document.querySelector("#letter path").style.fill = "#393";
document.querySelector("#svgInfo").addEventListener("mouseup",function() {
	document.querySelector("#letter").style.transform = "scale(1)";
	document.querySelector("#letter path").style.fill = "#000";

By now you should be familiar with this code. We have two events: mousedown and mouseup. Each event toggles the transform and the fill attributes of the #letter element and its path child.

Remember that the scale is affected by the transform-origin we set as a default for the #letter element. That is how the scale looks like it’s centered on the dot of the ‘i’.

Handling touch events

As a last note, I thought I could give you a primer on handling mobile touch events. The mouseenter and mouseout events only trigger when a mouse is used by the user. On a mobile, there is no mouse, so these events never trigger. Instead, a different set of events trigger.

The equivalent to the mouseenter event is touchstart. This is triggered when the user puts a finger on the device. The touch version of mouseleave is touchend. This one happens when the user lifts their finger.

In order to make your button react to these events, you need to add specific event handlers. All you really need to do is duplicate your event listeners, and change the events to the touch equivalent.

For example, for the info button, it would look like this.

document.querySelector("#svgInfo").addEventListener("touchstart",function() {
	document.querySelector("#letter").style.transform = "scale(0.9)";
	document.querySelector("#letter path").style.fill = "#393";
document.querySelector("#svgInfo").addEventListener("touchend",function() {
	document.querySelector("#letter").style.transform = "scale(1)";
	document.querySelector("#letter path").style.fill = "#000";

The only difference here with what you have from above is the name of the event, the first parametre of the addEventListener function.

Of course, you could also make the touch events do something different than the mouse events. This would mean that the effect on the icon or button would be different depending if you are on a computer or using your mobile.

Note that the :hover state in CSS works strangely and somewhat unpredictably on mobile. It often behaves like a :focus. There are many intricacies to deal with when designing interfaces for mobile. I will not get into all the details here. I could build a whole course about this. But I encourage you to at least add the touch events to your buttons and icons. (I have added them on this page.)


There are many other things you can do with an icon drawn in SVG. I have provided above only a few examples. I tried to include a variety of properties and options. I want to give you ideas, but I cannot tell you everything that is possible. This is not an exercise, it is a project. Remember what I wrote about becoming a designer/creator on the assignment sheet.

Most importantly, make sure you have a plan, and make sure you start working on this early. I cannot stress this enough.


You need to know what you want to achieve in order to find the way to achieve it. You cannot go into this without a plan. Again, you are creating here. You are making the decisions. You are not following a pre-established path, you are building your own object, your own product.