I’ve long found steps() a curious feature of the CSS animation specification. Rarely used in development and tricky to understand, I gave the feature fairly short shrift in my last book (Pro CSS3 Animation). However, steps() has a few perfect areas of application, and can be easily understood with the right analogies.

Discrete vs. Continuous Motion

By default, CSS transitions and animation create continuous motion between states. That is, if we position an element somewhere and move it somewhere else, the browser will make that motion a smooth as possible:

An example of default CSS animation motion

This is true no matter what the timing function of the animation, or even if the element isn’t moving; for example, changing the element’s background color over time exhibits the same behaviour:

An example of default CSS animation applied to color

To make the element pause during CSS animation, we must place two keyframes with the same information next to each other, separated only by time: you can see an example of this in my CSS slider code. But between keyframes that are different, the browser will go back to its smooth interpolation of the element.

Quo Vadis

In analog wristwatches, this is referred to as a mechanical motion: a smooth, continuous sweep, like the second hand of Rolex watch. The watch and clock faces you and I are probably more familiar with are driven by a quartz movement: a series of individual discrete ticks. How would we replicate that kind of motion in CSS?

Using steps()

By using steps() we can force a CSS animation to “tick”. The second hand of a watch is a good example: taking one minute to move around the watch dial, we can subdivide the motion of the second hand into 60 steps, which creates the example you see above.

@keyframes clocksweep {
	to { 
		transform: rotate(1turn);
#secondhand {
	animation: clocksweep 60s steps(60) infinite;

More generally, steps() is useful anytime you want an element to move suddenly between states, with no intervening interpolation: it divides the motion up into the number of defined steps. I will be demonstrating any more examples of using steps() in articles to come.

start and end

The optional start and end values define where these steps take place. Perhaps the values are best understood as beats in music: a value of end (the default) means that movement that steps() make is played “on the beat”: if you divide any span of time into 4 steps, motion will happen after a breath, and continue through to the end:

steps(4, end)

A value of start, on the other hand, is playing “before the beat”: in the same division of time, the first state will be taken immediately, with a pause at the end.

steps(4, start)

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.