I found myself in the position of wanting to recreate an animated gif (that’s pronounced “jif,” you miscreants) in CSS. Things did not go as planned.

The gif in question merely displayed text, but it faded different phrases in and out. Kind of like this (found in Google Image Search by search for “text fading gif”):

FinalResult

I thought to myself, Self, we can rebuild this in CSS. We have the technology! I was partially right.

I did a search on CodePen for animated text, and came across a pen that accomplished what I wanted to do via a jQuery plugin. I forked the pen and that was that.

See the Pen jQuery Text Rotator by Paul (@peiche) on CodePen.0

I decided that I wanted to try my hand at duplicating the animation in pure CSS, though. I figured keyframes would be perfect for this. And so this was born:

See the Pen CSS only rotating text by Paul (@peiche) on CodePen.0

Defining the animation duration and delay would have been more of a hassle without having access to Sass’s @for directive. As it turned out, the hardest part was figuring out the math required for the total duration of the animation (which must account for all elements being animated, in this case five) and the delay for each element. You don’t want everything appearing at once, so of course each subsequent element must be delayed until the previous element has been faded out.

$elements: 5;
$seconds_per_element: 5;

#rotate span {
  @for $i from 0 through ($elements - 1) {
    &:nth-child(#{$i + 1}) {
      $var: ($elements * $i);
      animation: quoteshift ($elements * $seconds_per_element)+s $var+s infinite linear;
    }
  }
}

It compiles to this (vendor prefixes excluded):

#rotate span:nth-child(1) {
  animation: quoteshift 25s 0s infinite linear;
}
#rotate span:nth-child(2) {
  animation: quoteshift 25s 5s infinite linear;
}
#rotate span:nth-child(3) {
  animation: quoteshift 25s 10s infinite linear;
}
#rotate span:nth-child(4) {
  animation: quoteshift 25s 15s infinite linear;
}
#rotate span:nth-child(5) {
  animation: quoteshift 25s 20s infinite linear;
}

Overall, I’m happy with how the animation turned out. But the problem here is how brittle the whole thing is. The keyframes depend on stepping through the animation cycle based on percentages; meanwhile, the element’s style has to define both the total duration and delay. So if I add elements and want to keep the same duration for each element, then the total duration has to change, as well as the percentage distribution in the keyframe (which is currently configured to give roughly five seconds of display time to each phrase):

@keyframes "quoteshift" {
  0% {
    opacity: 0;
  }
  1% {
    opacity: 1;
  }
  18% {
    opacity: 1;
  }
  19% {
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}

We can actually make this a little more dynamic, based on the number of elements. (We’ll subtract two from the last visible stage so that we have enough time to transition to the next element.)

@keyframes "quoteshift" {
  0% {
    opacity: 0;
  }
  1% {
    opacity: 1;
  }
  #{$percentage - 2}% {
    opacity: 1;
  }
  #{$percentage - 1}% {
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}

Maintaining the variable for the number of elements simplifies this a bit; now we only have to change one place instead of several if we add or remove elements in the markup. But the fact remains, all this still needs to be recompiled into a stylesheet and redeployed. I’m not a big fan of dynamic preprocessor compilation, which requires JavaScript (thus defeating the point of a CSS-only solution). No modern browser yet has any support for interpreting preprocessor languages at runtime, though a boy can dream.

The JavaScript solution, on the other hand, is clearly more versatile; I can add or remove elements in the markup without having to worry about updating the script. I can’t say that for the CSS-only solution. (Any suggestions or improvements are welcome, of course. Feel free to fork the pen.)

Husband. Daddy. Programmer. Artist. I'm not an expert, I just play one in real life.