Skip to content

Collapsible

A trigger that shows and hides a panel. Unstyled, so components and applications bring their own look; for a styled one, use Progressive disclosure.

Code
demo.tsx
import {KeyboardArrowDownIcon} from '@earendil-works/elanor-icons-react';
import {Collapsible, Text} from '@earendil-works/elanor-react';

import styles from './collapsible.module.css';

export default function CollapsibleDemo() {
  return (
    <Collapsible.Root>
      <Collapsible.Trigger className={styles.trigger}>
        <KeyboardArrowDownIcon className={styles.chevron} />
        Recovery keys
      </Collapsible.Trigger>
      <Collapsible.Panel className={styles.panel}>
        <Text className={styles.content}>
          Store recovery keys somewhere safe. Each key works once.
        </Text>
      </Collapsible.Panel>
    </Collapsible.Root>
  );
}
collapsible.module.css
.trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--semantic-dimension-space-control-gap-small);
  padding: 0;
  border: 0;
  background: none;
  color: var(--semantic-color-text-primary);
  font: var(--semantic-typography-ui-primary);
  cursor: pointer;
}

.chevron {
  transition: rotate var(--semantic-transition-movement-disclosure);
}

.trigger:not([data-panel-open]) .chevron {
  rotate: -90deg;
}

.panel {
  height: var(--collapsible-panel-height);
  overflow: hidden;
  transition: height var(--semantic-transition-movement-disclosure);
}

.panel[data-starting-style],
.panel[data-ending-style] {
  height: 0;
}

.content {
  padding-block-start: var(--semantic-dimension-space-padding-medium);
}

Usage

import {Collapsible} from '@earendil-works/elanor-react';

<Collapsible.Root>
  <Collapsible.Trigger />
  <Collapsible.Panel />
</Collapsible.Root>;

defaultOpen starts the panel open; open and onOpenChange control it. To animate the panel, give it height: var(--collapsible-panel-height), overflow: hidden, and a height transition, and a height of 0 with data-starting-style and data-ending-style, as the demo’s stylesheet does.

Find in page

With hiddenUntilFound, the browser’s find in page searches a closed panel and opens it on a match. Leave the panel closed and search this page for a word in it.

Code
hidden-until-found.tsx
import {KeyboardArrowDownIcon} from '@earendil-works/elanor-icons-react';
import {Collapsible, Text} from '@earendil-works/elanor-react';

import styles from './collapsible.module.css';

export default function CollapsibleHiddenUntilFoundDemo() {
  return (
    <Collapsible.Root>
      <Collapsible.Trigger className={styles.trigger}>
        <KeyboardArrowDownIcon className={styles.chevron} />
        Rollbacks
      </Collapsible.Trigger>
      <Collapsible.Panel hiddenUntilFound className={styles.panel}>
        <Text className={styles.content}>
          A deploy that fails its checks never replaces the running version.
        </Text>
      </Collapsible.Panel>
    </Collapsible.Root>
  );
}
collapsible.module.css
.trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--semantic-dimension-space-control-gap-small);
  padding: 0;
  border: 0;
  background: none;
  color: var(--semantic-color-text-primary);
  font: var(--semantic-typography-ui-primary);
  cursor: pointer;
}

.chevron {
  transition: rotate var(--semantic-transition-movement-disclosure);
}

.trigger:not([data-panel-open]) .chevron {
  rotate: -90deg;
}

.panel {
  height: var(--collapsible-panel-height);
  overflow: hidden;
  transition: height var(--semantic-transition-movement-disclosure);
}

.panel[data-starting-style],
.panel[data-ending-style] {
  height: 0;
}

.content {
  padding-block-start: var(--semantic-dimension-space-padding-medium);
}

Static rendering

On pages rendered on the server without React in the browser, use the static parts. They render the same markup, and the behavior opens and closes the panel with the same states, so the same stylesheet animates it:

<Collapsible.StaticRoot defaultOpen>
  <Collapsible.StaticTrigger />
  <Collapsible.StaticPanel hiddenUntilFound />
</Collapsible.StaticRoot>
import {createCollapsible, enhance} from '@earendil-works/elanor-react';

enhance({collapsible: createCollapsible});

Without the behavior, closed panels stay hidden.

API

Collapsible.Root

Accepts Base UI Collapsible.Root props.

Collapsible.Trigger

Accepts Base UI Collapsible.Trigger props.

Collapsible.Panel

Accepts Base UI Collapsible.Panel props.

Collapsible.StaticRoot

A collapsible for pages without React in the browser; createCollapsible makes it work.

PropTypeDefaultDescription
data-elanorstring-The page's own behaviors for this element, attached after the component's.

Also accepts Base UI Collapsible.Root props.

Collapsible.StaticTrigger

Renders <button>. Accepts its HTML attributes and ref.

Collapsible.StaticPanel

Accepts Base UI Collapsible.Panel props.