CSS steps() Function

The CSS steps() function divides an animation or transition into a specified number of discrete steps instead of producing continuous movement.

It is used with timing properties such as animation-timing-function and transition-timing-function. The function controls how many steps occur and whether changes happen at the beginning, end, or both boundaries of the timing interval.

Syntax

steps(number)
steps(number, position)

The steps() function requires a positive integer and accepts an optional jump position.

Examples:

steps(4)
steps(4, jump-start)
steps(4, jump-end)
steps(4, jump-none)
steps(4, jump-both)
steps(4, start)
steps(4, end)

When the position is omitted, jump-end is used.

For example, these declarations are equivalent:

animation-timing-function: steps(4);
animation-timing-function: steps(4, jump-end);
animation-timing-function: steps(4, end);

The function can also be used with the animation and transition shorthand properties:

animation: move 2s steps(4) infinite;
transition: width 1s steps(5, jump-end);

Unlike mathematical functions such as sqrt(), steps() returns a timing function rather than a numeric value.

Parameters

The steps() function accepts two parameters.

Parameter Description
number Required. A positive integer specifying the number of steps. At least 2 is required when using jump-none.
position Optional. Specifies where the jumps occur. Defaults to jump-end.

The number must be an integer greater than zero. Fractional, zero, and negative values are invalid.

Valid examples:

steps(1)
steps(3)
steps(5, jump-start)
steps(6, jump-none)
steps(8, jump-both)

Invalid examples:

steps(0)
steps(-2)
steps(2.5)
steps(1, jump-none)

The last example is invalid because jump-none requires at least two steps.

Jump Positions

The second argument controls where the timing function changes from one step to another.

Position Behavior
jump-start Includes a jump at the beginning of the interval and excludes a jump at the end.
jump-end Excludes a jump at the beginning and includes a jump at the end. This is the default.
jump-none Excludes jumps at both the beginning and end, holding the starting and ending values for part of the interval.
jump-both Includes jumps at both the beginning and end, creating an additional interval between jumps.
start Equivalent to jump-start.
end Equivalent to jump-end.

jump-start

animation-timing-function: steps(4, jump-start);

The animation makes its first jump immediately at the beginning of the interval.

jump-end

animation-timing-function: steps(4, jump-end);

The animation initially holds its starting value and makes its final jump at the end of the interval.

jump-none

animation-timing-function: steps(4, jump-none);

The starting and ending values are both held for a portion of the interval.

jump-both

animation-timing-function: steps(4, jump-both);

The function jumps at both boundaries of the interval, producing five equal-duration intervals for four steps.

How It Works

Most CSS animations use continuous timing functions such as ease or linear.

The steps() function instead holds a value for a period of time before changing it suddenly.

For example:

animation-timing-function: steps(4, jump-end);

With a two-second animation, the timing interval is divided into four half-second portions.

For an animation progressing from 0% to 100%, the values are:

Elapsed Time Progress
0 to before 0.5 seconds 0%
0.5 to before 1 second 25%
1 to before 1.5 seconds 50%
1.5 to before 2 seconds 75%
At 2 seconds 100%

These percentages represent the timing function's progress through the animated values, not necessarily the element's position on the page.

The exact behavior at animation boundaries also depends on properties such as animation-fill-mode, animation-direction, and animation-iteration-count.

step-start and step-end

CSS provides two predefined timing keywords that are equivalent to particular steps() expressions.

Keyword Equivalent Function
step-start steps(1, jump-start)
step-end steps(1, jump-end)

For example, these declarations are equivalent:

animation-timing-function: step-start;
animation-timing-function: steps(1, jump-start);

These are also equivalent:

animation-timing-function: step-end;
animation-timing-function: steps(1, jump-end);

Use the predefined keywords when only one step is needed. Use steps() when a specific number of steps or a different jump position is required.

Animation Timing

The animation-timing-function property determines how an animation progresses between keyframes.

For example:

.box {
  animation: move 2s steps(5, jump-end) infinite alternate;
}
@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(200px);
  }
}

The element moves through five discrete position changes rather than sliding continuously.

For a 200px translation, each step changes the horizontal position by 40px.

