Disclosure
A single expandable section — a button that shows or hides a panel of content. Use it for FAQs, collapsible details, "show more" sections, or to tuck away secondary information until it's needed. Built on Headless UI's disclosure, so the aria-expanded/aria-controls wiring, keyboard support, and focus are handled for you.
A disclosure is independent: each one manages its own open state. Stack several to form an FAQ or accordion-style list.
Anatomy
Disclosure ← the root; manages open/closed state
├── DisclosureButton ← the trigger row (shows a rotating chevron)
└── DisclosurePanel ← the collapsible contentThe button's content is the heading; the panel's content is what's revealed. The chevron rotates automatically with the open state.
Usage
<script setup lang="ts">
import { Disclosure, DisclosureTrigger, DisclosureContent } from "@dlbcodes/ui";
</script>
<template>
<div class="w-full max-w-md">
<Disclosure>
<DisclosureTrigger>Is team pricing available?</DisclosureTrigger>
<DisclosureContent>
Yes — you can buy a single license and share it with your whole
team.
</DisclosureContent>
</Disclosure>
</div>
</template>
Open by default
Set defaultOpen on the Disclosure to have it expanded initially.
<script setup lang="ts">
import { Disclosure, DisclosureTrigger, DisclosureContent } from "@dlbcodes/ui";
</script>
<template>
<div class="w-full max-w-md">
<Disclosure :default-open="true">
<DisclosureTrigger>Already expanded</DisclosureTrigger>
<DisclosureContent>
This panel starts open because <code>defaultOpen</code> is set.
</DisclosureContent>
</Disclosure>
</div>
</template>
FAQ list
Stack disclosures to build an FAQ. Each is independent — opening one doesn't close the others.
<script setup lang="ts">
import { Disclosure, DisclosureTrigger, DisclosureContent } from "@dlbcodes/ui";
const faqs = [
{
q: "Can I cancel anytime?",
a: "Yes, you can cancel your subscription at any point from your account settings.",
},
{
q: "Do you offer refunds?",
a: "We offer a 30-day money-back guarantee on all annual plans.",
},
{
q: "Is there a free trial?",
a: "Every plan comes with a 14-day free trial — no card required.",
},
];
</script>
<template>
<div class="w-full max-w-md">
<Disclosure v-for="faq in faqs" :key="faq.q">
<DisclosureTrigger>{{ faq.q }}</DisclosureTrigger>
<DisclosureContent>{{ faq.a }}</DisclosureContent>
</Disclosure>
</div>
</template>
Custom trigger
DisclosureButton exposes the open state via its default slot, so you can change the label or content based on whether the panel is open.
<script setup lang="ts">
import { Disclosure, DisclosureTrigger, DisclosureContent } from "@dlbcodes/ui";
</script>
<template>
<div class="w-full max-w-md">
<Disclosure>
<DisclosureTrigger>
<template #default="{ open }">
{{ open ? "Hide details" : "Show details" }}
</template>
</DisclosureTrigger>
<DisclosureContent>
The button label changes based on the open state, via the
<code>open</code> slot prop.
</DisclosureContent>
</Disclosure>
</div>
</template>
Props
Disclosure
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | false | Whether the panel is expanded initially. |
class | string | — | Classes merged onto the root. |
Exposes open (boolean) via its default slot.
DisclosureButton
Takes only class. Its slot content is the trigger label, and it exposes open (boolean) via the default slot. A chevron is included automatically and rotates with the open state.
DisclosurePanel
Takes only class. Its slot content is the collapsible body.
Accessibility
- Built on Headless UI's disclosure, so the button gets
aria-expandedandaria-controls, and the panel is associated with the button via generated ids. - The button is a real, focusable
<button>;Enter/Spacetoggle the panel. - Each disclosure is independent — for a true single-open accordion (opening one closes the others), you'd coordinate their state yourself.