Navigation
Accordion Expand Animation
An accordion panel that opens smoothly with its content fading in.
Opens with this animation loaded. Nothing is uploaded — the scene travels in the link.
.accordion {
transform: translate3d(0px, 0px, 0);
transform-origin: 240px 160px;
}
.accordion-header {
transform: translate3d(80px, 86px, 0);
width: 320px;
height: 48px;
background: #1c1e2a;
border-radius: 12px;
color: #e7e9ee;
font-size: 14px;
font-weight: 600;
transition: background 160ms ease-out;
}
.accordion-header:hover {
background: #262a3a;
}
.accordion-panel {
transform: translate3d(80px, 138px, 0);
width: 320px;
height: 96px;
background: #14161f;
border-radius: 12px;
animation: accordion-panel-anim 1200ms linear 0ms infinite both;
will-change: transform, opacity;
}
@keyframes accordion-panel-anim {
0% {
height: 0px;
opacity: 0;
animation-timing-function: ease-out;
}
25% {
height: 65.726px;
opacity: 1;
}
50% {
height: 96px;
opacity: 1;
}
100% {
height: 96px;
opacity: 1;
}
}
.panel-copy {
transform: translate3d(98px, 164px, 0);
width: 284px;
height: 20px;
background: #00000000;
color: #8b90a0;
font-size: 13px;
animation: panel-copy-anim 1200ms linear 0ms infinite both;
will-change: transform, opacity;
}
@keyframes panel-copy-anim {
0% {
transform: translate3d(98px, 172px, 0);
opacity: 0;
animation-timing-function: ease-out;
}
22.5% {
transform: translate3d(98px, 172px, 0);
opacity: 0;
animation-timing-function: ease-out;
}
50% {
transform: translate3d(98px, 164px, 0);
opacity: 1;
}
100% {
transform: translate3d(98px, 164px, 0);
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.accordion-header,
.accordion-panel,
.panel-copy {
animation: none;
transition: none;
}
}Why it is built this way
This is the one common case where transform genuinely cannot help: opening a panel changes layout, so height has to animate. Because that is more expensive than a transform, keep the duration short and let the copy fade in slightly late — the stagger hides the cost.
What it animates
3 properties across 3 CSS declarations on 2 elements. The bar shows when each one is moving.
heightopacitytransformWhat it costs to run
Animating height changes the size of the box, so the browser re-runs layout on every frame — the expensive kind. Fine on one element; think twice before putting it on a long list.
It runs for 1.20s across 8 keyframe stops, with every property moving over the same window.
2 curves in play — Ease Out and Linear. 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.20s alongside height and y, and the guard skips all of it together. The generated CSS ends on the resting state, so skipping straight there is safe.