The alternate keyword reverses the animation direction on every other iteration.

When an animation contains multiple keyframe intervals, the timing function applies to the interpolation between the relevant keyframes rather than necessarily to the entire animation as one interval.

Transition Timing

The steps() function can also control how a CSS transition progresses.

For example:

.box {
  width: 100px;
  transition: width 1s steps(4, jump-end);
}
.box:hover {
  width: 200px;
}

When the box is hovered, its width changes in four discrete increments instead of expanding smoothly.

The same approach can be used with other animatable properties:

transition: opacity 1s steps(5);
transition: transform 2s steps(8, jump-start);
transition: background-color 1s steps(3);

Use stepped transitions when a design requires distinct changes rather than continuous interpolation.

Example

The following example compares a continuously moving box with one using steps(5).

HTML:

<div class="steps-demo">
  <div class="steps-track">
    <span>Linear</span>
    <div class="steps-box steps-linear"></div>
  </div>
  <div class="steps-track">
    <span>Steps</span>
    <div class="steps-box steps-discrete"></div>
  </div>
</div>

CSS:

.steps-demo {
  display: grid;
  gap: 15px;
}
.steps-track {
  position: relative;
  height: 65px;
  padding: 8px;
  background-color: #f1f3f5;
  overflow: hidden;
}
.steps-track span {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
}
.steps-box {
  width: 30px;
  height: 30px;
  background-color: #3498db;
}
.steps-linear {
  animation: steps-move 3s linear infinite alternate;
}
.steps-discrete {
  animation: steps-move 3s steps(5, jump-end) infinite alternate;
}
@keyframes steps-move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(200px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .steps-box {
    animation: none;
  }
}
Browser Result
Linear
Steps

The first box moves continuously. The second jumps between five position changes during each animation iteration.

Both use the same duration and distance, but their timing functions create different visual effects.

Jump Position Example

This example compares jump-start, jump-end, jump-none, and jump-both using the same animation.

HTML:

<div class="steps-jump-demo">
  <div class="steps-jump-row">
    <span>jump-start</span>
    <div class="steps-jump-box" style="--position: jump-start;"></div>
  </div>
  <div class="steps-jump-row">
    <span>jump-end</span>
    <div class="steps-jump-box" style="--position: jump-end;"></div>
  </div>
  <div class="steps-jump-row">
    <span>jump-none</span>
    <div class="steps-jump-box" style="--position: jump-none;"></div>
  </div>
  <div class="steps-jump-row">
    <span>jump-both</span>
    <div class="steps-jump-box" style="--position: jump-both;"></div>
  </div>
</div>

CSS:

.steps-jump-demo {
  display: grid;
  gap: 10px;
}
.steps-jump-row {
  height: 60px;
  padding: 5px 8px;
  background-color: #f1f3f5;
  overflow: hidden;
}
.steps-jump-row span {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
}
.steps-jump-box {
  width: 25px;
  height: 25px;
  background-color: #16a085;
  animation: steps-jump-move 4s steps(4, var(--position)) infinite;
}
@keyframes steps-jump-move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(200px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .steps-jump-box {
    animation: none;
  }
}
Browser Result
jump-start
jump-end
jump-none
jump-both

All four boxes use four steps and the same animation duration.

Their positions differ because each jump-position keyword distributes the changes differently across the interval.

With jump-none, the starting and ending values are held. With jump-both, the timing function includes jumps at both boundaries.

Step Counter Example

The following example uses a stepped animation to update a CSS counter displayed inside an element.

HTML:

<div class="steps-counter-demo">
  <span>Step:</span>
  <span class="steps-counter-value"></span>
</div>

CSS:

@property --step {
  syntax: "<integer>";
  inherits: false;
  initial-value: 0;
}
.steps-counter-demo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 20px;
}
.steps-counter-value {
  --step: 0;
  counter-reset: current-step var(--step);
  animation: steps-count 5s steps(5, jump-end) infinite;
}
.steps-counter-value::after {
  content: counter(current-step);
}
@keyframes steps-count {
  from {
    --step: 0;
  }
  to {
    --step: 5;
  }
}
@media (prefers-reduced-motion: reduce) {
  .steps-counter-value {
    animation: none;
  }
}
Browser Result
Step:

