Navigation
accordion
Collapsible disclosure list (single or multiple open).
Yes. It follows the WAI-ARIA disclosure pattern.
Yes. It uses your theme tokens out of the box.
Yes, with the @alpinejs/collapse plugin.
<x-ui.accordion :items="[
'a' => 'Is it accessible?',
'b' => 'Is it styled?',
'c' => 'Is it animated?',
]" class="w-full max-w-md">
<x-slot:item_a>Yes. It follows the WAI-ARIA disclosure pattern.</x-slot:item_a>
<x-slot:item_b>Yes. It uses your theme tokens out of the box.</x-slot:item_b>
<x-slot:item_c>Yes, with the @alpinejs/collapse plugin.</x-slot:item_c>
</x-ui.accordion>
Installation
php artisan ui:add accordion
1. Install dependencies
- composer:
gehrisandro/tailwind-merge-laravel - npm:
alpinejs - npm:
@alpinejs/collapse
2.
Copy the source into
resources/views/components/ui/
resources/views/components/ui/accordion.blade.php
@props([
'items' => [],
'multiple' => false,
])
@php
$rootClasses = \TailwindMerge\Laravel\Facades\TailwindMerge::merge(
'',
$attributes->get('class'),
);
@endphp
<div x-data="{
multiple: @js((bool) $multiple),
open: [],
toggle(key) {
if (this.open.includes(key)) {
this.open = this.open.filter(k => k !== key);
} else {
this.open = this.multiple ? [...this.open, key] : [key];
}
},
}" class="{{ $rootClasses }}"
{{ $attributes->except('class') }}>
@foreach ($items as $key => $label)
<div class="border-b last:border-b-0">
<h3 class="flex">
<button type="button"
@click="toggle(@js($key))"
x-bind:aria-expanded="open.includes(@js($key)).toString()"
class="flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium outline-none transition-all hover:underline focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50">
<span>{{ $label }}</span>
<svg class="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200"
x-bind:class="open.includes(@js($key)) ?
'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">
<path d="m6 9 6 6 6-6" />
</svg>
</button>
</h3>
<div x-show="open.includes(@js($key))" x-collapse
x-cloak class="pb-4 pt-0 text-sm text-muted-foreground">
{{ ${'item_' . $key} ?? '' }}
</div>
</div>
@endforeach
</div>
Usage
<x-ui.accordion :items="['a' => 'Is it accessible?']">
<x-slot:item_a>Yes. It follows the WAI-ARIA disclosure pattern.</x-slot:item_a>
</x-ui.accordion>
Requires Alpine.js with the @alpinejs/collapse plugin (x-collapse). Pass `:items` as ['key' => 'Label']; provide each body via a named slot `item_<key>`. Set `multiple` to allow several open.