Shots in this scene
@keyframes and the animation Property
In one line
@keyframes lists the states an animation passes through, and the animation property starts it and sets its timing. Missing stops and missing names have exact, predictable results.
Think of it like this
A flip-book. Each page is a still drawing, and flipping shows them one after another. A keyframe is one page you draw yourself, the key ones. The browser draws every page in between. (The CSS course's Topic 10.3 showed the basics. Here: every part, and every gap.)
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- keyframe
- One stop in an animation: a point in its time and the values the element has there.
- @keyframes
- The CSS rule that names an animation and lists its keyframes.
- shorthand
- A property that sets several others at once, such as
animation. It also resets the ones you leave out. - longhand
- One of the single properties a shorthand sets, such as
animation-delay. - stop
- A percentage inside @keyframes, such as
70%.
Step by step
01Two animations at once
Press Ping the headlight. The dot slides 200 px (900 ms) and, starting 300 ms in, pulses its shadow twice (600 ms each). The console says animations on the dot: slide + ping, the order they are listed in.
Slow it to 0.1× and read the tracks: slide starts at 0, ping has delay 300 and ×2.
02Read the shorthand
ping 600ms ease-out 300ms 2 is: name, duration 600 ms, easing, delay 300 ms, iteration count 2. Two time values: the first is always the duration.
03Look at the gaps
The second example logs what the browser built. A misspelt name gives animations created = 0. A single 50% stop gives three keyframes, with none at both ends. A lone to starts from the element's own translateX(20px).
04Order of the shorthand and the longhands
Write .box { animation-fill-mode: forwards; } and then .box { animation: slide 1s; }. The second rule resets the fill mode to none: the box snaps back at the end. Swap the two rules and it holds.
Try it yourself
- 1
Swap the two time values
In the first example change
ping 600ms ease-out 300ms 2toping 300ms ease-out 600ms 2. Press Run and the button. The track now saysdelay 600, duration 300: the first time is the duration, the second the delay. - 2
Watch the later animation win
In the second example, step the clock to the end. The "two animations" box stops at 30 px (
x2), not 100 px (x1): both animatetransform, and the later one in the list wins.
Code & diagrams
Press the button, pause, and read the two tracks. The ping track is amber: box-shadow costs a repaint.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
animations on the dot: slide + pingFour edge cases in one page. The first row has no track at all: its animation was never created.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
misspelt name: animations created = 0
only 50%: [[0,"none"],[0.5,"translateX(100px)"],[1,"none"]]
only to: [[0,"translateX(20px)"],[1,"translateX(100px)"]]
two on one property: x1, x2Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Misspell the animation name
In the second example, change .go #mid { animation: half 1s linear; } to animation: halff 1s linear; and press Run.
Break #2
A shorthand that wipes the fill mode
In the second example, add .go #nofrom { animation-fill-mode: forwards; } before the .go #nofrom { transform: …; animation: tox 1s linear forwards; } rule, and take forwards out of that rule.
Myth vs fact
Myth
A missing @keyframes gives an error in the console.
Fact
It gives nothing. The animation is simply not created, which makes typos hard to spot.
Myth
Percent stops are milliseconds.
Fact
They are fractions of one iteration. The same keyframes run at any duration.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Each
@keyframesname belongs to the stylesheet's scope, and a later rule with the same name replaces the earlier one completely, stops and all. Prefix names (toast-in) to avoid clashes between components. - ▸
Setting
animation-name: noneand back on the next frame restarts an animation: the first change cancels it (ananimationcancelevent), the second starts it.void el.offsetWidthbetween a class removal and addition does the same. - ▸
Keyframes can set
animation-timing-functionper stop: it applies from that stop to the next, so one animation can ease out of one stop and linear into the next.
Remember this
- 1
The problem this solves: a transition only goes from the old value to the new one, and only when something changes. Some motion has more than two states (a pulse that grows, then fades) or must start by itself, repeat, or run when a class is added. Keyframes give you any number of states.
- 2
Keyframes.
@keyframes ping { 0% {…} 70% {…} 100% {…} }names the animation and lists stops.fromis0%,tois100%, and you may list stops together (70%, 100% { … }). Stops are positions in the animation's time, not in milliseconds: the duration comes from theanimationproperty. - 3
Starting it.
animation: slide 900ms cubic-bezier(0.22, 1, 0.36, 1) forwardsis the shorthand. The first time value is the duration, the second is the delay. The longhands areanimation-name,-duration,-timing-function,-delay,-iteration-count,-direction,-fill-mode,-play-stateand-composition. A comma list runs several animations on one element, as the first example does withslideandping. - 4
The shorthand resets everything it leaves out.
animation: slide 1ssets fill mode back tonone, even if an earlier rule saidforwards. Put longhands after the shorthand, not before. This is a very common reason for an animation that "stopped holding its end state". - 5
Gaps fill in from the element. If a keyframe lacks a property at
0%or100%, the browser uses the element's own value for it. If an animation lists a property in only some stops, the property is interpolated between the stops that have it. If you name a@keyframesthat does not exist, nothing happens and nothing is reported: no animation is created. - 6
Two animations, one property. The later one in the list wins that property at each moment. Its missing
fromstarts from where the earlier one is, which is why their combination can look odd. Topic 4.5 shows how to combine them on purpose.
Explain it without notes
What happens when an animation's keyframes have no from stop?
Why can a shorthand animation rule break an earlier animation-fill-mode?
What do you get when you name a @keyframes rule that does not exist?
Practice
Write keyframes pulse that scale a badge to 1.2 at 50% and back, used as animation: pulse 800ms ease-in-out 3. What stops does the browser create for 100%?
A button's pulse works in one file but not in another. List three things to check.
Done when you can
You can write @keyframes with from, to and percent stops
You can read the shorthand: duration first, delay second
You know what the browser does with missing stops and missing names
You can run two animations on one element and say which wins