Accordion

Disclosure list. Changed to nested syntax in v0.4.0 (naturally pass Blade slots without XSS). Nest <x-accordion-item title="...">content</x-accordion-item> inside <x-accordion>. Single-open or multiple-open, 3 sizes. Content is a Blade slot, so any Blade markup, components, or links work. Alpine x-collapse animates smoothly.

single-open (default) — only one open at a time

First section content.
Second section content.
Third section content.
<x-accordion>
    <x-accordion-item title="Section 1">First section content.</x-accordion-item>
    <x-accordion-item title="Section 2">Second section content.</x-accordion-item>
    <x-accordion-item title="Section 3">Third section content.</x-accordion-item>
</x-accordion>
<div x-data="{ open: null }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_d880133d') ? null : 'item_d880133d'" x-bind:aria-expanded="open === 'item_d880133d'"><span>Section 1</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_d880133d' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_d880133d'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                First section content.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_1dc146c1') ? null : 'item_1dc146c1'" x-bind:aria-expanded="open === 'item_1dc146c1'"><span>Section 2</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_1dc146c1' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_1dc146c1'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Second section content.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_8020ca89') ? null : 'item_8020ca89'" x-bind:aria-expanded="open === 'item_8020ca89'"><span>Section 3</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_8020ca89' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_8020ca89'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Third section content.
            </div>
</div>
</div>
</div>

multiple-open — open several at once

First section content.
Second section content.
Third section content.
<x-accordion :multiple="true">
    <x-accordion-item title="Section 1">First section content.</x-accordion-item>
    <x-accordion-item title="Section 2">Second section content.</x-accordion-item>
    <x-accordion-item title="Section 3">Third section content.</x-accordion-item>
</x-accordion>
<div x-data="{ open: [] }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open.includes('item_a4623301') ? (open = open.filter(i =&gt; i !== 'item_a4623301')) : open.push('item_a4623301')" x-bind:aria-expanded="open.includes('item_a4623301')"><span>Section 1</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open.includes('item_a4623301') &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open.includes('item_a4623301')" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                First section content.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open.includes('item_74b40603') ? (open = open.filter(i =&gt; i !== 'item_74b40603')) : open.push('item_74b40603')" x-bind:aria-expanded="open.includes('item_74b40603')"><span>Section 2</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open.includes('item_74b40603') &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open.includes('item_74b40603')" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Second section content.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open.includes('item_b22723e5') ? (open = open.filter(i =&gt; i !== 'item_b22723e5')) : open.push('item_b22723e5')" x-bind:aria-expanded="open.includes('item_b22723e5')"><span>Section 3</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open.includes('item_b22723e5') &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open.includes('item_b22723e5')" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Third section content.
            </div>
</div>
</div>
</div>

Sizes (sm / md / lg)

Compact rows.
Second section content.
Standard rows.
Second section content.
Roomy rows.
Second section content.
<x-accordion size="sm">
    <x-accordion-item title="size=sm">Compact rows.</x-accordion-item>
    <x-accordion-item title="Section 2">Second section content.</x-accordion-item>
</x-accordion>
<x-accordion size="md">
    <x-accordion-item title="size=md (default)">Standard rows.</x-accordion-item>
    <x-accordion-item title="Section 2">Second section content.</x-accordion-item>
</x-accordion>
<x-accordion size="lg">
    <x-accordion-item title="size=lg">Roomy rows.</x-accordion-item>
    <x-accordion-item title="Section 2">Second section content.</x-accordion-item>
