Hey, I’m Eszter.

SVG animations with CSS

SVGs are lovely: they are small, scalable, and easily editable by a developer. They can also be animated using CSS, which means that while keeping their tiny size, they can do much better than a GIF. Need those champagne bubbles to bubble a little faster? No problem. Whatever the designer suggests can be done with a few lines of CSS, without significantly modifying the original asset.

The main ingredients: @keyframes and CSS animation

@keyframes are a way to define animation functions. They need a name, a few timing points (at least a from and a to, or percentages), and instructions on where to be at each of these time points.

CSS property animation takes name, duration, timing function (default: ease), delay, iteration count (default: 1; number or infinite) etc.

Take this beating heart as an example (it’s 11.15KB):

Heart icon by Kirill Kazachek from flaticon.com is licensed by CC 3.0 BY

Here’s the code itself — you can check the source, I’m not cheating ;)

HTML

<img src="heart.svg" class="heart" />

CSS

.heart {
  width: 120px;
  animation: pulse .6s ease-in infinite alternate;
}

@keyframes pulse {
  from {
    transform: scale(.94);
  }

  to {
    transform: scale(1);
  }
}

That’s boring. Is that all SVG can do?

Of course not.

SVGs are written in XML, and can contain paths, texts, shapes (lines, circles etc.) — and <g> for groups of these. We just have to name these building blocks (with CSS classes and IDs) to animate them separately.

Consider this image:

Cloud icon by Freepik from flaticon.com is licensed by CC 3.0 BY

Its source code has the following structure:

<svg>
  <!-- cloud in the back -->
  <path style="..." d="...">

  <!-- cloud in the front -->
  <path style="..." d="...">
  <path style="..." d="...">

  <g>
    <!-- cloud fluffs -->
    <path style="..." d="...">
    <path style="..." d="...">
    <path style="..." d="...">

    <!-- rain drops -->
    <path style="..." d="...">
    <path style="..." d="...">
    <path style="..." d="...">
    <!-- ...and many more of them -->
  </g>
</svg>

I would like the little rain drops to fall, and the two clouds to float.
For this, we will have to slightly modify the original SVG. Namely:

An illustration of these changes:

<svg>
  <!-- cloud in the back: id added -->
  <path id="cloud-back" style="fill:#8BbbD8;" d="...">

  <!-- cloud in the front: grouped -->
  <g id="cloud-front">
    <path style="..." d="...">
    <path style="..." d="...">

    <!-- cloud fluffs added to front cloud's group -->
    <path style="..." d="...">
    <path style="..." d="...">
    <path style="..." d="...">
  </g>

  <!-- rain drops are all individuals, but if we are lazy, we can "class"
  a few together. some will move at the same time, but it's fine -->
  <path class="drop1" style="..." d="...">
  <path class="drop2" style="..." d="...">
  <path class="drop3" style="..." d="...">
  <path class="drop2" style="..." d="...">
  <!-- and so forth, quasi-randomly assigning classes -->
</svg>

Now we just have to add some css animations, and we’re done.

#cloud-front {
  animation: float 3s ease-in-out infinite alternate;
}

#cloud-back {
  transform: translateX(12px);
  animation: float2 2.2s .2s ease-in-out infinite alternate;
}

.drop1 {
  animation: rainfall .5s linear infinite;
}

.drop2 {
  animation: rainfall .6s .1s linear infinite;
}

.drop3 {
  animation: rainfall .7s .3s linear infinite;
}

.drop4 {
  animation: rainfall .6s .4s linear infinite;
}

@keyframes float {
  from { transform: translateX(0);   }
  to   { transform: translateX(22px);}
}

@keyframes float2 {
  from { transform: translateX(14px);}
  to   { transform: translateX(0);   }
}

@keyframes rainfall {
  from {
    transform: translate(0, 0);
    opacity: 1;
  }

  to {
    transform: translate(-15px, 30px);
    opacity: .1;
  }
}
Cloud icon by Freepik from flaticon.com is licensed by CC 3.0 BY
 

That’s it — stay dry!

Previous: Flexbox with fixed sidebar
Next: