Collapse

Content folded behind its own heading, one panel or several, with an accordion mode that keeps one panel open at a time.

Accessibility

A Collapse is a stack of disclosures built from divs and ARIA, not from <button> and <details>. This tab covers what it sets for you, the key that does not work, and what a folded panel hides.

What it renders

A Collapse has no <button> and no <details>. Every panel is a <div> with a header <div> and, once opened, a body <div> beside it. The semantics come from ARIA attributes on those divs.
  • The header carries role="button", aria-expanded for the open state, aria-disabled and tabIndex={0}. aria-expanded is what announces the change after a toggle.
  • The header and its body are not linked by aria-controls. The body is the header's next sibling, which works for a screen reader reading in order and not for one jumping by role.
  • With accordion the roles change: the root becomes a tablist, each header a tab and each body a tabpanel, with none of the keyboard support or aria-selected those roles promise. If the stack is a tab pattern, use Tabs.
  • Keep the expand icon decorative. The kit's Icon is aria-hidden by default, so the default arrow is too. Anything else you return from expandIcon needs aria-hidden of its own.

Keyboard

Tab reaches every panel header in document order, open or closed, so the titles work as a map. After that, a div with role="button" behaves differently from a real button.
  • Enter toggles a panel. Space does not. Space scrolls the page instead, where a native <button> would take both keys. There is no prop that changes this, so readers who press Space first may think the control is broken.
  • There are no arrow keys, no Home and no End. A Collapse is a list of separate buttons, so a long stack is a long walk. Keep stacks short rather than adding a key handler.
  • collapsible moves the tab stop. Unset, the whole header is the trigger. "icon" narrows it to the arrow, for headers that hold their own controls. "header" puts it on the title text and the arrow, which gives each panel two tab stops for one action.
  • A control inside a default header needs stopPropagation, or clicking it also toggles the panel. It also puts a button inside a role="button", which gives one row two tab stops and two meanings. Use collapsible="icon" instead.

Hidden content is hidden

A closed panel is gone for every reader, not only out of sight, and most Collapse decisions depend on that.
  • Before its first open, the body is not rendered. A panel that was never opened adds nothing to the page.
  • After it closes, the body stays mounted under display: none. That takes it out of the accessibility tree and out of the layout.
  • Find-in-page cannot reach it. The browser only searches rendered text, and a folded panel has none. A reader looking for a value they know is on the page will decide it is not.
  • forceRender on an item renders the body from the start and keeps it mounted, for content that has to measure itself. It does not make a closed body findable, because display: none still applies.
  • destroyOnHidden unmounts the body on close. Use it for a panel with a live subscription or a heavy tree, and not by default: it throws away scroll position and unsaved input.

Focus after a toggle

Focus stays on the header, and nothing moves it into the panel that opened. The reader chose to show the content, not to go to it.
  • aria-expanded changes on the element that keeps focus, which is how a disclosure is announced. Do not add a live region on top of it.
  • The body opens below the header, so the next Tab lands inside it in reading order. When the header also holds controls, keep the trigger before them in the DOM.
  • Closing a panel can move the page. With accordion, opening panel four closes panel one, and the header the reader pressed jumps up. Keep accordion stacks short enough that it stays on screen.
  • Never move focus into a panel the reader did not open. A panel opened by validation should scroll into view, with focus on the field that failed.

Disabled panels

  • collapsible="disabled" sets aria-disabled and tabIndex={-1}, so the header is announced as disabled and leaves the tab order. The title is still read by someone reading the whole page.
  • Set it on one item rather than the whole stack. The Collapse value is the default and an item's own collapsible overrides it.
  • Say why. A disabled title is only grayed text, and gray is not a message. Put the reason in the title or in extra.
  • A panel disabled because it is empty is usually better open, with an empty state inside.

Contrast and motion

  • The open state shows through the arrow's rotation and the body being there, not through color, so a restyle rarely breaks it. Hiding the arrow with showArrow={false} removes the only sign the row can be opened.
  • A ghost stack draws its header on whatever is behind it, so check the title color against that surface, not against the shaded header band of the bordered form.
  • Keep the focus ring on the header. It is the only thing that tells the focused row apart from six others that look the same.
  • Opening and closing animates the height. A long panel sweeps a large part of the screen, which is the case a reduced-motion setting is for.
  • Turn motion off for the whole theme with theme={{ token: { motion: false } }}, and tie it to the reader's own preference.