CSS Animations Module Level 1

Editor’s Draft,

More details about this document
This version:
https://drafts.csswg.org/css-animations/
Latest published version:
https://www.w3.org/TR/css-animations-1/
Previous Versions:
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
(Apple Inc.)
L. David Baron (Google)
Tab Atkins Jr. (Google)
(Invited Expert)
Former Editors:
David Hyatt (Apple Inc.)
Chris Marrin (Apple Inc.)
(Adobe)
Suggest an Edit for this Spec:
GitHub Editor
Issues List:
https://github.com/w3c/csswg-drafts/labels/css-animations-1

Abstract

This CSS module describes a way for authors to animate the values of CSS properties over time, using keyframes. The behavior of these keyframe animations can be controlled by specifying their duration, number of repeats, and repeating behavior.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-animations” in the title, like this: “[css-animations] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list www-style@w3.org.

This document is governed by the 18 August 2025 W3C Process Document.

1. Introduction

This section is not normative

CSS Transitions [CSS3-TRANSITIONS] provide a way to interpolate CSS property values when they change as a result of underlying property changes. This provides an easy way to do simple animation, but the start and end states of the animation are controlled by the existing property values, and transitions provide little control to the author on how the animation progresses.

This proposal introduces defined animations, in which the author can specify the changes in CSS properties over time as a set of keyframes. Animations are similar to transitions in that they change the presentational value of CSS properties over time. The principal difference is that while transitions trigger implicitly when property values change, animations are explicitly executed when the animation properties are applied. Because of this, animations require explicit values for the properties being animated. These values are specified using animation keyframes, described below.

Many aspects of the animation can be controlled, including how many times the animation iterates, whether or not it alternates between the begin and end values, and whether or not the animation should be running or paused. An animation can also delay its start time.

1.1. Value Definitions

This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3]. Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3]. Combination with other CSS modules may expand the definitions of these value types.

In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.

2. CSS Animations Model

CSS Animations affect computed property values. This effect happens by adding a specified value to the CSS cascade ([CSS3CASCADE]) (at the level for CSS Animations) that will produce the correct computed value for the current state of the animation. As defined in [CSS3CASCADE], animations override all normal rules, but are overridden by !important rules.

If at some point in time there are multiple animations specifying behavior for the same property, the animation which occurs last in the value of animation-name will override the other animations at that point.

An animation does not affect the computed value before the application of the animation (that is, when the animation-name property is set on an element) or after it is removed. Furthermore, typically an animation does not affect the computed value before the animation delay has expired or after the end of the animation, but may do so depending on the animation-fill-mode property.

While running, the animation computes the value of those properties it animates. Other values may take precedence over the animated value according to the CSS cascade ([CSS3CASCADE]).

While an animation is applied but has not finished, or has finished but has an animation-fill-mode of forwards or both, the user agent must act as if the will-change property ([css-will-change-1]) on the element additionally includes all the properties animated by the animation.

The start time of an animation is the time at which the style applying the animation and the corresponding @keyframes rule are both resolved. If an animation is specified for an element but the corresponding @keyframes rule does not yet exist, the animation cannot start; the animation will start from the beginning as soon as a matching @keyframes rule can be resolved. An animation specified by dynamically modifying the element’s style will start when this style is resolved; that may be immediately in the case of a pseudo style rule such as hover, or may be when the scripting engine returns control to the browser (in the case of style applied by script). Note that dynamically updating keyframe style rules does not start or re-start an animation.

An animation applies to an element if its name appears as one of the identifiers in the computed value of the animation-name property and the animation uses a valid @keyframes rule. Once an animation has started it continues until it ends or the animation-name is removed. Changes to the values of animation properties while the animation is running apply as if the animation had those values from when it began. For example, shortening the animation-delay may cause the animation to jump forwards or even finish immediately and dispatch an animationend event. Conversely, extending the animation-delay may cause an animation to re-start and dispatch an animationstart event.

The same @keyframes rule name may be repeated within an animation-name. Changes to the animation-name update existing animations by iterating over the new list of animations from last to first, and, for each animation, finding the last matching animation in the list of existing animations. If a match is found, the existing animation is updated using the animation properties corresponding to its position in the new list of animations, whilst maintaining its current playback time as described above. The matching animation is removed from the existing list of animations such that it will not match twice. If a match is not found, a new animation is created. As a result, updating animation-name from ‘a’ to ‘a, a’ will cause the existing animation for ‘a’ to become the second animation in the list and a new animation will be created for the first item in the list.

