M
Marai.UI
v1.0.0-alpha.7

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:

razor
@using Marai.UI.Components.MAccordion

Basic 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.

Preview
Marai.UI is a ShadCN-style Blazor component library targeting .NET 10. It uses Tailwind CSS for styling and CSS custom properties for theming.
Run dotnet add package Marai.UI and add the stylesheet reference in your App.razor or index.html.
Yes. Dark mode is toggled via the .dark class on the root element, managed through the built-in ThemeService.
razor
<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.

Preview
Content for section A. Both sections can be open at the same time.
Content for section B. Open or close independently of Section A.
Content for section C.
razor
<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.

Preview
This item can be opened and closed normally.
This content is unreachable because the trigger is disabled.
razor
<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.

Preview
Content with smooth max-height animation. No border, flush with the page. This uses data-[state=closed]:max-h-0 and data-[state=open]:max-h-96 with overflow-hidden transition-all for a CSS-only transition.
Each section animates independently. Toggle both open to see simultaneous animations.
razor
<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:

razor
<!-- 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"> with aria-expanded="true/false" (lowercase) and aria-controls
  • Each content panel has role="region" and aria-labelledby pointing 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.