Never animate from scale(0)
Enter from scale(0.95) and opacity: 0, never from zero.
Emil Kowalski@emilkowalskiAnimating from `scale(0)` usually feels "off". Try animating from a higher initial scale instead (0.9+). It helps it feel more gentle and elegant. Here's the difference between initial `scale(0)` and `scale(0.93)`:
From emil-design-eng; demo 1 rebuilds his Options popover.
The Options popover
Open both. Only the starting scale differs.
scale(0)Bad
scale(0) → 1opacity 0 → 1scale(0.93)Good
scale(0.93) → 1opacity 0 → 1.menu {
transform-origin: top center;
opacity: 0;
transform: scale(0.93);
visibility: hidden;
transition: opacity 200ms var(--ease-out),
transform 200ms var(--ease-out),
visibility 0s linear 200ms;
}
.menu[data-open="true"] {
opacity: 1;
transform: none;
visibility: visible;
transition-delay: 0s;
}
Find the floor
Pick a start scale, then show the toast.
scale(0.93) → 1Good
Event createdTomorrow at 9:00 AM
First frames, 60 fps
scale(0.93) · opacity 0toast.animate(
[
{ transform: "scale(0.93)", opacity: 0 },
{ transform: "scale(1)", opacity: 1 },
],
{ duration: 200, easing: "cubic-bezier(0.23, 1, 0.32, 1)" },
);
Type a message
Send a message to both threads.
scale(0), no fadeBad
Lunch tomorrow?
transform: scale(0)scale(0.95) + opacityGood
Lunch tomorrow?
transform: scale(0.95); opacity: 0.entering { transform: scale(0.95); opacity: 0; }
.bubble { transform-origin: bottom right; }
Why it matters, pros and cons
Why it matters
From scale(0) the first frames are a dot. From 0.93 it is itself at once.
Pros
- Readable from the first frame.
- Less travel, so it feels faster.
- Transform and opacity stay on the compositor.
Cons
- Below 0.9 it reads as a zoom again.
- Scale without the fade is a one-frame jump.
- Ripples and badge dots may truly grow from 0.