div {
  animation-name: diagonal-slide;
  animation-duration: 5s;
  animation-iteration-count: 10;
}

@keyframes diagonal-slide {

  from {
    left: 0;
    top: 0;
  }

  to {
    left: 100px;
    top: 100px;
  }

}

This will produce an animation that moves an element from (0, 0) to (100px, 100px) over five seconds and repeats itself nine times (for a total of ten iterations).

Setting the display property to none will terminate any running animation applied to the element and its descendants. If an element has a display of none, updating display to a value other than none will start all animations applied to the element by the animation-name property, as well as all animations applied to descendants with display other than none.

While authors can use animations to create dynamically changing content, dynamically changing content can lead to seizures in some users. For information on how to avoid content that can lead to seizures, see Guideline 2.3: Seizures: Do not design content in a way that is known to cause seizures ([WCAG20]).

Implementations may ignore animations when the rendering medium is not interactive e.g. when printed. A future version of this specification may define how to render animations for these media.

3. Declaring Keyframes

Keyframes are used to specify the values for the animating properties at various points during the animation. The keyframes specify the behavior of one cycle of the animation; the animation may iterate zero or more times.

Keyframes are specified using the @keyframes at-rule, defined as follows:

@keyframes = @keyframes <keyframes-name> { <qualified-rule-list> }

<keyframes-name> = <custom-ident> | <string>

<keyframe-block> = <keyframe-selector># { <declaration-list> }

<keyframe-selector> = from | to | <percentage [0,100]>

The <rule-list> inside of @keyframes can only contain <keyframe-block> rules.

The <declaration-list> inside of <keyframe-block> accepts any CSS property except those defined in this specification, but does accept the animation-timing-function property and interprets it specially. None of the properties interact with the cascade (so using !important on them is invalid and will cause the property to be ignored).

A @keyframes block has a name given by the <custom-ident> or <string> in its prelude. The two syntaxes are equivalent in functionality; the name is the value of the ident or string. As normal for <custom-ident>s and <string>s, the names are fully case-sensitive; two names are equal only if they are codepoint-by-codepoint equal.

The <custom-ident> additionally excludes the none keyword. The <string> additionally excludes the empty string (but allows the string "none" and other excluded keywords). When serialized, the value is serialized as an <ident> unless it’s a disallowed keyword, in which case it’s serialized as a <string>.
For example, the following two @keyframes rules have the same name, so the first will be ignored:
@keyframes foo { /* ... */ }
@keyframes "foo" { /* ... */ }

On the other hand, the following @keyframes rule’s name is different from the previous two rules:

@keyframes FOO { /* ... */ }

The following @keyframes rules are invalid because they use disallowed <custom-ident> values:

@keyframes initial { /* ... */ }
@keyframes None { /* ... */ }

However, those names can be specified with a <string>, so the following are both valid:

@keyframes "initial" { /* ... */ }
@keyframes "None" { /* ... */ }

The <keyframe-selector> for a <keyframe-block> consists of a comma-separated list of percentage values or the keywords from or to. The selector is used to specify the percentage along the duration of the animation that the keyframe represents. The keyframe itself is specified by the block of property values declared on the selector. The keyword from is equivalent to the value 0%. The keyword to is equivalent to the value 100%. Values less than 0% or higher than 100% are invalid and cause their <keyframe-block> to be ignored.

Note: Note that the percentage unit specifier must be used on percentage values. Therefore, 0 is an invalid keyframe selector.

If a 0% or from keyframe is not specified, then the user agent constructs a 0% keyframe using the computed values of the properties being animated. If a 100% or to keyframe is not specified, then the user agent constructs a 100% keyframe using the computed values of the properties being animated.

The <keyframe-block> contains properties and values. The properties defined by this specification are ignored in these rules, with the exception of animation-timing-function, the behavior of which is described below. In addition, properties qualified with !important are invalid and ignored.

If multiple @keyframes rules are defined with the same name, the last one in document order wins, and all preceding ones are ignored.

