Bring an icon to life with CSS

Joss shares how you can create your own icon with CSS

Clock 2 min read Calendar Published: 18 Apr 2023
Author Joss Dixon
Bring an icon to life with CSS

 

It’s arguable that good animation should complement the page and add a certain finesse to the overall design rather than being a focal point of it. If anything, err on the side of caution and consider the possibility of a particular flourish becoming a bit irritating when seen repeatedly by the user. If that seems at all likely, tone it down and go for something less dramatic.

I had a perfect opportunity to put this into practice recently while working on a prototype for a new feature at Glean. While I won’t reveal the specifics of the work for fear of sharing highly classified information, it’s still easy enough to illustrate the point.

Let’s say you have a small icon or image that you want to animate, but rather than giving it a simple rotation or fade you want to show the effect of it going through a small change in state. Fundamentally, animating this change relies on keeping the icon whole while changing one element of it. For a good example, I recommend hovering your mouse over the small mascot face in the top left corner of the Twitch website. 

As a disclaimer, there may well be much better ways of doing this (and indeed you can probably see that the following is not what Twitch do), but for me this was a quick and straightforward solution. The trick is to give the illusion of there being a single image which changes, when in fact it’s simply a transition between two similar images.

Illustration Of A Robot Head

I’ll show you the way that I achieved this. First, you’re going to have a structure like this:

<div class=“image-container”>

    <img class=“image-one”>

    <img class=“image-two”>

</div>

Secondly, use a grid to position the images on top of each other. For me, this is preferable to using absolute and relative positioning because it’s much simpler and more responsive. This would mean that in your CSS you’d have something like this:

.image-container {
    display: grid;
}


.image-one {
    grid-row: 1;
    grid-column: 1;
}

.image-two {
    grid-row: 1;
    grid-column: 1;
}

The final step is to add some sort of event listening depending on when you want the effect to be fired. In my opinion, hover effects are particularly good because they’re more nuanced in their response, frequently visible to the user but also unlikely to be distracting. At Glean we use React, so it’s simple enough to just add a useState hook with some conditional class names to achieve the desired effect. The resulting code would look something like this:

const [hovered, setHovered] = useState(false)

<div class=“image-container”>
    <img 
      class={ hovered ? “image-one–hovered” : “image-one”}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
    >
    <img 
      class={ hovered ? “image-two--hovered” : “image-two”} 
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
    >
</div>

All that’s left is to add transitioning effects. You’ll want some sort of opacity change, and with the right kind of image adding a bit of motion (e.g. with tilt) can help to enhance the illusion. Use keyframes and the animate property to put it into action, and you should be good to go. The finished result in the css will look something like the following - I also decided to have the animation loop back to its starting point:

.image-one {
  grid-column: 1;
  grid-row: 1;

  &--hovered {
    grid-column: 1;
    grid-row: 1;
    animation: tilt 1s ease-in-out;
  }
}

.image-two {
  grid-column: 1;
  grid-row: 1;
  opacity: 0;

  &--hovered {
    grid-column: 1;
    grid-row: 1;
    animation: fade 1s ease-in-out;
    opacity: 0;
  }
}

@keyframes tilt {
  0% {
    transform: rotate(0deg);
    opacity: 1;
  }

  50% {
    transform: rotate(-15deg);
    opacity: 0;
  }

  60% {
    transform: rotate(-15deg);
    opacity: 0;
  }

  100% {
    transform: rotate(0deg);
    opacity: 1;
  }
}

@keyframes fade {
  0% {
    opacity: 0;
  }

  50% {
    opacity: 1;
  }

  60% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

As mentioned before, there are limitations to this method and I wouldn’t say it’s objectively the best. Using useState can leave you vulnerable to bugs such as small visual discrepancies arising from rendering two different images. Also, you’re somewhat restricted in the degree to which you can animate, and this would probably be difficult to fine tune. However, I’d recommend it as a quick solution that gives you lucid code and doesn’t rely on manipulating SVGs.

Visit our Careers page
Time for a simpler, smarter note taking accommodation?

Time for a simpler, smarter note taking accommodation?

Glean is the online note taking tool that makes compliance simple, reduces cost and admin burden, and improves student outcomes.
Learn More