Accordion
A composable set of collapsible content sections with polished defaults.
Open/closed state drives data-state attributes on each item and content element,
letting you add further overrides with Tailwind's data-[state=open]: variants.
Overview
MAccordion manages open/close state and ARIA wiring, and ships with production-quality
defaults — items have a bottom border, triggers are full-width with font weight and hover styling,
and content uses height-based CSS animation driven by data-state.
Use Class on any part to override or extend the defaults.
The component set has four parts: MAccordion (root, manages state), MAccordionItem
(per-item wrapper, exposes data-state), MAccordionTrigger (the toggle button,
includes a built-in chevron that rotates on open), and MAccordionContent (the panel,
always in the DOM so CSS transitions work).
Usage
Add the namespace import:
@using Marai.UI.Components.MAccordionBasic Usage
A bordered accordion with three items. group on each item lets children target
group-data-[state=open]:. The chevron SVG is developer-supplied inside
MAccordionTrigger's ChildContent.
dotnet add package Marai.UI and add the stylesheet reference
in your App.razor or index.html.
.dark class on the root element,
managed through the built-in ThemeService.
<MAccordion Class="w-full divide-y divide-border rounded-md border border-border">
<MAccordionItem Value="item-1" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
What is Marai.UI?
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Marai.UI is a ShadCN-style Blazor component library targeting .NET 10.
It uses Tailwind CSS for styling and CSS custom properties for theming.
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="item-2" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
How do I install it?
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Run <code>dotnet add package Marai.UI</code> and add the stylesheet reference
in your <code>App.razor</code> or <code>index.html</code>.
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="item-3" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
Does it support dark mode?
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Yes. Dark mode is toggled via the <code>.dark</code> class on the root element,
managed through the built-in <code>ThemeService</code>.
</MAccordionContent>
</MAccordionItem>
</MAccordion>
Examples
Multiple Mode
Set Type="MAccordionType.Multiple" to allow any number of items open simultaneously.
The default Single mode closes all other items when one opens.
<MAccordion Type="MAccordionType.Multiple" Class="w-full divide-y divide-border rounded-md border border-border">
<MAccordionItem Value="multi-1" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
Section A
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Content for section A. Both sections can be open at the same time.
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="multi-2" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
Section B
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Content for section B. Open or close independently of Section A.
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="multi-3" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
Section C
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
Content for section C.
</MAccordionContent>
</MAccordionItem>
</MAccordion>
Disabled Item
Pass disabled="true" to the trigger via AdditionalAttributes.
Add disabled:pointer-events-none disabled:opacity-50 to your trigger
Class to apply the visual disabled treatment — the library does not inject these.
<MAccordion Class="w-full divide-y divide-border rounded-md border border-border">
<MAccordionItem Value="dis-1" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors hover:bg-muted">
Available item
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
This item can be opened and closed normally.
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="dis-2" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between px-4 py-3 text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50"
disabled="true">
Disabled item
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="px-4 pb-4 text-sm text-muted-foreground data-[state=closed]:hidden">
This content is unreachable because the trigger is disabled.
</MAccordionContent>
</MAccordionItem>
</MAccordion>
Animated Content
Because MAccordionContent is always in the DOM, CSS transitions work without
JavaScript. Use overflow-hidden transition-all with
data-[state=closed]:max-h-0 data-[state=open]:max-h-96 for a smooth height animation,
or data-[state=closed]:hidden for a simple show/hide.
data-[state=closed]:max-h-0 and data-[state=open]:max-h-96
with overflow-hidden transition-all for a CSS-only transition.
<MAccordion Class="w-full divide-y divide-border">
<MAccordionItem Value="anim-1" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between py-3 text-sm font-medium transition-colors hover:text-foreground">
Animated height
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="overflow-hidden text-sm text-muted-foreground transition-all duration-300 data-[state=closed]:max-h-0 data-[state=open]:max-h-96">
<div class="pb-4">
Content with smooth max-height animation. No border, flush with the page.
This uses <code>data-[state=closed]:max-h-0</code> and <code>data-[state=open]:max-h-96</code>
with <code>overflow-hidden transition-all</code> for a CSS-only transition.
</div>
</MAccordionContent>
</MAccordionItem>
<MAccordionItem Value="anim-2" Class="group">
<MAccordionTrigger Class="flex w-full items-center justify-between py-3 text-sm font-medium transition-colors hover:text-foreground">
Another section
<svg class="h-4 w-4 shrink-0 transition-transform group-data-[state=open]:rotate-180"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9" />
</svg>
</MAccordionTrigger>
<MAccordionContent Class="overflow-hidden text-sm text-muted-foreground transition-all duration-300 data-[state=closed]:max-h-0 data-[state=open]:max-h-96">
<div class="pb-4">
Each section animates independently. Toggle both open to see simultaneous animations.
</div>
</MAccordionContent>
</MAccordionItem>
</MAccordion>
Data State
MAccordionItem and MAccordionContent set a data-state
attribute on their root element — "open" or "closed" — reflecting
the current open/closed state. Use this to drive all visual changes from CSS:
| Element | Attribute | Values |
|---|---|---|
MAccordionItem |
data-state |
"open" / "closed" |
MAccordionContent |
data-state |
"open" / "closed" |
Common Tailwind patterns using data-state:
<!-- Simple show/hide -->
<MAccordionContent Class="data-[state=closed]:hidden">
<!-- Animated height (always-in-DOM enables CSS transition) -->
<MAccordionContent Class="overflow-hidden transition-all data-[state=closed]:max-h-0 data-[state=open]:max-h-96">
<!-- Rotate chevron via group on the item -->
<MAccordionItem Class="group">
<MAccordionTrigger Class="...">
Title
<svg class="transition-transform group-data-[state=open]:rotate-180">...</svg>
</MAccordionTrigger>Accessibility
- The root element uses
role="group"to semantically group accordion items - Each trigger renders as a
<button type="button">witharia-expanded="true/false"(lowercase) andaria-controls - Each content panel has
role="region"andaria-labelledbypointing to its trigger - Content is always in the DOM — screen readers can still reach it, and transitions animate correctly
- All components accept arbitrary ARIA attributes via
AdditionalAttributes
API Reference
MAccordion
| Parameter | Type | Default | Description |
|---|---|---|---|
| Type | MAccordionType | Single |
Single closes all other items when one opens; Multiple allows any number open simultaneously. |
| ChildContent | RenderFragment? | null |
One or more MAccordionItem components. |
| Class | string? | null |
Additional CSS classes applied to the root <div>. User-supplied classes win all conflicts. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes passed through to the root element. |
MAccordionItem
| Parameter | Type | Default | Description |
|---|---|---|---|
| Value | string | required | Unique identifier for this item. Used to track open state and generate ARIA IDs. |
| ChildContent | RenderFragment? | null |
Should contain one MAccordionTrigger and one MAccordionContent. |
| Class | string? | null |
Additional CSS classes on the item wrapper. Default includes a bottom border. Add group to enable group-data-[state=open]: targeting on child elements. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes. The data-state attribute is set by the component and should not be overridden here. |
MAccordionTrigger
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
Label text and any icons (e.g. a chevron SVG). No icon is injected by default. |
| Class | string? | null |
Additional CSS classes on the <button>. Default includes flex layout, font weight, hover underline, and focus ring. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes on the button (e.g. disabled="true", aria-label). |
MAccordionContent
| Parameter | Type | Default | Description |
|---|---|---|---|
| ChildContent | RenderFragment? | null |
The content revealed when the item is open. Always rendered in the DOM. |
| Class | string? | null |
Additional CSS classes on the content <div>. Default includes overflow-hidden transition-all with data-[state=closed]:h-0 for smooth animation. |
| AdditionalAttributes | Dictionary<string, object>? | null |
Arbitrary HTML attributes. The data-state attribute is set by the component. |