Expand
The height springs open. The content fades in.
Live demo
The preset here changes the whole site. Slow motion and reduced motion change this stage only.
Frame by frame
Every frame of the movement, on top of each other. Wide gaps are fast. Ghosts past the dashed outline are overshoot. It updates with the preset.
When to use
Accordions, disclosures, "show more" sections, and collapsible settings. The panel's height springs open on spring.default and its content fades in. Closing is a fast tween, so a section shuts out of the way.
This is the one recipe that animates height, because content must push the page down. Keep the content inside light, and put the open state in aria-expanded on the trigger.
Recipe
Accordions and disclosures. The panel's height springs open. Its content fades in. Closing tweens the height shut, faster.
| State | Property | Token | Now |
|---|---|---|---|
| closed | progress | literal | 0 |
| opacity | literal | 0 | |
| open | progress | literal | 1 |
| opacity | literal | 1 | |
| transition | Movement (spring) | {spring.default} | stiffness 511 · damping 40 |
| transition | Fade duration | {duration.fast} | 132ms |
| transition | Fade easing | {easing.standard} | cubic-bezier(0.2, 0, 0, 1) |
| transition | Exit duration | {duration.fast} | 132ms |
| transition | Exit easing | {easing.exit} | cubic-bezier(0.3, 0, 1, 1) |
Tokens used
spring.default stiffness 511 · damping 40duration.fast 132mseasing.standard cubic-bezier(0.2, 0, 0, 1)easing.exit cubic-bezier(0.3, 0, 1, 1)
Code
/* expand: Accordions and disclosures. The panel's height springs open. Its content fades in. Closing tweens the height shut, faster.
Use: <div class="motif-expand" data-state="open|closed"> with one child that holds the content.
The height animates through grid-template-rows (0fr to 1fr), so no height is measured. */
.motif-expand {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows var(--motif-duration-fast) var(--motif-ease-exit);
}
.motif-expand > * {
min-height: 0;
overflow: hidden;
opacity: 0;
transition: opacity var(--motif-duration-fast) var(--motif-ease-exit);
}
.motif-expand[data-state="open"] {
grid-template-rows: 1fr;
transition: grid-template-rows var(--motif-spring-default-duration) var(--motif-spring-default);
}
.motif-expand[data-state="open"] > * {
opacity: 1;
transition: opacity var(--motif-duration-fast) var(--motif-ease-standard);
}
@media (prefers-reduced-motion: reduce) {
/* expand: the panel opens and closes to full height at once. The content still fades. */
.motif-expand,
.motif-expand[data-state] {
transition: none;
}
}
Reduced motion
- The panel opens to full height at once. Its content still fades in, so the change stays visible.
- Fades take 132ms (
duration.fast), so the state change stays visible. - Turn on the Reduced motion box under the demo to preview it, or set your system to reduce motion.