</x-accordion>
<div x-data="{ open: null }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-sm)] px-[var(--px-field-sm)] text-[length:var(--text-field-sm)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_4b6af0ff') ? null : 'item_4b6af0ff'" x-bind:aria-expanded="open === 'item_4b6af0ff'"><span>size=sm</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_4b6af0ff' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_4b6af0ff'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-sm)] px-[var(--px-field-sm)] text-[length:var(--text-field-sm)] py-sm text-base-content/70">
                Compact rows.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-sm)] px-[var(--px-field-sm)] text-[length:var(--text-field-sm)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_a2d6c1b5') ? null : 'item_a2d6c1b5'" x-bind:aria-expanded="open === 'item_a2d6c1b5'"><span>Section 2</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_a2d6c1b5' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_a2d6c1b5'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-sm)] px-[var(--px-field-sm)] text-[length:var(--text-field-sm)] py-sm text-base-content/70">
                Second section content.
            </div>
</div>
</div>
</div>
<div x-data="{ open: null }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_979d2d73') ? null : 'item_979d2d73'" x-bind:aria-expanded="open === 'item_979d2d73'"><span>size=md (default)</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_979d2d73' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_979d2d73'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Standard rows.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_05a17b87') ? null : 'item_05a17b87'" x-bind:aria-expanded="open === 'item_05a17b87'"><span>Section 2</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_05a17b87' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_05a17b87'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
                Second section content.
            </div>
</div>
</div>
</div>
<div x-data="{ open: null }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-lg)] px-[var(--px-field-lg)] text-[length:var(--text-field-lg)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_cad51b12') ? null : 'item_cad51b12'" x-bind:aria-expanded="open === 'item_cad51b12'"><span>size=lg</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_cad51b12' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_cad51b12'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-lg)] px-[var(--px-field-lg)] text-[length:var(--text-field-lg)] py-sm text-base-content/70">
                Roomy rows.
            </div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-lg)] px-[var(--px-field-lg)] text-[length:var(--text-field-lg)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_ea94bf31') ? null : 'item_ea94bf31'" x-bind:aria-expanded="open === 'item_ea94bf31'"><span>Section 2</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_ea94bf31' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_ea94bf31'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-lg)] px-[var(--px-field-lg)] text-[length:var(--text-field-lg)] py-sm text-base-content/70">
                Second section content.
            </div>
</div>
</div>
</div>

Rich content — since it is a Blade slot, any markup / component / link / list / multiple paragraphs work naturally

Content containing a link to an external resource. See the official docs.

  • Bullet 1
  • Bullet 2
  • Bullet 3

The body is a Blade slot, so any HTML or Blade component can be embedded.

A second paragraph. Line breaks and styling are free-form.

<x-accordion>
    <x-accordion-item title="With a link">
        <p>Content containing a link to an external resource. See the <a href="#" class="text-primary underline underline-offset-2 hover:opacity-80">official docs</a>.</p>
    </x-accordion-item>
    <x-accordion-item title="With a list">
        <ul class="list-disc list-inside space-y-1">
            <li>Bullet 1</li>
            <li>Bullet 2</li>
            <li>Bullet 3</li>
        </ul>
    </x-accordion-item>
    <x-accordion-item title="Multiple paragraphs">
        <p class="mb-2">The body is a Blade slot, so any HTML or Blade component can be embedded.</p>
        <p>A second paragraph. Line breaks and styling are free-form.</p>
    </x-accordion-item>
</x-accordion>
<div x-data="{ open: null }" class="w-full divide-y divide-base-300 radius-box tune-border border-base-300 overflow-hidden">
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_e55fc371') ? null : 'item_e55fc371'" x-bind:aria-expanded="open === 'item_e55fc371'"><span>With a link</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_e55fc371' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_e55fc371'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
<p>
                    Content containing a link to an external resource. See the <a href="#" class="text-primary underline underline-offset-2 hover:opacity-80">official docs</a>.
                </p>
</div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_bdd74600') ? null : 'item_bdd74600'" x-bind:aria-expanded="open === 'item_bdd74600'"><span>With a list</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_bdd74600' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_bdd74600'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
<ul class="list-disc list-inside space-y-1">
<li>Bullet 1
                    </li>
<li>Bullet 2
                    </li>
<li>Bullet 3
                    </li>
</ul>
</div>
</div>
</div>
<div>
<button type="button" class="w-full flex items-center justify-between min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-0 font-medium text-base-content hover:bg-base-200/50 transition-colors cursor-pointer" x-on:click="open = (open === 'item_cacbad78') ? null : 'item_cacbad78'" x-bind:aria-expanded="open === 'item_cacbad78'"><span>Multiple paragraphs</span>
<svg class="w-4 h-4 shrink-0 transition-transform duration-200" x-bind:class="open === 'item_cacbad78' &amp;&amp; 'rotate-180'" fill="none" viewbox="0 0 24 24" stroke="currentcolor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path></svg></button>
<div x-show="open === 'item_cacbad78'" x-collapse="" x-cloak="">
<div class="min-h-[var(--h-field-md)] px-[var(--px-field-md)] text-[length:var(--text-field-md)] py-sm text-base-content/70">
<p class="mb-2">
                    The body is a Blade slot, so any HTML or Blade component can be embedded.
                </p>
<p>
                    A second paragraph. Line breaks and styling are free-form.
                </p>
</div>
</div>
</div>
</div>

FAQ demo — a realistic use case (single-open)

An in-house Blade component library by Yakaze Tech Studio, built on Tailwind v4 + daisyUI v5 + Alpine.js. It aims to keep UI consistency across projects.
Run composer require sparrowhawk-labs/pinion-ui, then php artisan ui:install to wire up the required resources.
Yes. pinion-ui uses daisyUI classes internally, so it shares the same theme variables. You can freely mix <x-...> with regular daisyUI markup.
Define your theme with daisyUI v5's @plugin "daisyui/theme" directive. Every pinion-ui class goes through theme variables, so changing the theme is enough to update the colors.
Please report it via GitHub Issues. Reproduction steps and screenshots are appreciated. sparrowhawk-labs/pinion-ui#issues

FAQ (multiple-open) — for comparing items side by side

A Blade component library built on Tailwind v4 + daisyUI v5 + Alpine.js.
composer requirephp artisan ui:install.
Light only (pinion) since v0.4.0. If you need dark, specify daisyUI's standard dark / dim / night on <html data-theme>.