Transitions retarget
Anything triggered fast or reversed uses transitions, not @keyframes.
From improve-animations, dimension 4 Interruptibility.
Spam a toggle
Click each switch fast, or press Spam.
@keyframesBad
animation: knob-on 300ms both0 restartstransitionGood
transition: transform 300ms0 retargets.switch .knob {
transform: translateX(0);
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.switch[aria-checked="true"] .knob { transform: translateX(30px); }
Close it halfway open
Press Details twice quickly, or use Open, then interrupt.
@keyframesBad
- Subtotal
- $1,240.00
- Tax
- $99.20
- Shipping
- $18.00
- Total
- $1,357.20
animation: reveal 300ms both0 restartstransitionGood
- Subtotal
- $1,240.00
- Tax
- $99.20
- Shipping
- $18.00
- Total
- $1,357.20
transition: clip-path, transform, opacity0 retargets.panel {
clip-path: inset(0 0 100% 0 round 10px);
transform: translateY(-6px);
opacity: 0;
transition:
clip-path 300ms cubic-bezier(0.23, 1, 0.32, 1),
transform 300ms cubic-bezier(0.23, 1, 0.32, 1),
opacity 200ms ease;
}
.panel[data-open="true"] {
clip-path: inset(0 0 0 0 round 10px);
transform: none;
opacity: 1;
}
Stack toasts
Add a few toasts and watch the ones already shown.
@keyframesBad
animation: toast-in 250ms both0 replaystransitionGood
transition: transform 250ms, opacity 150ms0 replays.toast {
transform: translateY(var(--y));
transition: transform 250ms cubic-bezier(0.23, 1, 0.32, 1), opacity 150ms ease;
}
.toast[data-state="enter"] { transform: translateY(calc(var(--y) + 64px)); opacity: 0; }
.toast[data-state="exit"] { transform: translateY(calc(var(--y) + 12px)); opacity: 0; }
Why it matters, pros and cons
Why it matters
A transition runs from the current value; keyframes replay from 0%.
Pros
- Spamming never restarts from zero.
- Reversal takes back only the time spent.
- State lives in one attribute.
Cons
- Keyframes suit spinners, shimmer and one-shot sequences.
- Entering needs
@starting-styleor an enter frame. - Gestures need springs to keep velocity.