div {
  animation-name: slide-right;
  animation-duration: 2s;
}

@keyframes slide-right {

  from {
    margin-left: 0px;
  }

  50% {
    margin-left: 110px;
    opacity: 1;
  }

  50% {
    opacity: 0.9;
  }

  to {
    margin-left: 200px;
  }

}

The two 50% rules from above can also be combined into an equivalent single rule as illustrated below:

@keyframes slide-right {

  from {
    margin-left: 0px;
  }

  50% {
    margin-left: 110px;
    opacity: 0.9;
  }

  to {
    margin-left: 200px;
  }

}

To determine the set of keyframes, all of the values in the selectors are sorted in increasing order by time. The rules within the @keyframes rule then cascade; the properties of a keyframe may thus derive from more than one @keyframes rule with the same selector value.

If a property is not specified for a keyframe, or is specified but invalid, the animation of that property proceeds as if that keyframe did not exist. Conceptually, it is as if a set of keyframes is constructed for each property that is present in any of the keyframes, and an animation is run independently for each property.

@keyframes wobble {
  0% {
    left: 100px;
  }

  40% {
    left: 150px;
  }

  60% {
    left: 75px;
  }

  100% {
    left: 100px;
  }
}

Four keyframes are specified for the animation named "wobble". In the first keyframe, shown at the beginning of the animation cycle, the value of the left property being animated is 100px. By 40% of the animation duration, left has animated to 150px. At 60% of the animation duration, left has animated back to 75px. At the end of the animation cycle, the value of left has returned to 100px. The diagram below shows the state of the animation if it were given a duration of 10s.

Animation states specified by keyframes

This specification needs to define how the value is determined from the keyframes, like the section on Application of transitions does for CSS Transitions.

3.1. Timing functions for keyframes

A keyframe style rule may also declare the timing function that is to be used as the animation moves to the next keyframe.

@keyframes bounce {

  from {
    top: 100px;
    animation-timing-function: ease-out;
  }

  25% {
    top: 50px;
    animation-timing-function: ease-in;
  }

  50% {
    top: 100px;
    animation-timing-function: ease-out;
  }

  75% {
    top: 75px;
    animation-timing-function: ease-in;
  }

  to {
    top: 100px;
  }

}

Five keyframes are specified for the animation named "bounce". Between the first and second keyframe (i.e., between 0% and 25%) an ease-out timing function is used. Between the second and third keyframe (i.e., between 25% and 50%) an ease-in timing function is used. And so on. The effect will appear as an element that moves up the page 50px, slowing down as it reaches its highest point then speeding up as it falls back to 100px. The second half of the animation behaves in a similar manner, but only moves the element 25px up the page.

A timing function specified on the to or 100% keyframe is ignored.

See the animation-timing-function property for more information.

4. Declaring Animations

CSS Animations are defined by binding keyframes to an element using the animation-* properties. These list-valued properties, which are all longhands of the animation shorthand, form a coordinating list property group with animation-name as the coordinating list base property and each item in the coordinated value list defining the properties of a single animation effect.

Note: This is analogous to the behavior of the background-* properties, with background-image analogous to animation-name.

See CSS Values 4 § A Coordinating List-Valued Properties for how the individual animation-* property values coordinate.

4.1. The animation-name property

The animation-name property defines a list of animations that apply. Each name is used to select the keyframe at-rule that provides the property values for the animation. If the name does not match any keyframe at-rule, there are no properties to be animated and the animation will not execute. Furthermore, if the animation name is none then there will be no animation. This can be used to override any animations coming from the cascade. If multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.

Name: animation-name
Value: [ none | <keyframes-name> ]#
Initial: none
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: list, each item either a case-sensitive css identifier or the keyword none
Canonical order: per grammar
Animation type: not animatable

The values of animation-name have the following meanings:

none
No keyframes are specified at all, so there will be no animation. Any other animations properties specified for this animation have no effect.
<keyframes-name>
The animation will use the keyframes with the name specified by the <keyframes-name>, if they exist. If no @keyframes rule with that name exists, there is no animation.

4.2. The animation-duration property

The animation-duration property defines duration of a single animation cycle.

Name: animation-duration
Value: <time [0s,∞]>#
Initial: 0s
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: list, each item a duration
Canonical order: