# collapse

Animates a panel between zero and its natural height. The preset for accordions and collapsibles, where fading alone leaves the layout jumping. Takes a single element child.

## Use it

```html
data-motion="collapse"
```

## What it sets

The preset only declares these. Everything else, duration and easing included, comes from the shared tokens, so overriding a token restyles every preset at once rather than one of them.

```css
[data-motion~="collapse"] {
  /* The grid mechanism. A row track between `0fr` and `1fr` is two concrete flex
   * values, so the enter and the exit both have a start value the browser can
   * interpolate.
   *
   * Animating `height` cannot say that. The open steady state is `auto`, and
   * `auto` to 0 is a discrete transition: under allow-discrete the panel hangs at
   * full height for half the duration and then snaps shut. `interpolate-size:
   * allow-keywords` fixed it by making `auto` interpolatable, and that property is
   * Chromium-only, so every other engine got the snap. A track list interpolates
   * in all three.
   *
   * Nothing here reads a host height variable any more. The four names Radix and
   * Base UI publish between them are out of the mechanism entirely, so there is
   * nothing left to resolve or to keep in step with either library.
   *
   * The cost is one composition rule: the panel takes a single element child, and
   * that child is what gets clipped. Every accordion and collapsible page states
   * it in its composition tree. */
  grid-template-rows: 1fr;
  overflow: hidden;
  /* grid-template-rows is absent from the base transition list, where every
   * property is compositor-friendly. Collapsing is a layout move, so it opts in
   * here. */
  transition-property: opacity, translate, scale, rotate, grid-template-rows, display, overlay, content-visibility;

  /* Declared only while the panel is visible. `[hidden]` is a user-agent rule of
   * `display: none` and any author `display` beats it, so an unconditional
   * `display: grid` would stop [hidden] from ever hiding a plain panel. */
  &:not([hidden]) {
    display: grid;
  }

  /* A grid item's automatic minimum size is its content, so the track cannot
   * shrink below it. The child's own overflow is what makes that minimum zero. */
  & > * {
    min-height: 0;
    overflow: hidden;
  }

  /* Radix keyframe path. A second animation track carries the row size, so this
   * still composes with fade. fill-mode `none` on enter hands the track back to
   * the resting 1fr, letting the panel grow with its content after it has
   * opened. */
  &:is([data-state="open"], [data-state="visible"]):not([data-starting-style]):not([data-ending-style]) {
    animation-name: motion-enter, motion-collapse-enter;
    animation-fill-mode: both, none;
  }
  &:is([data-state="closed"], [data-state="hidden"]):not([data-starting-style]):not([data-ending-style]) {
    animation-name: motion-exit, motion-collapse-exit;
  }

  /* Transition path: Base UI, and any plain [hidden] panel. The closed list is
   * written last so it wins on the opening frame, where Base UI sets data-open
   * and data-starting-style together and the panel must still start at zero. */
  &:is([data-closed], [data-starting-style], [data-ending-style], [hidden], [data-motion-state="closed"]) {
    grid-template-rows: 0fr;
  }
}
```

## Composition

Composes with `fade`. Combine them with a space.

```html
data-motion="fade collapse"
```

Do not combine it with `scale-fade`, `slide-up`.

## Hosts

The same attribute across every host it supports. You never choose the mechanism, the host's own state attributes do.

| Host | Markup |
| --- | --- |
| radix | `<Accordion.Content data-motion="collapse" />` |
| base-ui | `<Accordion.Panel data-motion="collapse" />` |
| plain | `<div hidden data-motion="collapse" />` |
