Swapping State with CSS Keyframes

Avatar of Chris Coyier
Chris Coyier on

Say you want an element to be in one state for 9 seconds, and in another state for 1 second, on a loop.

No tweening between the state, just a straight swap.

I was wondering how to go about this other day, and Sarah Drasner showed me that you can use reallllllly short distances between keyframes to move from one state to another. Like 59.999% to 60%. No opportunity to tween there on a normal animation duration like 10 seconds.

See the Pen State Change with Quick Keyframe Change by Chris Coyier (@chriscoyier) on CodePen.

Perhaps a little bit cleaner, you don’t even have to get tricky with the .999 stuff if you use steps, like:

div {
  ...
  animation: color 10s steps(10) infinite both;
}

Single keyframe / Single step changer

That led me to stumble into another super weird CSS thing. You’d think if you used steps(1) that no change at all would occur, right? Since there is only one step? Not true, there is actually 2 steps when you use steps(1), for who-knows-what reasoning. We can use that.

Say we were going for the original premise of this article: one state for 9 seconds, and in another state for 1 second. Like this:

1-1-1-1-1-1-1-1-1-2

You could do:

div {
  ...
  background: orangered;
  animation: color 10s steps(1) infinite both;
}

@keyframes color {
  90% {
    background: teal;
  }
}

That div will only be teal for 1 second! Then switch back to orangered. Change that keyframe to 70% and you’d get:

1-1-1-1-1-1-1-2-2-2

Change it to 10% and you’d get:

1-2-2-2-2-2-2-2-2-2

See the Pen 2efd2dc514da5485073acd6f4b9f3dab by Chris Coyier (@chriscoyier) on CodePen.

Change state and “stay there”

We have a pretty cool trick in hand now for changing state in a timed loop. If you didn’t want the looping part, you could have the animation run once and stay there like:

div {
  ...
  background: orangered;
  animation: color 10s steps(1) forwards;
}

@keyframes color {
  90% {
    background: teal;
  }
}

All we’ve done here is removed the infinite keyword which was making it repeat, and used forwards meaning “when it gets to the end, keep the styles in that final state”. (The both keyword will do that too.)

But how do you swap from one state to another from a user interaction? Even this is possible! Imagine an animation with a super long duration (like, days and days). You could tie a user interaction to jumping around positions (state) within that. Here’s a simple demo of that where the clicking of a link triggers a :target state, which triggers an animation to jump to a position within keyframes that style that state:

See the Pen Keyframe State Changer by Chris Coyier (@chriscoyier) on CodePen.

Just at tricks, folks. We’ll be here all decade.