AD JB Toolbox 01 EN

CSS Tutorial / CSS Keyframe Animations, Fill Modes, and Pausing

CSS keyframe animations describe property values over an animation's timeline and can run without a hover or focus trigger. Duration, delay, iteration count, fill mode, and play state control different parts of that behavior.

Distinguish transitions and keyframe animations

standard transition animation
start condition When the value changes due to hover, focus, class change, etc. After the animation is applied and the delay time has passed,
state definition CSS values before and after change Specify start, middle, and end values in keyframes
repetition and direction Proceed with state changes Separately specify the number of times, direction, and stop status

Transition is simple as long as the background color of the button changes. You can look at animations for effects such as when a notification first appears or for a visual explanation that goes through several steps. Controlling the same property on the same element with two functions simultaneously makes it difficult to keep track of which value is applied, so for this exercise we split the roles. If the effects before and after the state change are sufficient, consider starting with a simple transition using the CSS Transforms and Transitions of the transition.

Define keyframes and playback settings

In the keyframe, from corresponds to 0%, and to corresponds to 100%. If you need an intermediate state, add a progress percentage like 50%. Just declaring a keyframe name will not cause it to play; you must associate it with the animation-name of the element.

@keyframes travel {
  0% { transform: translateX(0); }
  50% { transform: translateX(3rem); }
  100% { transform: translateX(6rem); }
}
.marker { animation: travel 4s linear 1s 1 normal forwards; }

This partial code is a shortened version of the keyframe and .marker setting of the same name in the full example below. 4s is the time it takes to play once and 1s is the startup delay. If the duration is not specified, the default value is 0 seconds, making it difficult to observe the moving process. You can reduce confusion about time and frequency by dividing it into longhand properties at first.

Property Values for this example Things to change
animation-duration 4s one playing time
animation-timing-function linear Difference between constant progress and ease curve
animation-delay 1s Waiting time before starting
animation-iteration-count 1 number of plays; infinite repeats infinitely
animation-direction normal Forward/reverse/alternate playback
animation-fill-mode forwards Value to apply during out-of-play time
animation-play-state Default running Pause current progress with paused

Even if the rates are written at equal intervals, the actual change in position will vary as a function of time. This exercise uses linear, which makes it easy to check intermediate positions. For keyframe creation and connection, refer to MDN @keyframes document.

Compare direction, iteration count, and fill mode

normal proceeds from the beginning to the end, and reverse proceeds from the end to the beginning. alternate plays alternately in forward direction the first time and reverse direction the next time. If the number of iterations is 2 and alternate, the last one will arrive at the first keyframe.

forwards is not a setting that always maintains 100% keyframes. Depending on the playback direction and number of times, the value of the last actually reached keyframe is maintained. In the example, once in the forward direction maintains the 6rem position, once in the reverse direction it maintains the 0 position, and twice in the alternate direction also maintains the 0 position. This description is based on an integer number of completed plays, a fractional number may end in mid-progress. MDN fill-mode description Conditions for each direction are summarized in.

fill-mode Delaying After finishing
none Basic CSS Basic CSS
forwards Basic CSS Last played keyframe
backwards Keyframe to play first Basic CSS
both Keyframe to play first Last played keyframe

If the base CSS value and end keyframe value are the same, the difference between none and forwards may be invisible. The movement box below has a default position of 1rem, a start of 0, and an end of 6rem to reveal the difference. On the other hand, the must-read guide defaults to the completion state that appears when the animation is removed.

Run and change one setting at a time

Save the following code as animation-lab.html in UTF-8 and open it in your browser. The upper guidance appears briefly, and the lower decorative box starts moving after 1 second and moves for 4 seconds. After modifying the values, save the file and reload the page to compare from the beginning.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Keyframes and end states exercise</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
.notice { padding: 1rem; border: 1px solid #90bdd8; border-radius: .5rem; background: #eaf4fa;
  opacity: 1; transform: none; animation: appear 400ms ease-out; }
