Motion

animation-timing-function

Controlling progress between animation keyframes.

Overview

ClassDeclarations
animation-timing-function-smoothanimation-timing-function: var(--easing-smooth);
animation-timing-function-softanimation-timing-function: var(--easing-soft);
animation-timing-function-crispanimation-timing-function: var(--easing-crisp);
animation-timing-function-snapanimation-timing-function: var(--easing-snap);
animation-timing-function-accelerateanimation-timing-function: var(--easing-accelerate);
animation-timing-function-decelerateanimation-timing-function: var(--easing-decelerate);
animation-timing-function-overshootanimation-timing-function: var(--easing-overshoot);
animation-timing-function-rewindanimation-timing-function: var(--easing-rewind);
animation-timing-function-springanimation-timing-function: var(--easing-spring);
animation-timing-function:easeanimation-timing-function: ease;
animation-timing-function:ease-inanimation-timing-function: ease-in;
animation-timing-function:ease-outanimation-timing-function: ease-out;
animation-timing-function:ease-in-outanimation-timing-function: ease-in-out;
animation-timing-function:linearanimation-timing-function: linear;
animation-timing-function:step-startanimation-timing-function: step-start;
animation-timing-function:step-endanimation-timing-function: step-end;
animation-timing-function:<function>,<…>animation-timing-function: <function>,<…>;

Examples

Set keyframe easing

Compare linear, ease-in and ease-out over the same one-second path. Replay starts all three native timelines together.

Set keyframe easing
animation-timing-function: Set keyframe easing 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); }  }}
<!-- Linear · even progress --><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>
<!-- Ease-in · slower start --><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:ease-in             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>
<!-- Ease-out · slower finish --><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:ease-out             animation:none@print             animation-play-state:paused@reduce-motion">→</span>  </div></div>
Generated CSS
@layer utilities {  .animation-timing-function\:ease-out {    animation-timing-function: ease-out  }}

An easing function redistributes progress between keyframe stops. It does not change the declared duration or endpoints.

Use linear timing for rotation

Compare a constant-speed turn with ease-in-out. Both have a two-second duration and infinite count.

Use linear timing for rotation
animation-timing-function: Use linear timing for rotation 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.
<!-- Linear · steady turn --><div class="flex items-center gap-md p-sm">  <svg id="target" aria-hidden="true" width="48" height="48" viewBox="0 0 48 48"    fill="none" stroke="currentColor" stroke-width="2"    class="text-blue animation-duration:2s           animation-iteration-count:infinite           animation-name:rotate           animation-timing-function:linear           animation:none@print           animation-play-state:paused@reduce-motion">    <circle cx="24" cy="24" r="17" opacity=".25" />    <path d="M24 8v16h12 M19 13l5-5 5 5" />  </svg>  <span class="font-sm">Linear · steady turn</span></div>
<!-- Ease-in-out · changing speed --><div class="flex items-center gap-md p-sm">  <svg id="target" aria-hidden="true" width="48" height="48" viewBox="0 0 48 48"    fill="none" stroke="currentColor" stroke-width="2"    class="text-blue animation-duration:2s           animation-iteration-count:infinite           animation-name:rotate           animation-timing-function:ease-in-out           animation:none@print           animation-play-state:paused@reduce-motion">    <circle cx="24" cy="24" r="17" opacity=".25" />    <path d="M24 8v16h12 M19 13l5-5 5 5" />  </svg>  <span class="font-sm">Ease-in-out · changing speed</span></div>

The linear hand maintains angular speed across cycles. Ease-in-out slows near each cycle boundary, even though a full turn ends at the same visual orientation.

Match easing to intent

For a deliberately stepped movement, place a timing function on the starting keyframe. It controls the interval leading to the next keyframe.

Match easing to intent
animation-timing-function: Match easing to intent 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); animation-timing-function:steps(4,end); }    to { transform: translateX(96px); }  }}
<!-- Keyframe easing · four 18px steps --><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>

The element declares linear, but the keyframe’s steps(4,end) overrides it for this interval. Easing on the final 100% keyframe has no following interval to affect.

Apply conditionally

Resize before replaying to switch from ease-out to linear at sm. The one-second duration and 72px path remain unchanged.

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

The native query changes the element’s easing. A keyframe-level easing would take precedence for its own interval; these keyframes contain no such override.



© 2026 Aoyue Design LLC.MIT License
Trademark Policy