Phi
Collapsible
@dicehub/phiv1.0.0-beta.1

Collapsible

A composable disclosure component for showing and hiding content.

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(true);
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.DefaultTrigger>What is Phi?</Collapsible.DefaultTrigger>
    <Collapsible.DefaultPanel>
      Phi is a Vue component library.
    </Collapsible.DefaultPanel>
  </Collapsible>
</template>

Installation

Barrel

import { Collapsible } from "@dicehub/phi";

Granular

import { Collapsible } from "@dicehub/phi/components/collapsible";

Usage

Collapsible uses a compound component pattern for full composition control.

With Default Styling

Use DefaultTrigger and DefaultPanel for Phi styling and smooth height animation. DefaultPanel applies its border and spacing through an inner content wrapper; use Panel when custom layout classes must control the panel element directly.

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(false);
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.DefaultTrigger>Show details</Collapsible.DefaultTrigger>
    <Collapsible.DefaultPanel>
      Content with border-left accent styling.
    </Collapsible.DefaultPanel>
  </Collapsible>
</template>

Custom Trigger

Use as-child on Trigger for full control over the trigger element.

<script setup>
import { ref } from "vue";
import { Button } from "@dicehub/phi/components/button";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(false);
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.Trigger as-child>
      <Button variant="secondary" size="sm">
        {{ open ? "Hide details" : "Show details" }}
      </Button>
    </Collapsible.Trigger>
    <Collapsible.Panel class="custom-panel">
      Custom styled panel content.
    </Collapsible.Panel>
  </Collapsible>
</template>

Examples

Basic

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(false);
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.DefaultTrigger>What is Phi?</Collapsible.DefaultTrigger>
    <Collapsible.DefaultPanel>
      Phi is a Vue component library.
    </Collapsible.DefaultPanel>
  </Collapsible>
</template>

Multiple Items

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const firstOpen = ref(false);
const secondOpen = ref(false);
const thirdOpen = ref(false);
</script>

<template>
  <div class="space-y-2">
    <Collapsible v-model:open="firstOpen">
      <Collapsible.DefaultTrigger>What is Phi?</Collapsible.DefaultTrigger>
      <Collapsible.DefaultPanel>
        Phi is a Vue component library.
      </Collapsible.DefaultPanel>
    </Collapsible>
    <Collapsible v-model:open="secondOpen">
      <Collapsible.DefaultTrigger>How do I use it?</Collapsible.DefaultTrigger>
      <Collapsible.DefaultPanel>
        Install the components and import them into your project.
      </Collapsible.DefaultPanel>
    </Collapsible>
    <Collapsible v-model:open="thirdOpen">
      <Collapsible.DefaultTrigger>Is it open source?</Collapsible.DefaultTrigger>
      <Collapsible.DefaultPanel>
        Check the repository for license information.
      </Collapsible.DefaultPanel>
    </Collapsible>
  </div>
</template>

Custom Trigger

Use Collapsible.Trigger with as-child for full control.

<script setup>
import { ref } from "vue";
import { Button } from "@dicehub/phi/components/button";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(false);
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.Trigger as-child>
      <Button variant="secondary" size="sm">
        {{ open ? "Hide details" : "Show details" }}
      </Button>
    </Collapsible.Trigger>
    <Collapsible.Panel class="custom-panel">
      This panel uses custom styling instead of the default border-left accent.
    </Collapsible.Panel>
  </Collapsible>
</template>

Keep Mounted

Phi panels stay mounted by default. Avoid unmountOnExit when internal state, such as form input, should be preserved while collapsed.

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const open = ref(true);
const name = ref("");
</script>

<template>
  <Collapsible v-model:open="open">
    <Collapsible.DefaultTrigger>Edit details</Collapsible.DefaultTrigger>
    <Collapsible.DefaultPanel>
      <p>
        Type something below, then collapse and re-open. Your input is preserved
        because panels stay mounted by default.
      </p>
      <label>
        <span>Name</span>
        <input v-model="name" placeholder="Type here..." />
      </label>
    </Collapsible.DefaultPanel>
  </Collapsible>
</template>

Accordion Pattern

Control which item is open to create an accordion where only one item can be expanded at a time.

<script setup>
import { ref } from "vue";
import { Collapsible } from "@dicehub/phi/components/collapsible";

const activeIndex = ref(0);

const items = [
  {
    title: "What is Phi?",
    content: "Phi is a Vue component library built on accessible primitives.",
  },
  {
    title: "How do I install it?",
    content: "Install the package and import the components you need.",
  },
  {
    title: "Is it accessible?",
    content: "Yes. Phi components keep keyboard and screen reader behavior in the primitive layer.",
  },
];

const setAccordionItem = (index, open) => {
  activeIndex.value = open ? index : -1;
};
</script>

<template>
  <div class="space-y-2">
    <Collapsible
      v-for="(item, index) in items"
      :key="item.title"
      :open="activeIndex === index"
      @open-change="(details) => setAccordionItem(index, details.open)"
    >
      <Collapsible.DefaultTrigger>{{ item.title }}</Collapsible.DefaultTrigger>
      <Collapsible.DefaultPanel>
        {{ item.content }}
      </Collapsible.DefaultPanel>
    </Collapsible>
  </div>
</template>

Sub-components

ComponentDescription
Collapsible.RootManages open state. Use open and update:open for controlled mode.
Collapsible.TriggerButton that toggles visibility. Use as-child to render a custom trigger component.
Collapsible.PanelContainer for collapsible content.
Collapsible.DefaultTriggerPre-styled trigger with text label and animated caret icon.
Collapsible.DefaultPanelPre-styled panel with border-left accent and standard spacing.

API Reference

Collapsible.Root

PropTypeDefaultDescription
openboolean-Controlled open state. Use v-model:open in Vue.
defaultOpenbooleanfalseInitial open state for uncontrolled usage.
disabledbooleanfalseDisables the trigger and prevents state changes.
lazyMountbooleanfalseMounts the panel only when it first opens.
unmountOnExitbooleanfalseUnmounts the panel after it closes.
collapsedHeightnumber | string-Visible content height while collapsed.
collapsedWidthnumber | string-Visible content width while collapsed.

Collapsible.Trigger

PropTypeDefaultDescription
asChildbooleanfalseUses the provided child as the rendered trigger element.
classstring-Additional CSS classes.

Collapsible.Panel

PropTypeDefaultDescription
classstring-Additional CSS classes.

Collapsible.DefaultTrigger

PropTypeDefaultDescription
default slotunknown-Label content displayed in the trigger.
classstring-Additional CSS classes.

Collapsible.DefaultPanel

PropTypeDefaultDescription
default slotunknown-Panel content.
classstring-Additional CSS classes.