Motion

animation-fill-mode

Setting how a CSS animation applies styles to its target before and after its execution.

Overview

ClassDeclarations
animation-fill-mode:noneanimation-fill-mode: none;
animation-fill-mode:forwardsanimation-fill-mode: forwards;
animation-fill-mode:backwardsanimation-fill-mode: backwards;
animation-fill-mode:bothanimation-fill-mode: both;
animation-fill-mode:<mode>,<…>animation-fill-mode: <mode>,<…>;

Examples

Keep the final keyframe

Compare no fill with forwards fill after a one-second movement. The ordinary position is 0px; the keyframes run from 24px to 96px.

Keep the final keyframe
animation-fill-mode: Keep the final keyframe 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); }  }}
<!-- None · returns to base --><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-duration:1s             animation-fill-mode:none             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>
<!-- Forwards · retains end --><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-duration:1s             animation-fill-mode:forwards             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-fill-mode\:forwards {    animation-fill-mode: forwards  }}

Forwards retains the value where the final iteration ends. Direction and a fractional iteration count can change that value; it is not always the 100% keyframe.

Apply both before and after states

Both specimens wait 600ms. Backwards fill applies the 24px starting keyframe during that delay; both also retains the 96px ending keyframe after completion.

Apply both before and after states
animation-fill-mode: Apply both before and after states 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); }  }}
<!-- Backwards · starting state only --><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-delay:600ms animation-duration:1s             animation-fill-mode:backwards             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>
<!-- Both · starting and ending states --><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-delay:600ms animation-duration:1s             animation-fill-mode:both             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>

The starting keyframe depends on direction. For reverse and alternate-reverse, backwards fill uses the end of the keyframe list during the delay.

Reset fill behavior

Use none to return to ordinary styling during the 600ms delay and after the one-second active interval.

Reset fill behavior
animation-fill-mode: Reset fill behavior 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); }  }}
<!-- None · base before and after --><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-delay:600ms animation-duration:1s             animation-fill-mode:none             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>

The marker waits at 0px, jumps to its 24px starting keyframe when active, moves to 96px, then returns to 0px. Fill mode does not edit the underlying transform.

Apply conditionally

Resize before replaying. Below sm, both retains the starting and ending states; at sm, none leaves ordinary styling outside the active interval.

Apply conditionally
animation-fill-mode: 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); }  }}
<!-- Both → none 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-delay:600ms animation-duration:1s             animation-fill-mode:both             animation-name:layer-slide             animation-timing-function:linear             animation:none@print             animation-play-state:paused@reduce-motion             animation-fill-mode:none@sm">→</span>  </div></div>

Changing fill mode can update an already-finished animation’s visible result. Print removes the animation completely rather than retaining a motion snapshot.



© 2026 Aoyue Design LLC.MIT License
Trademark Policy