Highly Efficient CSS Animations with Sass/SCSS 3.2

Snippet CSS Sass Animation

Last updated Apr 21, 2016

I was recently involved in a project that required extensive legacy browser support for CSS animations and SVG graphics. Writing basic keyframe animations in CSS results in a large pile of code, but adding in vender prefixes (-moz-, -webkit-, etc.) will make your project completely unmaintainable. Thankfully, Sass gives us the tools needed to keep our animations DRY while maintaining code readability. In this lesson, I am going to share some of the tricks I used with SASS 3.2 to keep our complex CSS animations organized.


Lesson Checkpoints

1. A Cross-Browser Keyframe Mixin

CSS Animations are still a frustrating area when it comes to cross browser compatibility. As of 2016, it is still best practice to add the vendor prefix for keyframe definitions, as well as certain properties, such as transform. Fortunately, we can use Sass to avoid writing these vendor prefixes over and over again.


@mixin animation($name) {
    @-webkit-keyframes #{$name} {
    @-moz-keyframes #{$name} {
    @keyframes #{$name} {

Are you familiar with the @content code? The @content directive is an awesome feature added in Sass 3.2. It allows you to pass anything to the mixin, which we will put to use in the next step. Not only is this great for keyframes, but many developers use it for CSS media queries.

Also, the $name variable needs to be interpolated into the name by surrounding it with #{ } hash brackets. Otherwise, its value will not be evaluated and be treated like a plain old string.

2. How to Pass any @content to a Mixin

Now that we have our keyframes ready, we need to pass it some properties for the animation. One of the coolest features of Sass 3.2 is the ability pass @content to a mixin. This makes it possible to send an entire block of mixed content, rather than a single string, integer, or list. Because keyframe animations are structured like nested [key, value] pairs, it becomes extremely complicated to build Mixins with them without the @content feature.

At this point, we could manually pass the animation code to the mixin like so:

  @include keyframes(example) {
    0% {
      transform: scale(0,0);
    100% {
      transform: scale(1,1);

The compiled CSS will include these keyframes with each of the required vendor prefixes.

3. Interpolating CSS Property Names

The main problem at this juncture is that we also need to add vendor prefixes to individual CSS properties inside of the keyframes, such as transform. Our code will become very verbose as our animations grow in complexity. Keep in mind, not all properties need to be prefixed so check out ShouldIPrefix for the most up to date conventions. Let’s create another mixin to handle properties that require prefixes.

@mixin prefix-property($name, $value) {
  -webkit-#{$name}: $value;
     -moz-#{$name}: $value;
       -o-#{$name}: $value;
          #{$name}: $value;

We can interpolate the CSS property name to give us a reusable mixin for any propery that requires the prefixes.

4. Using Mixins inside of Mixins

This is where the Sass @content feature gets really awesome. It allows us to nest mixins inside of other mixins.


  @include keyframes(example) {
    0% {
      @include prefix-property(transform, scale(0,0));
    100% {
      @include prefix-property(transform, scale(1,1));

With just 8 lines of readable Sass, we are able to knock out about 40+ lines of plain CSS.

5. One Final Mixin for the Animation Class

The only thing were missing at this point is a class that defines the animation parameters. Again, we’re dealing with vendor prefixes. In this case, our mixin is going to have a bunch of default values that we can override as needed. The only required value in this example is the $name, everything else will inherit the default. Tip: You can shorten this mixin by removing properties you don’t frequently use, such as play-state or fill-mode. I went ahead and included them all just so you’re aware of the available properties.

@mixin animation($name,
                 $duration: 3s,
                 $iteration-count: 1,
                 $direction: normal,
                 $fill-mode: none,
                 $play-state: running) {
  -webkit-animation: $name $duration $timing-function $delay $iteration-count $direction $fill-mode $play-state;
  -moz-animation: $name $duration $timing-function $delay $iteration-count $direction $fill-mode $play-state;
  animation: $name $duration $timing-function $delay $iteration-count $direction $fill-mode $play-state;

Now you can create the animation class by just passing the name of the keyframes to the Mixin.


.exampleAnimation {
  @include animation(example);

If you want to override the default variables, you can pass them to the mixin like so:

.exampleAnimation-Fast-Loop {
  @include animation(example, $duration: 0.5s, $iteration-count: infinite);