Motion

animation-direction

Setting the direction of the animation.

Overview

ClassDeclarations
animation-direction:normalanimation-direction: normal;
animation-direction:reverseanimation-direction: reverse;
animation-direction:alternateanimation-direction: alternate;
animation-direction:alternate-reverseanimation-direction: alternate-reverse;
animation-direction:<value>,<…>animation-direction: <value>,<…>;

Basic usage

Play forward

Play each cycle from 24px to 96px. The one-second linear path repeats with the same direction.

Play forward
animation-direction: Play forward controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
index.css
@theme {  @keyframes layer-slide {    from { transform: translateX(24px); }    to { transform: translateX(96px); }  }}
<!-- normal --><div class="w:240px max-w:100%">  <p class="mb-sm font-mono font-xs text-muted">Base 0 · start 24 · end 96px</p>  <div aria-hidden="true"    class="h:56px p-xs outline:1px|dashed|var(--color-line-muted)">    <span id="target"      class="grid place-items:center h:40px w:48px             b:1px|solid|var(--color-blue-60) r-sm bg-blue-10             fg-blue-80 transform:none             animation-direction:normal             animation-duration:1s             animation-iteration-count:infinite             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>
Generated CSS
@layer utilities {  .animation-direction\:normal {    animation-direction: normal  }}

A new normal cycle jumps back to its starting keyframe. Direction does not change the number or duration of cycles.

Play backward

Play each cycle from 96px back to 24px by reversing the same keyframes.

Play backward
animation-direction: Play backward controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
index.css
@theme {  @keyframes layer-slide {    from { transform: translateX(24px); }    to { transform: translateX(96px); }  }}
<!-- reverse --><div class="w:240px max-w:100%">  <p class="mb-sm font-mono font-xs text-muted">Base 0 · start 24 · end 96px</p>  <div aria-hidden="true"    class="h:56px p-xs outline:1px|dashed|var(--color-line-muted)">    <span id="target"      class="grid place-items:center h:40px w:48px             b:1px|solid|var(--color-blue-60) r-sm bg-blue-10             fg-blue-80 transform:none             animation-direction:reverse             animation-duration:1s             animation-iteration-count:infinite             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>

Reverse also reverses easing. An ease-in curve read backward behaves like ease-out; this linear example keeps speed constant.

Play alternately

Play the first cycle forward, then the second backward. The direction changes at each one-second boundary.

Play alternately
animation-direction: Play alternately controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
index.css
@theme {  @keyframes layer-slide {    from { transform: translateX(24px); }    to { transform: translateX(96px); }  }}
<!-- alternate --><div class="w:240px max-w:100%">  <p class="mb-sm font-mono font-xs text-muted">Base 0 · start 24 · end 96px</p>  <div aria-hidden="true"    class="h:56px p-xs outline:1px|dashed|var(--color-line-muted)">    <span id="target"      class="grid place-items:center h:40px w:48px             b:1px|solid|var(--color-blue-60) r-sm bg-blue-10             fg-blue-80 transform:none             animation-direction:alternate             animation-duration:1s             animation-iteration-count:infinite             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>

Iterations are counted from one. Odd iterations go forward and even iterations go backward, so the marker returns without a jump between these endpoints.

Play alternately and reverse first

Start at 96px and play backward first, then forward on the next cycle.

Play alternately and reverse first
animation-direction: Play alternately and reverse first controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
index.css
@theme {  @keyframes layer-slide {    from { transform: translateX(24px); }    to { transform: translateX(96px); }  }}
<!-- alternate-reverse --><div class="w:240px max-w:100%">  <p class="mb-sm font-mono font-xs text-muted">Base 0 · start 24 · end 96px</p>  <div aria-hidden="true"    class="h:56px p-xs outline:1px|dashed|var(--color-line-muted)">    <span id="target"      class="grid place-items:center h:40px w:48px             b:1px|solid|var(--color-blue-60) r-sm bg-blue-10             fg-blue-80 transform:none             animation-direction:alternate-reverse             animation-duration:1s             animation-iteration-count:infinite             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>

Odd iterations run backward and even iterations run forward. The keyframes, duration and easing remain identical to the alternate example.

Fade out

Reverse the preset fade keyframes, which normally go from opacity 0 to 1. This one-shot example fades its decorative marker out over one second.

Fade out
animation-direction: Fade out controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
<!-- Fades out once --><div class="flex items-center gap-md p-sm">  <span id="target" aria-hidden="true"    class="block h:40px w:64px b:1px|solid|var(--color-blue-60) r-sm           bg-blue-10 opacity:1           animation-direction:reverse           animation-duration:1s           animation-fill-mode:forwards           animation-name:fade           animation-timing-function:linear           animation:none@print           animation-play-state:paused@reduce-motion"></span>  <span class="font-sm">Fades out once</span></div>

Forwards fill retains the last value of the completed reverse iteration: opacity 0. The visible message remains readable, and print removes the animation.

Apply conditionally

Resize before replaying. The path runs backward below sm and forward at sm, using a real viewport query.

Apply conditionally
animation-direction: Apply conditionally controls
Play opts into the actual CSS animation. Pause retains its time; Replay restarts even a finished one-shot. Readouts show native timeline time and progress, including delay and fill.
index.css
@theme {  @keyframes layer-slide {    from { transform: translateX(24px); }    to { transform: translateX(96px); }  }}
<!-- Reverse → normal at sm --><div class="w:240px max-w:100%">  <p class="mb-sm font-mono font-xs text-muted">Base 0 · start 24 · end 96px</p>  <div aria-hidden="true"    class="h:56px p-xs outline:1px|dashed|var(--color-line-muted)">    <span id="target"      class="grid place-items:center h:40px w:48px             b:1px|solid|var(--color-blue-60) r-sm bg-blue-10             fg-blue-80 transform:none             animation-direction:reverse             animation-duration:1s             animation-iteration-count:infinite             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion             animation-direction:normal@sm">→</span>  </div></div>

Direction changes reuse the existing timeline; they do not inherently restart it. Replay starts the comparison again from time zero.



© 2026 Aoyue Design LLC.MIT License
Trademark Policy