

<Preview name="BasicCollapsibleExample" />

## Overview [#overview]

The **Collapsible** component provides an interactive way to show and hide content with smooth CSS transitions. Built on Base UI primitives, it supports both controlled and uncontrolled states, making it perfect for creating expandable sections, details panels, and progressive disclosure interfaces.

## Usage [#usage]

```tsx
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "@tilt-legal/cubitt-components/primitives";
```

```tsx
<Collapsible>
  <CollapsiblePanel>Your collapsible content goes here</CollapsiblePanel>
  <CollapsibleTrigger />
</Collapsible>
```

## Examples [#examples]

### Card with Collapsible [#card-with-collapsible]

Combine collapsible with cards to create expandable information panels.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="CardCollapsibleExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    "use client";

    import { cn } from "@tilt-legal/cubitt-components/utilities";
    import {
      Button,
      Badge,
      Collapsible,
      CollapsiblePanel,
      CollapsibleTrigger,
      Card,
      CardContent,
      CardHeader,
      CardHeading,
      CardToolbar,
    } from "@tilt-legal/cubitt-components/primitives";
    import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "@tilt-legal/cubitt-components/primitives";
    import {
      ChevronDown,
      ChevronUp,
      ArrowDown,
      ArrowUp,
      } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Component() {
      const [isOpen,
      setIsOpen] = React.useState(false);

      interface IStatItem {
        label: string;
        value: number;
        change: string;
        changeType: "increase" | "decrease";
      }

      const stats: IStatItem[] = [
        {
          label: "Added to Cart",
      value: 3842,
      change: "+1.8%",
      changeType: "increase",
      },
      {
          label: "Reached Checkout",
      value: 1256,
      change: "-1.2%",
      changeType: "decrease",
      },
      {
          label: "Purchased",
      value: 649,
      change: "+2.4%",
      changeType: "increase",
      },
      ];

      return (
        <Card className="w-full max-w-[350px]">
          <Collapsible open={isOpen} onOpenChange={setIsOpen}>
            <CardHeader
              className={`h-auto py-4 transition-[border-radius,
      margin] ${isOpen ? "rounded-b-none!" : "rounded-b-[calc(theme(borderRadius.2xl)-4px)]! mb-0!"}`}
            >
              <CardHeading>
                <div className="text-muted-foreground font-medium text-sm">
                  Conversion Rate
                </div>
                <div className="flex items-center gap-1.5">
                  <span className="text-foreground font-semibold text-xl">
                    29.9%
                  </span>
                  <Badge
                    variant="primary"
                    appearance="outline"
                    size="sm"
                    shape="circle"
                  >
                    +4.5%
                  </Badge>
                </div>
              </CardHeading>
              <CardToolbar>
                <CollapsibleTrigger
                  render={
                    <Button variant="secondary">
                      Details
                      {isOpen ? <ChevronUp /> : <ChevronDown />}
                    </Button>
                  }
                />
              </CardToolbar>
            </CardHeader>
            <CollapsiblePanel>
              <CardContent className="text-sm space-y-3 rounded-t-none!">
                {stats.map((stat,
      index) => (
                  <div key={index} className="flex items-center justify-between">
                    <span className="text-muted-foreground">{stat.label}</span>
                    <div className="flex items-center space-x-2">
                      <span className="text-foreground font-semibold">
                        ${stat.value.toLocaleString()}
                      </span>
                      <span
                        className={cn(
                          "flex items-center justify-end text-sm font-medium min-w-20",
      stat.changeType === "increase"
                            ? "text-success"
                            : "text-destructive",
      )}
                      >
                        {stat.changeType === "increase" ? (
                          <ArrowUp className="size-3.5" />
                        ) : (
                          <ArrowDown className="size-3.5" />
                        )}
                        {stat.change}
                      </span>
                    </div>
                  </div>
                ))}
              </CardContent>
            </CollapsiblePanel>
          </Collapsible>
        </Card>
      );
    }
    ```
  </Tab>
</Tabs>

### Default Open [#default-open]

Start the collapsible in an open state by default.

<Tabs
  items="['Preview',
'Code']"
>
  <Tab value="Preview">
    <Preview name="DefaultOpenExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { Button } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <div className="w-full max-w-[500px] text-foreground text-sm rounded-lg border border-border p-4">
          <div className="font-medium">Open by Default</div>
          <Collapsible defaultOpen>
            <CollapsiblePanel>
              <div className="mt-3 text-muted-foreground">
                This collapsible starts in the open state by default. Click the
                trigger below to collapse it.
              </div>
            </CollapsiblePanel>

            <div className="text-end mt-2">
              <CollapsibleTrigger
                render={
                  <Button underlined="solid" mode="link" size="sm">
                    Toggle
                  </Button>
                }
              />
            </div>
          </Collapsible>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Disabled [#disabled]

Prevent the collapsible from being toggled.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DisabledCollapsibleExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Button,
      Collapsible,
      CollapsiblePanel,
      CollapsibleTrigger,
    } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <div className="w-full max-w-[500px] text-foreground text-sm rounded-lg border border-border p-4 opacity-50">
          <div className="font-medium">Disabled Collapsible</div>
          <Collapsible disabled>
            <CollapsiblePanel>
              <div className="mt-3 text-muted-foreground">
                This content cannot be toggled because the collapsible is disabled.
              </div>
            </CollapsiblePanel>

            <div className="text-end mt-2">
              <CollapsibleTrigger
                render={
                  <Button underlined="dashed" mode="link" size="sm" disabled>
                    Cannot toggle
                  </Button>
                }
              />
            </div>
          </Collapsible>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

### Nested [#nested]

Create hierarchical collapsible sections by nesting multiple collapsibles.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="NestedCollapsibleExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    "use client";

    import {
      Button,
      Collapsible,
      CollapsiblePanel,
      CollapsibleTrigger,
    } from "@tilt-legal/cubitt-components/primitives";
    import { ChevronDown, ChevronUp } from "@tilt-legal/cubitt-icons/ui/outline";

    export default function Component() {
      const [parentOpen, setParentOpen] = React.useState(false);
      const [child1Open, setChild1Open] = React.useState(false);
      const [child2Open, setChild2Open] = React.useState(false);

      return (
        <div className="w-full max-w-[500px] text-foreground text-sm rounded-lg border border-border p-4">
          <Collapsible open={parentOpen} onOpenChange={setParentOpen}>
            <div className="flex items-center justify-between">
              <div className="font-medium">Parent Section</div>
              <CollapsibleTrigger
                render={
                  <Button variant="ghost" mode="icon">
                    {parentOpen ? <ChevronUp /> : <ChevronDown />}
                  </Button>
                }
              />
            </div>

            <CollapsiblePanel>
              <div className="space-y-3 mt-3 pl-4 border-l-2 border-border">
                {/* Child 1 */}
                <Collapsible open={child1Open} onOpenChange={setChild1Open}>
                  <div className="flex items-center justify-between">
                    <div className="font-medium text-sm">Child Section 1</div>
                    <CollapsibleTrigger
                      render={
                        <Button variant="ghost" mode="icon">
                          {child1Open ? <ChevronUp /> : <ChevronDown />}
                        </Button>
                      }
                    />
                  </div>
                  <CollapsiblePanel>
                    <div className="mt-2 text-muted-foreground text-sm">
                      Nested collapsible content for section 1.
                    </div>
                  </CollapsiblePanel>
                </Collapsible>

                {/* Child 2 */}
                <Collapsible open={child2Open} onOpenChange={setChild2Open}>
                  <div className="flex items-center justify-between">
                    <div className="font-medium text-sm">Child Section 2</div>
                    <CollapsibleTrigger
                      render={
                        <Button variant="ghost" mode="icon">
                          {child2Open ? <ChevronUp /> : <ChevronDown />}
                        </Button>
                      }
                    />
                  </div>
                  <CollapsiblePanel>
                    <div className="mt-2 text-muted-foreground text-sm">
                      Nested collapsible content for section 2.
                    </div>
                  </CollapsiblePanel>
                </Collapsible>
              </div>
            </CollapsiblePanel>
          </Collapsible>
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

***

## API Reference [#api-reference]

### Collapsible [#collapsible]

The root component that manages the collapsible state.

| Prop           | Type                             | Default | Description                                      |
| -------------- | -------------------------------- | ------- | ------------------------------------------------ |
| `open`         | `boolean`                        | -       | The controlled open state of the collapsible.    |
| `defaultOpen`  | `boolean`                        | `false` | The default open state when uncontrolled.        |
| `onOpenChange` | `(open: boolean) => void`        | -       | Callback fired when the open state changes.      |
| `disabled`     | `boolean`                        | `false` | Whether the collapsible is disabled.             |
| `className`    | `string`                         | -       | Additional CSS classes for the collapsible root. |
| `render`       | `React.ReactElement \| function` | -       | Allows replacing the component's HTML element.   |

### CollapsiblePanel [#collapsiblepanel]

The panel component that contains the collapsible content with animated height transitions.

| Prop        | Type                             | Default | Description                                    |
| ----------- | -------------------------------- | ------- | ---------------------------------------------- |
| `className` | `string`                         | -       | Additional CSS classes for the panel.          |
| `render`    | `React.ReactElement \| function` | -       | Allows replacing the component's HTML element. |

The panel includes built-in CSS transitions for smooth expand/collapse animations with automatic height calculation.

### CollapsibleTrigger [#collapsibletrigger]

The trigger component that toggles the collapsible state when clicked.

| Prop        | Type                             | Default | Description                                                                                        |
| ----------- | -------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `render`    | `React.ReactElement \| function` | -       | Allows replacing the component's HTML element. Use this to render as a custom element like Button. |
| `className` | `string`                         | -       | Additional CSS classes for the trigger.                                                            |

When using the `render` prop, the trigger will forward all collapsible state to the rendered element.
