Skip to content
Ndop UI

Collapsible

Shows and hides a section of content.

@peduarte starred 3 repositories

@radix-ui/primitives
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Button } from '@/ui/button/button';
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/ui/collapsible/collapsible';

/** A list of starred repositories that expands to show the rest. */
@Component({
  selector: 'example-collapsible-demo',
  imports: [
    Button,
    Collapsible,
    CollapsibleContent,
    CollapsibleTrigger,
    NdIcon,
  ],
  template: `
    <div ndCollapsible class="flex w-[350px] flex-col gap-2">
      <div class="flex items-center justify-between gap-4 px-4">
        <h2 class="text-sm font-semibold">
          &#64;peduarte starred 3 repositories
        </h2>
        <button
          ndCollapsibleTrigger
          ndButton
          variant="ghost"
          size="icon"
          class="size-8"
        >
          <svg ndIcon="chevrons-up-down"></svg>
          <span class="sr-only">Show all repositories</span>
        </button>
      </div>
      <div class="rounded-md border px-4 py-2 font-mono text-sm">
        &#64;radix-ui/primitives
      </div>
      <div ndCollapsibleContent class="flex flex-col gap-2">
        <div class="rounded-md border px-4 py-2 font-mono text-sm">
          &#64;radix-ui/colors
        </div>
        <div class="rounded-md border px-4 py-2 font-mono text-sm">
          &#64;stitches/react
        </div>
      </div>
    </div>
  `,
})
export class CollapsibleDemoExample {}

A collapsible shows and hides a section of content with a button, the WAI-ARIA disclosure pattern. The trigger says whether the content is shown, with aria-expanded, and hidden content is out of the tab order. It has no styles of its own: style the trigger as a Button and the content as you like, and use data-state to rotate a chevron.

Installation

npx @ndop/cli add collapsible

Usage

import { Button } from '@/ui/button/button';
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/ui/collapsible/collapsible';
<div ndCollapsible class="flex w-[350px] flex-col gap-2">
  <div class="flex items-center justify-between gap-4 px-4">
    <h2 class="text-sm font-semibold">
      &#64;peduarte starred 3 repositories
    </h2>
    <button
      ndCollapsibleTrigger
      ndButton
      variant="ghost"
      size="icon"
      class="size-8"
    >
      <svg ndIcon="chevrons-up-down"></svg>
      <span class="sr-only">Show all repositories</span>
    </button>
  </div>
  <div class="rounded-md border px-4 py-2 font-mono text-sm">
    &#64;radix-ui/primitives
  </div>
  <div ndCollapsibleContent class="flex flex-col gap-2">
    <div class="rounded-md border px-4 py-2 font-mono text-sm">
      &#64;radix-ui/colors
    </div>
    <div class="rounded-md border px-4 py-2 font-mono text-sm">
      &#64;stitches/react
    </div>
  </div>
</div>

Examples

Collapsible settings

Advanced options shown on demand.

import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Button } from '@/ui/button/button';
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/ui/collapsible/collapsible';
import { Input } from '@/ui/input/input';
import { Label } from '@/ui/label/label';

/**
 * Advanced options a form shows on demand; the chevron turns with
 * `data-state`.
 */
@Component({
  selector: 'example-collapsible-settings',
  imports: [
    Button,
    Input,
    Label,
    Collapsible,
    CollapsibleContent,
    CollapsibleTrigger,
    NdIcon,
  ],
  template: `
    <form class="grid w-full max-w-sm gap-4">
      <div class="grid gap-2">
        <label ndLabel for="collapsible-settings-name">Project name</label>
        <input ndInput id="collapsible-settings-name" value="ndop-docs" />
      </div>
      <div ndCollapsible class="grid gap-4">
        <button
          ndCollapsibleTrigger
          ndButton
          variant="ghost"
          size="sm"
          class="group justify-self-start"
        >
          Advanced options
          <svg
            ndIcon="chevron-right"
            class="transition-transform group-data-[state=open]:rotate-90 rtl:rotate-180 rtl:group-data-[state=open]:rotate-90"
          ></svg>
        </button>
        <div ndCollapsibleContent class="grid gap-2">
          <label ndLabel for="collapsible-settings-dir">Output folder</label>
          <input ndInput id="collapsible-settings-dir" value="dist" />
        </div>
      </div>
    </form>
  `,
})
export class CollapsibleSettingsExample {}

Accessibility

Implements the Disclosure (Show/Hide) pattern of the WAI-ARIA Authoring Practices.

Keyboard

Keys the component handles.

Key Action
EnterOn the trigger, shows or hides the content.
SpaceOn the trigger, shows or hides the content.

Focus

  • Focus stays on the trigger when the content shows or hides.
  • Shown content joins the tab order after the trigger; hidden content leaves it.

Right-to-left

Nothing mirrors.

API reference

Collapsible

[ndCollapsible]

A collapsible (@ndop/primitives/collapsible): a trigger that shows and hides a section of content, which stays in the page and slides open and closed. Style the trigger with other items, such as a ghost Button. open is two-way bindable.

Collapsible binding Type Default
[defaultOpen]
Initial open state when open is not bound.
booleanfalse
[disabled]
Stops the trigger from toggling the content.
booleanfalse
[(open)]
Whether the content is shown. Two-way bindable with [(open)].
booleanfalse
Data attribute Values
data-slotcollapsible
data-state —
data-disabled —

CollapsibleTrigger

button[ndCollapsibleTrigger]

The button that shows and hides its collapsible's content, with aria-expanded and aria-controls.

Manages aria-expanded, aria-controls.

Data attribute Values
data-slotcollapsible-trigger
data-state —
data-disabled —

CollapsibleContent

[ndCollapsibleContent]

The content a collapsible shows and hides, sliding open and closed; hidden content leaves the accessibility tree and the tab order. It clips its content only while it animates, so focus rings inside it show.

CollapsibleContent binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcollapsible-content
data-state —
data-animating —

Agent notes

Do

  • Put ndCollapsibleTrigger on a native button, often with ndButton.
  • Write a trigger label that says what it reveals, such as "Show 3 more repositories".
  • Start it open with defaultOpen, or bind [(open)] to control it.

Don't

  • Don't use it for a set of headings that each show a section; use an Accordion.
  • Don't hide content that most people need.