Entrances
Elastic Entrance Animation
A springy scale-in that wobbles slightly before settling.
Opens with this animation loaded. Nothing is uploaded — the scene travels in the link.
.elastic-in {
transform: translate3d(180px, 100px, 0);
width: 120px;
height: 120px;
background: #6366f1;
border-radius: 20px;
animation: elastic-in-anim 1000ms linear 0ms infinite both;
will-change: transform, opacity;
}
@keyframes elastic-in-anim {
0% {
transform: translate3d(180px, 100px, 0) scale(0.2, 0.2);
opacity: 0;
animation-timing-function: ease-out;
}
2.5% {
transform: translate3d(180px, 100px, 0) scale(0.417, 0.417);
opacity: 0.161;
}
5% {
transform: translate3d(180px, 100px, 0) scale(0.717, 0.717);
opacity: 0.308;
}
7.5% {
transform: translate3d(180px, 100px, 0);
opacity: 0.445;
}
10% {
transform: translate3d(180px, 100px, 0) scale(1.2, 1.2);
opacity: 0.571;
}
12.5% {
transform: translate3d(180px, 100px, 0) scale(1.291, 1.291);
opacity: 0.685;
}
15% {
transform: translate3d(180px, 100px, 0) scale(1.283, 1.283);
opacity: 0.785;
}
17.5% {
transform: translate3d(180px, 100px, 0) scale(1.206, 1.206);
opacity: 0.87;
}
20% {
transform: translate3d(180px, 100px, 0) scale(1.1, 1.1);
opacity: 0.938;
}
22.5% {
transform: translate3d(180px, 100px, 0);
opacity: 0.983;
}
25% {
transform: translate3d(180px, 100px, 0) scale(0.929, 0.929);
opacity: 1;
}
27.5% {
transform: translate3d(180px, 100px, 0) scale(0.897, 0.897);
opacity: 1;
}
30% {
transform: translate3d(180px, 100px, 0) scale(0.9, 0.9);
opacity: 1;
}
32.5% {
transform: translate3d(180px, 100px, 0) scale(0.927, 0.927);
opacity: 1;
}
35% {
transform: translate3d(180px, 100px, 0) scale(0.965, 0.965);
opacity: 1;
}
37.5% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
40% {
transform: translate3d(180px, 100px, 0) scale(1.025, 1.025);
opacity: 1;
}
42.5% {
transform: translate3d(180px, 100px, 0) scale(1.036, 1.036);
opacity: 1;
}
45% {
transform: translate3d(180px, 100px, 0) scale(1.035, 1.035);
opacity: 1;
}
47.5% {
transform: translate3d(180px, 100px, 0) scale(1.026, 1.026);
opacity: 1;
}
50% {
transform: translate3d(180px, 100px, 0) scale(1.012, 1.012);
opacity: 1;
}
52.5% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
55% {
transform: translate3d(180px, 100px, 0) scale(0.991, 0.991);
opacity: 1;
}
57.5% {
transform: translate3d(180px, 100px, 0) scale(0.987, 0.987);
opacity: 1;
}
60% {
transform: translate3d(180px, 100px, 0) scale(0.988, 0.988);
opacity: 1;
}
62.5% {
transform: translate3d(180px, 100px, 0) scale(0.991, 0.991);
opacity: 1;
}
65% {
transform: translate3d(180px, 100px, 0) scale(0.996, 0.996);
opacity: 1;
}
67.5% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
70% {
transform: translate3d(180px, 100px, 0) scale(1.003, 1.003);
opacity: 1;
}
72.5% {
transform: translate3d(180px, 100px, 0) scale(1.005, 1.005);
opacity: 1;
}
75% {
transform: translate3d(180px, 100px, 0) scale(1.004, 1.004);
opacity: 1;
}
77.5% {
transform: translate3d(180px, 100px, 0) scale(1.003, 1.003);
opacity: 1;
}
80% {
transform: translate3d(180px, 100px, 0) scale(1.002, 1.002);
opacity: 1;
}
82.5% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
85% {
transform: translate3d(180px, 100px, 0) scale(0.999, 0.999);
opacity: 1;
}
87.5% {
transform: translate3d(180px, 100px, 0) scale(0.998, 0.998);
opacity: 1;
}
90% {
transform: translate3d(180px, 100px, 0) scale(0.998, 0.998);
opacity: 1;
}
92.5% {
transform: translate3d(180px, 100px, 0) scale(0.999, 0.999);
opacity: 1;
}
95% {
transform: translate3d(180px, 100px, 0) scale(0.999, 0.999);
opacity: 1;
}
97.5% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
100% {
transform: translate3d(180px, 100px, 0);
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.elastic-in {
animation: none;
transition: none;
}
}Why it is built this way
Same principle as the bounce: the oscillation is baked into keyframes rather than expressed as an easing function. Use it sparingly — elastic motion draws a lot of attention, which is the point once per screen and a nuisance twice.
What it animates
3 properties across 2 CSS declarations. The bar shows when each one is moving.
opacitytransformtransformWhat it costs to run
Opacity, scale x and scale y are handled by the compositor: no layout, no repaint, and the work can run off the main thread. This is the cheap end of CSS animation, which is why it stays smooth on a slow phone where other properties will not.
It runs for 1.00s across 7 keyframe stops, and most of those are not hand-written. The easing here oscillates, and no single cubic-bezier can — so the curve is sampled into extra stops on export. That is why the CSS is longer than you would write yourself, and why it needs no JavaScript.
3 curves in play — Ease Out, Linear and Elastic Out on a plain box. Different properties easing differently is how a movement gets texture: the eye reads the mismatch as weight rather than as a mistake.
Accessibility
Starting at opacity 0 makes the reduced-motion guard load-bearing rather than polite: strip it and a reader who asked for less movement gets no content at all, because nothing ever fades it in. It fades over 1.00s alongside scale x and scale y, and the guard skips all of it together. The generated CSS ends on the resting state, so skipping straight there is safe.