loading...

Reusable @keyframes with CSS custom properties

j3nnning profile image Jenning Ho ・Updated on ・3 min read

In this article, I will share with you an application of CSS custom properties that I have found useful.

If you're like me, who like to cramp as many animations in the page as possible for micro interactions sake (just kidding, don't do that), you would be writing a lot of CSS animations with @keyframes.

@keyframes is notorious for its inflexibility, once it is declared, the values are then set in stone. If you have a set of @keyframes with the same behavior but different values, you'll need to make another set. This result in a lot of @keyframes that are similar but with a little variations in the numbers. Let’s take a look at the example below:

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

  100% {
    opacity: 1;
  }
}

@keyframes fade-in-a-little {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 0.3;
  }
}

.item-1 {
  animation: fade-in 300ms ease;
}

.item-2 {
  animation: fade-in-a-little 300ms ease;
}

In the example above, we have 2 items with 2 different animations that are similar, with only slight difference in the opacity value at 100%.

Now imagine what this would turn into when applied to more items. As the website scale up, what you'll end up with is 100s of similar @keyframes, bloating up the CSS file. We can't have that. 😐

CSS custom properties to the rescue

CSS custom properties (also known as CSS variables), are most widely applied for theming. It can also be used to make @keyframes reusable! (If you want to learn about CSS custom properties, see here)

Let's refactor our previous example with CSS custom properties!

@keyframes fade {
  0% {
    opacity: var(--fade-start, 0);
  }

  100% {
    opacity: var(--fade-end, 1);
  }
}

.item-1 {
  animation: fade 300ms ease;
}

.item-2 {
  --fade-end: 0.3;
  animation: fade 300ms ease;
}

fade-in-a-little @keyframes is taken out as it is not needed anymore. We now have only 1 set of @keyframes fade, and apply CSS custom properties to the starting opacity value and the ending opacity value with:

opacity: var(--fade-start, 0); // start

opacity: var(--fade-end, 1); // end

In this example, we have 2 custom properties: --fade-start and --fade-end. These variables are applied using var() function (read more about it here). The var() function accepts 2 parameters, a value and a fallback (default value).

So with this line opacity: var(--fade-start, 0);, if --fade-start is not set, 0 will be applied instead. The same goes to --fade-end.

The end result we get here is a set of @keyframes that can fade between opacity: 0 and opacity: 1 by default and be tuned within the scope of a CSS selector. 🀯

Wait... so to fade out? Yes! Instead of declaring another set of @keyframes for fade-out, you can reuse fade and set --fade-start as 1, and --fade-end to 0, like so:

.item-to-fade-out {
  --fade-start: 1;
  --fade-end: 0;
  animation: fade 300ms ease;
}

With CSS custom properties, @keyframes can now be function like, where the variable parts can be defined at a later stage, making @keyframes reusable. Yay DRY ❀️️

Time to bust out the Codepen

One @keyframes to fade it all

Summary

Fading animation is one of the most common animation on the web, now with CSS custom properties we can reduce our CSS bloat significantly while having as many variations of it as we'd like.

Consider applying this to @keyframes that you find yourself repeating a lot with many little variations.

Hope you'll find this useful!

Discussion

pic
Editor guide
Collapse
giorgosk profile image
Giorgos Kontopoulos πŸ‘€

@j3nnning thanks for the post, nicely laid out. CSS custom properties (variables) are amazing.

One pain point I usually face is when there is a need for IE11 support (quite common) and appropriate polyfill has to be applied stackoverflow.com/questions/464299...

Collapse
j3nnning profile image
Jenning Ho Author

Great point. If your project requires support for IE11 and below, I would suggest refrain from implementing CSS custom properties on critical features and only leave it for fluff, things that are ok to break.

In the accepted answer from the link that you've provided (great link btw), it only works with custom properties in the root level. In the context of reusable keyframes, it would defeat the purpose because you want to be able to set the value at the selector level.

As a fallback, you can simply write another line with absolute value before the var() line, so any browser that aren't supporting var() will fallback to it. i.e.

  @keyframes fade {
    0% {
      opacity: 0;
      opacity: var(--fade-start, 0);
    }

    100% {
      opacity: 1;
      opacity: var(--fade-end, 1);
    }
  }

Any browser where var() isn't supported would simply fade from 0 to 1, your animation would not break completely, imo that would be a good enough fallback.

Collapse
waylonwalker profile image
Waylon Walker

Love the code pen addition!

Collapse
imcheesecake profile image
Freddie Runnding

This is amazing! Thank you!

Collapse
pflash profile image
Precious adeyinka

Thank you for this, I never thought of it!