Skip to content

Search pages

Jump to any page on the site.

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.

24 frames383 ms
24 frames at 60 fps. Settles in 383 ms, overshoots 0.3%.

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.

States of the expand recipe
StatePropertyTokenNow
closedprogressliteral0
opacityliteral0
openprogressliteral1
opacityliteral1
transitionMovement (spring){spring.default}stiffness 511 · damping 40
transitionFade duration{duration.fast}132ms
transitionFade easing{easing.standard}cubic-bezier(0.2, 0, 0, 1)
transitionExit duration{duration.fast}132ms
transitionExit easing{easing.exit}cubic-bezier(0.3, 0, 1, 1)

Tokens used

  • spring.default stiffness 511 · damping 40
  • duration.fast 132ms
  • easing.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.