@keyframes appear {
  from { opacity: 0; transform: translateY(.5rem); }
  to { opacity: 1; transform: translateY(0); }
}
.track { margin-block: 1rem; padding: 1rem; border: 2px dashed #8a9ba7; }
.marker { width: 2rem; height: 2rem; border-radius: .25rem; background: #166b92;
  transform: translateX(1rem);
  animation-name: travel; animation-duration: 4s; animation-timing-function: linear;
  animation-delay: 1s; animation-iteration-count: 1; animation-direction: normal;
  animation-fill-mode: forwards; }
@keyframes travel {
  0% { transform: translateX(0); background-color: #166b92; }
  50% { transform: translateX(3rem); background-color: #6a3989; }
  100% { transform: translateX(6rem); background-color: #28633b; }
}
#pause:checked ~ .track .marker { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .notice, .marker { animation: none; }
}
  </style>
</head>
<body>
<main>
<h1>Check entrance and movement</h1>
<p class="notice">This notice remains readable without animation.</p>
<h2>Comparing end states</h2>
<p>Movement starts one second after loading. Refresh the page to compare again.</p>
<input id="pause" type="checkbox"><label for="pause">Pause movement</label>
<div class="track"><div class="marker" aria-hidden="true"></div></div>
<p>The colors and movement are decorative. Essential information remains available independently of the animation, as in this sentence.</p>
</main>
</body>
</html>
  1. In the default settings, the box is at position 1rem during delay, moves to 0 at start, and remains at position 6rem when it ends. Check the difference between the delay value and the start value.
  2. Change only animation-fill-mode to none and refresh. Once finished, it returns to the default position of 1rem and the default blue color. Restore back to forwards.
  3. Change only fill-mode to both. Starting position 0 applies even during delays. After confirmation, return to forwards.
  4. Leave the count as 1 and change the direction to reverse. After completion, check that the 0 position remains and restore to normal.
  5. Check the results by changing the direction to alternate and keeping the count at 1. In that state, if you change the number to 2, a second playback will occur. After confirmation, the count is 1 and the direction is restored to normal.
  6. Immediately after refreshing, turn on the Pause checkbox and wait a moment. If you cancel it, you will continue from where you left off. This is different from the ability to replay from the beginning.

Keep content visible with reduced motion

The reduced setting removes the animation of the guidance and decoration boxes. The instructions are still readable as the default is opacity: 1 and the box appears at the default position of 1rem. Leaving readable state only on end keyframes or forwards can cause content to disappear in the absence of animation.

Enable reduced motion in the DevTools Rendering panel or operating system and reload. Both elements should remain still while their content stays visible. Consult the MDN prefers-reduced-motion guide, then restore the emulation setting after the comparison. Also follow the Check Responsive CSS with Width, Zoom, and Keyboard Tests to check clipping and usability with motion disabled.

Before adding infinite repetition, decide why motion is needed and how it will stop. This example uses finite iterations and supplies important information independently of movement and color. If nothing moves, check the animation name, duration, and reduced-motion condition. For an unexpected end state, compare direction, iteration count, fill mode, and the underlying CSS.

More in This Category
CSS Tutorial / CSS Grid Basics: Tracks, Lines, and Item Placement

CSS Tutorial / CSS Grid Basics: Tracks, Lines, and Item Placement

Create a two-dimensional grid, distinguish lines, tracks, cells, and areas, and use fr, minmax(), and spans to place items.

CSS Tutorial / CSS Width, Height, min/max Limits, and box-sizing

CSS Tutorial / CSS Width, Height, min/max Limits, and box-sizing

Compare content-box and border-box sizing, percentage heights, fluid width limits, and the consequences of fixed heights for growing text.

CSS Tutorial / CSS Grid: repeat, auto-fit, auto-fill, and Areas

CSS Tutorial / CSS Grid: repeat, auto-fit, auto-fill, and Areas

Compare auto-fit and auto-fill, test minimum track sizes and long text, and use named areas without confusing visual placement with reading order.

CSS Tutorial / Organize CSS with Shared Styles, Nesting, and Layers

CSS Tutorial / Organize CSS with Shared Styles, Nesting, and Layers

Refactor a small stylesheet without changing its layout, compare nesting and cascade layers, and check relative file paths and selector specificity.

CSS Tutorial / CSS display: Block, Inline, inline-block, and Hiding

CSS Tutorial / CSS display: Block, Inline, inline-block, and Hiding

Compare block and inline sizing, replaced elements such as images, and the layout and interaction differences between common hiding techniques.

CSS Tutorial / Build a Responsive Website with HTML and CSS

CSS Tutorial / Build a Responsive Website with HTML and CSS

Create a complete two-file learning website with responsive cards and navigation, then test long text, keyboard focus, zoom, and reduced motion.

CSS Tutorial / CSS position: relative, absolute, fixed, and sticky

CSS Tutorial / CSS position: relative, absolute, fixed, and sticky

Compare positioning modes in a complete example, identify containing blocks, and troubleshoot sticky limits and overlapping content.

CSS Tutorial / Fluid CSS Layouts with min, max, and clamp

CSS Tutorial / Fluid CSS Layouts with min, max, and clamp

Build a bounded fluid layout, compare flexible sizes and spacing, and check that longer content fits without introducing horizontal page scrolling.

CSS Tutorial / CSS Syntax: Selectors, Declarations, Comments, and Errors

CSS Tutorial / CSS Syntax: Selectors, Declarations, Comments, and Errors

Read and write CSS rules, use comments, and distinguish syntax errors from selectors that do not match and declarations overridden by other rules.

CSS Tutorial / CSS Paragraph and List Styling

CSS Tutorial / CSS Paragraph and List Styling

Style paragraphs and ordered or unordered lists, compare inside and outside markers, and check spacing and wrapped text on narrow screens.