Accordion
A vertically stacked set of collapsible sections, single or multiple open at a time.
Basic Accordion (Single)
Only one item can be open at a time. DefaultValue seeds the initial open item. Each trigger gets aria-expanded and aria-controls automatically.
bit Bheadless is a headless component library for Blazor.
Behaviour and accessibility are built in; styling is entirely up to you.
You own the look and feel entirely. ARIA and keyboard support never have to be re-implemented.
Use
data-state="open"|"closed" on any element, or just apply classes. The library emits no styles of its own.
<BitBhAccordion DefaultValue="item-1" class="w-full overflow-hidden rounded-lg border border-zinc-200">
<BitBhAccordionItem Value="item-1" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
What is bit Bheadless?
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
bit Bheadless is a headless component library for Blazor.
Behaviour and accessibility are built in; styling is entirely up to you.
</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
<BitBhAccordionItem Value="item-2" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
Why headless?
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
You own the look and feel entirely. ARIA and keyboard support never have to be re-implemented.
</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
<BitBhAccordionItem Value="item-3" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
How do I style it?
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">
Use <code>data-state="open"|"closed"</code> on any element, or just apply classes. The library emits no styles of its own.
</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
</BitBhAccordion>Multiple Open Items
Set Type="BitBhAccordionType.Multiple".
Yes — every panel is independent. Try opening both!
No — both can be open at once.
<BitBhAccordion Type="BitBhAccordionType.Multiple" class="w-full overflow-hidden rounded-lg border border-zinc-200">
<BitBhAccordionItem Value="faq-1" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
Can I open multiple panels?
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">Yes — every panel is independent. Try opening both!</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
<BitBhAccordionItem Value="faq-2" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
Does this one close when I open another?
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">No — both can be open at once.</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
</BitBhAccordion>Disabled Item
Individual Disabled="true" on each item.
This item works normally.
You should not be able to open this.
<BitBhAccordion class="w-full overflow-hidden rounded-lg border border-zinc-200">
<BitBhAccordionItem Value="d-1" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
I am enabled
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">This item works normally.</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
<BitBhAccordionItem Value="d-2" Disabled="true" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
I am disabled
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">You should not be able to open this.</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
</BitBhAccordion>Controlled Accordion
Drive Value + OnValueChange from outside.
Open: none
Controlled by external state.
This panel is toggled by the button above.
<div class="flex flex-col gap-4 w-full">
<div class="flex flex-wrap items-center gap-3">
<BitBhButton OnClick="ToggleControlledItem"
class="rounded-lg border border-zinc-200 bg-white px-3 py-1.5 text-sm font-medium hover:bg-zinc-100">
Toggle "Panel 2"
</BitBhButton>
<span class="text-sm text-zinc-500">Open: @(controlledValue ?? "none")</span>
</div>
<BitBhAccordion Value="@controlledValue" OnValueChange="v => controlledValue = v" class="w-full overflow-hidden rounded-lg border border-zinc-200">
<BitBhAccordionItem Value="ctrl-1" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
Panel 1
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">Controlled by external state.</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
<BitBhAccordionItem Value="ctrl-2" class="border-b border-zinc-200 last:border-b-0 data-disabled:bg-zinc-50">
<BitBhAccordionTrigger class="flex w-full cursor-pointer items-center justify-between border-0 bg-transparent px-5 py-4 text-base font-medium outline-none box-border 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="t">
Panel 2
<span class="@($"text-zinc-500 transition-transform duration-200 {(t.IsOpen ? "rotate-180" : "")}")">▾</span>
</Template>
</BitBhAccordionTrigger>
<BitBhAccordionContent>
<div class="border-t border-zinc-200 px-5 py-3.5 text-sm text-zinc-500">This panel is toggled by the button above.</div>
</BitBhAccordionContent>
</BitBhAccordionItem>
</BitBhAccordion>
</div>
@code {
private string? controlledValue;
private void ToggleControlledItem()
{
controlledValue = controlledValue == "ctrl-2" ? null : "ctrl-2";
}
}