Transitions
Start without any motion. When the mouse moves over the button, .btn:hover matches and scale: 1.2 applies. On the very next frame the button is 1.2 times bigger. On the graph of scale over time that is one sudden jump.
Add one declaration to .btn: transition: scale 0.3s;. It names the property to watch, scale, and how long any change to it should take: 0.3 seconds, which is 300 milliseconds (ms). Changes to scale now glide.
Move the mouse over the button. :hover matches, so the new value of scale is 1.2. Because of the transition the browser does not jump. It notes a start value, 1, an end value, 1.2, and starts a clock at 0 ms.
The screen redraws in frames, about every 16.7 ms on a 60 Hz screen. Each frame the browser works out progress, the time passed divided by 300 ms, turns it into a scale between 1 and 1.2 and redraws. That is about 18 frames.
The default timing function, ease, is quick at first and gentle at the end. Halfway, at 150 ms, progress is 0.50, but ease turns it into 0.80. So the button is already at 1.16, not 1.10.
Move the mouse away. :hover stops matching, so scale goes back to its own value, 1. The transition is written on .btn itself, so it works both ways: another 300 ms with the same easing, from 1.2 down to 1.
Keyframe animations
A transition waits for a change. A keyframe animation does not. @keyframes pulse lists waypoints along the way. This one has a single waypoint, at 50% of the time: scale 1.4. Start and end are not written, so they use the element's own scale, 1.
The animation property plays the keyframes: animation: pulse 1s; runs pulse on .dot over one second. No hover is needed. It starts as soon as the rule applies, here when the page loads, and plays once, because one play is the default.
Frame by frame again: the dot grows from 1 to 1.4 in the first 500 ms, then shrinks back to 1. ease is applied to each part between two keyframes, not to the whole second, so the dot slows down at the top.
How the browser paints
A frame can need four stages: style, layout, paint and composite. scale is a transform, and transforms never move the boxes around them. So the browser can skip layout and often paint too. Animating width would redo layout every frame.
What happens
;; step 1/10 · jump
.btn:hover { scale: 1.2; } no transition yet
mouse over the button :hover matches
scale 1.00 → 1.20 on the next frame
nothing in between: a jump
The figure plays every transition and animation about eight times slower than a real browser, so each frame can be seen. The value cells update in 50 ms steps for the transition and 100 ms steps for the animation; a real browser computes a new value on every frame.