The registered custom property stores an integer that changes from 0 to 5 in five steps.

The counter-reset and counter() features display the current value.

This example also requires browser support for the @property rule and registered custom properties.

Sprite Animation

The steps() function is useful for sprite-sheet animations, where each frame occupies a fixed area of a larger image.

For example, consider a horizontal sprite sheet containing six frames, each 100px wide.

.sprite {
  width: 100px;
  height: 100px;
  background-image: url("sprite-sheet.png");
  background-repeat: no-repeat;
  animation: sprite-play 1s steps(6, jump-start) infinite;
}
@keyframes sprite-play {
  from {
    background-position: 0 0;
  }
  to {
    background-position: -600px 0;
  }
}

The animation changes the background position in 100px increments, displaying one frame at a time.

The jump-start position is used here with the endpoint values arranged so the six intended frames are displayed during the repeating animation.

The sprite image must be arranged to match the frame dimensions and background-position values.

This is a code example only. It requires an actual sprite-sheet image to display the animation.

Common Uses

The steps() function is useful whenever an animation or transition should change in discrete increments.

  • Sprite animations: Display individual frames from a sprite sheet without showing intermediate positions.
  • Frame-by-frame effects: Create animation that changes between distinct visual states.
  • Typing effects: Reveal text in increments, often using a width or character-based measurement.
  • Progress indicators: Show changes at fixed intervals rather than continuously.
  • Stepped movement: Move elements between distinct positions.
  • Discrete transitions: Change animatable property values in visible increments.
  • Custom timing: Control when jumps occur using the jump-position keywords.

For example, a progress indicator can change width in ten steps:

.progress-bar {
  width: 0;
  animation: progress-steps 5s steps(10, jump-end) forwards;
}
@keyframes progress-steps {
  from {
    width: 0;
  }
  to {
    width: 100%;
  }
}

The indicator advances in ten equal increments and retains its final width after the animation finishes.

Limitations

The steps() function has several requirements and behaviors to consider.

  • The first argument must be a positive integer.
  • The jump-none position requires at least two steps.
  • The optional second argument must be a supported jump-position keyword.
  • The default position is jump-end.
  • The function defines timing behavior, not a CSS length or numeric value.
  • It does not automatically create keyframes or animate an element.
  • It produces abrupt changes rather than smooth interpolation.
  • Jump positions affect whether the starting and ending values are held or changed immediately.
  • Multiple keyframe intervals may each use the timing function independently.
  • Very large step counts can make movement appear nearly continuous.
  • Rapid or repeated visual changes may be distracting for some users.

Valid and Invalid Values

Valid declarations:

animation-timing-function: steps(4);
animation-timing-function: steps(5, jump-start);
animation-timing-function: steps(3, jump-none);
transition-timing-function: steps(8, jump-both);

Invalid declarations:

animation-timing-function: steps(0);
animation-timing-function: steps(2.5);
animation-timing-function: steps(-3);
animation-timing-function: steps(1, jump-none);
animation-timing-function: steps(4, middle);

Accessibility

Stepped animations can create sudden or repeated visual changes. Avoid unnecessary flashing and rapid movement.

Use the prefers-reduced-motion media query to provide a less animated alternative when appropriate:

@media (prefers-reduced-motion: reduce) {
  .animated-item {
    animation: none;
    transition: none;
  }
}

When disabling an animation, make sure important information remains visible and accessible.

Browser Support

The CSS steps() timing function is Baseline Widely available and is supported by modern versions of Chrome, Edge, Firefox, Safari, and other major browsers.

The basic steps() function has been available for many years. The jump-none and jump-both keywords were introduced later, so older browsers may not recognize those positions.

For current compatibility information, visit Can I Use? and search for steps().

Specifications

The steps() function is defined in the CSS Easing Functions Level 1 specification.

The specification defines step counts, jump positions, and the timing behavior of step easing functions.

Related timing functions include linear() and cubic-bezier(), as well as the predefined step-start and step-end keywords.