Disclosure
A single show/hide region toggled by a button โ the simplest collapsible primitive.
Basic Disclosure
aria-expanded, aria-controls, and aria-labelledby are wired automatically.
Disclosure is the simpler sibling of Accordion: one trigger, one panel, zero coordination.
<BitBhDisclosure class="w-full overflow-hidden rounded-lg border border-zinc-200 bg-white data-disabled:bg-zinc-50">
<BitBhDisclosureButton class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-3.5 text-base font-medium outline-none hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blue-600 data-state-open:bg-zinc-100 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
Show details
</BitBhDisclosureButton>
<BitBhDisclosurePanel>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
Disclosure is the simpler sibling of Accordion: one trigger, one panel, zero coordination.
</div>
</BitBhDisclosurePanel>
</BitBhDisclosure>Default Open
Set DefaultOpen="true" to render open initially.
I'm open by default. Click the button to close me.
<BitBhDisclosure DefaultOpen="true" class="w-full overflow-hidden rounded-lg border border-zinc-200 bg-white data-disabled:bg-zinc-50">
<BitBhDisclosureButton class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-3.5 text-base font-medium outline-none hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blue-600 data-state-open:bg-zinc-100 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
Already open
</BitBhDisclosureButton>
<BitBhDisclosurePanel>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
I'm open by default. Click the button to close me.
</div>
</BitBhDisclosurePanel>
</BitBhDisclosure>Render Context (rotating chevron + Close)
All three components expose a BitBhDisclosureRenderContext. Use it to drive UI from the open state and call d.Close().
You can close me from inside the panel:
<BitBhDisclosure class="w-full overflow-hidden rounded-lg border border-zinc-200 bg-white data-disabled:bg-zinc-50">
<BitBhDisclosureButton class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-3.5 text-base font-medium outline-none hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blue-600 data-state-open:bg-zinc-100 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
<Template Context="d">
<span>What's inside?</span>
<span class="@($"text-zinc-500 transition-transform duration-200 {(d.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhDisclosureButton>
<BitBhDisclosurePanel>
<Template Context="d">
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
<p>You can close me from inside the panel:</p>
<button class="mt-3 rounded-lg border border-zinc-200 bg-white px-3 py-1.5 text-sm font-medium hover:bg-zinc-100" @onclick="d.Close">Close panel</button>
</div>
</Template>
</BitBhDisclosurePanel>
</BitBhDisclosure>Disabled
data-disabled: dims the row.
You'll never see this content.
<BitBhDisclosure Disabled="true" class="w-full overflow-hidden rounded-lg border border-zinc-200 bg-white data-disabled:bg-zinc-50">
<BitBhDisclosureButton class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-3.5 text-base font-medium outline-none hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blue-600 data-state-open:bg-zinc-100 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
Can't toggle me
</BitBhDisclosureButton>
<BitBhDisclosurePanel>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">You'll never see this content.</div>
</BitBhDisclosurePanel>
</BitBhDisclosure>Controlled
External Open + OnOpenChange.
Open: false
Either trigger updates the same state.
<div class="flex flex-col gap-4 w-full">
<div class="flex flex-wrap items-center gap-3">
<BitBhButton OnClick="ToggleControlled"
class="rounded-lg border border-zinc-200 bg-white px-3 py-1.5 text-sm font-medium hover:bg-zinc-100">
Toggle from outside
</BitBhButton>
<span class="text-sm text-zinc-500">Open: @controlledOpen.ToString().ToLowerInvariant()</span>
</div>
<BitBhDisclosure Open="controlledOpen" OnOpenChange="v => controlledOpen = v"
class="w-full overflow-hidden rounded-lg border border-zinc-200 bg-white data-disabled:bg-zinc-50">
<BitBhDisclosureButton class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-3.5 text-base font-medium outline-none hover:bg-zinc-100 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blue-600 data-state-open:bg-zinc-100 data-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:pointer-events-none">
Internal trigger
</BitBhDisclosureButton>
<BitBhDisclosurePanel>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">Either trigger updates the same state.</div>
</BitBhDisclosurePanel>
</BitBhDisclosure>
</div>
@code {
private bool controlledOpen;
private void ToggleControlled() => controlledOpen = !controlledOpen;
}