accordions
A basic accordion for frequently asked questions.
This Faqs Accordion component is designed to present a series of items that can be expanded or collapsed, allowing users to view content in a compact manner.
The accordion component is composed of two main files:
accordion/index.blade.php
): Serves as the main wrapper for the accordion component.accordion/item.blade.php
): Manages individual items within the accordion.The container is the outer wrapper for the entire accordion component. It sets up the Alpine.js active
state, which helps manage which item is open at any given time.
<div x-data="{ active: null }" {{ $attributes->merge([ 'class'=>"w-full min-h-fit space-y-4 rounded-xl pb-4" ]) }} > {{ $slot }} </div>
Each accordion item component represents an individual expandable item within the accordion. This component uses Alpine.js to manage its own expand/collapse behavior based on the active state from the container.
<div role="region" x-data="{ id: $id('accordion'), toggle() { this.isVisible = !this.isVisible; }, get isVisible() { return this.active === this.id }, set isVisible(value) { this.active = value ? this.id : null }, }" {{ $attributes->merge([ 'class' => 'rounded-lg dark:text-gray-400 text-gray-800 ', ]) }}> <h2> <button {{ $question->attributes->merge(['class' => 'flex w-full items-center justify-between px-6 py-4 text-xl font-bold']) }} x-on:click="toggle()" x-bind:aria-expanded="isVisible"> <span >{{ $question }}</span> <span class="ml-4" aria-hidden="true" x-show="isVisible">−</span> <span class="ml-4" aria-hidden="true" x-show="!isVisible">+</span> </button> </h2> <div style="display: none" x-show="isVisible" x-collapse> <div {{ $response->attributes->merge(['class' => 'px-6 pb-4 pt-2']) }} > {{ $response }} </div> </div> </div>
So, the JavaScript here is managing whether each accordion item is open or closed. When we set up x-data, we’re creating an isolated scope for each item, which is super helpful because each item controls its own open and close behavior without interfering with others.
Here's the breakdown:
id: $id('accordion')
: This line is really just creating a unique ID for each item. The $id('accordion')
generates a unique identifier each time, which is crucial because our accordion logic depends on each item knowing who it is. Think of it as giving each item its own “name tag.”
toggle()
: This is a function that we run whenever the user clicks on the accordion item. What toggle()
does is flip the visibility of the item. So, if the item is closed, toggle()
opens it, and if it’s open, toggle()
closes it.
get isVisible()
: Here’s where it gets interesting. We use a getter called isVisible to determine whether the item should be shown or hidden. Every item is essentially asking, “Am I the active one?” It checks by comparing its unique id
(remember the name tag we set) to the active
ID on the parent accordion. If they match, isVisible
will return true
, meaning the item should be displayed.`
set isVisible(value)
: The setter for isVisible is equally important. When we set isVisible
to true
, this setter assigns the current item’s id to active
, marking it as the open one. If we set isVisible
to false
, it clears active
, effectively closing the item. This setter is what lets us control opening and closing by clicking each item.
In simple terms, when you click an item, toggle()
runs, which changes isVisible
by updating active
in the parent accordion. Then, only the item with the matching id
stays open.
That’s the core idea!
Once you have the core files in place, you’re ready to test and adjust your component. Here’s a quick start example to get you going.
<x-accordion> <x-accordion.item class="dark:bg-white/5 bg-white shadow"> <x-slot:question> # Question 1 </x-slot> <x-slot:response> Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia, eaque delectus libero atque magnam repellendus iure voluptatum odio eius laudantium officiis repudiandae beatae quasi maxime commodi placeat quia deserunt qui </x-slot> </x-accordion.item> <x-accordion.item class="dark:bg-white/5 bg-white shadow"> <x-slot:question> # Question 2 </x-slot> <x-slot:response> Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia, eaque delectus libero atque magnam repellendus iure voluptatum odio eius laudantium officiis repudiandae beatae quasi maxime commodi placeat quia deserunt qui </x-slot> </x-accordion.item> </x-accordion>
1<div
2 x-data="{ active: null }"
3 {{ $attributes->merge([
4 'class'=>"w-full min-h-fit space-y-4 rounded-xl pb-4"
5 ]) }}
6>
7 {{ $slot }}
8</div>
1<div
2 role="region"
3 x-data="{
4 id: $id('accordion'),
5 toggle() {
6 this.isVisible = !this.isVisible;
7 },
8 get isVisible() {
9 return this.active === this.id
10 },
11 set isVisible(value) {
12 this.active = value ? this.id : null
13 },
14 }"
15 {{ $attributes->merge([
16 'class' => 'rounded-lg dark:text-gray-400 text-gray-800 ',
17 ]) }}>
18 <h2>
19 <button
20 {{ $question->attributes->merge(['class' => 'flex w-full items-center justify-between px-6 py-4 text-xl font-bold']) }}
21 x-on:click="toggle()" x-bind:aria-expanded="isVisible">
22 <span >{{ $question }}</span>
23 <span class="ml-4" aria-hidden="true" x-show="isVisible">−</span>
24 <span class="ml-4" aria-hidden="true" x-show="!isVisible">+</span>
25 </button>
26 </h2>
27
28 <div style="display: none" x-show="isVisible" x-collapse>
29 <div
30 {{ $response->attributes->merge(['class' => 'px-6 pb-4 pt-2']) }}
31 >
32 {{ $response }}
33 </div>
34 </div>
35</div>
1<x-accordion>
2 <x-accordion.item class="dark:bg-white/5 bg-white shadow">
3 <x-slot:question>
4 # Question 1
5 </x-slot>
6 <x-slot:response>
7 Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia, eaque delectus libero atque magnam repellendus iure voluptatum odio eius laudantium officiis repudiandae beatae quasi maxime commodi placeat quia deserunt qui
8 </x-slot>
9 </x-accordion.item>
10 <x-accordion.item class="dark:bg-white/5 bg-white shadow">
11 <x-slot:question>
12 # Question 2
13 </x-slot>
14 <x-slot:response>
15 Lorem ipsum dolor sit amet consectetur adipisicing elit. Officia, eaque delectus libero atque magnam repellendus iure voluptatum odio eius laudantium officiis repudiandae beatae quasi maxime commodi placeat quia deserunt qui
16 </x-slot>
17 </x-accordion.item>
18</x-accordion>