Disclosure
Expand/collapse primitive with accessible trigger and animated panel. Requires @alpinejs/collapse.
Requires Alpine.js
Default
1{{ partial:components/primitives/disclosure label="Toggle disclosure" }}
2 This panel content is revealed when the trigger is clicked. Padding lives on the inner wrapper so the collapse
3 animation stays smooth.
4{{ /partial:components/primitives/disclosure }}
{{ partial:components/primitives/disclosure label="Toggle disclosure" }}
This panel content is revealed when the trigger is clicked. Padding lives on the inner wrapper so the collapse
animation stays smooth.
{{ /partial:components/primitives/disclosure }}
Initially Open
1{{ partial:components/primitives/disclosure label="What is a disclosure?" open="true" }}
2 This disclosure starts expanded. Pass
3 <code>open="true"</code>
4 to set the initial state.
5{{ /partial:components/primitives/disclosure }}
{{ partial:components/primitives/disclosure label="What is a disclosure?" open="true" }}
This disclosure starts expanded. Pass
<code>open="true"</code>
to set the initial state.
{{ /partial:components/primitives/disclosure }}
Custom Trigger
1{{ partial:components/primitives/disclosure }}
2 {{ slot:trigger }}
3 <div class="flex items-center gap-2">
4 {{ svg src="icons/star" class="size-4 shrink-0" aria-hidden="true" }}
5 Show details
6 </div>
7 {{ /slot:trigger }}
8 {{ slot:panel }}
9 Use
10 <code>slot:trigger</code>
11 for any HTML content in the trigger button — icons, badges, or custom layouts.
12 {{ /slot:panel }}
13{{ /partial:components/primitives/disclosure }}
{{ partial:components/primitives/disclosure }}
{{ slot:trigger }}
<div class="flex items-center gap-2">
{{ svg src="icons/star" class="size-4 shrink-0" aria-hidden="true" }}
Show details
</div>
{{ /slot:trigger }}
{{ slot:panel }}
Use
<code>slot:trigger</code>
for any HTML content in the trigger button — icons, badges, or custom layouts.
{{ /slot:panel }}
{{ /partial:components/primitives/disclosure }}
Props
| Name | Type | Default | Description |
|---|---|---|---|
label
|
string
|
Trigger button label text (falls back to slot:trigger, then "Toggle disclosure") | |
open
|
boolean
|
|
Initial expanded state |
class
|
string
|
Additional classes merged via tw_merge (root element only) |
Slots
| Name | Fallback / Default | Description |
|---|---|---|
default
|
||
panel
|
default slot
|
Collapsible panel content (falls back to default slot) |
trigger
|
label
|
Trigger button content as HTML (overrides label) |
Source
1{{#
2 @name Disclosure
3 @desc Expand/collapse primitive with accessible trigger and animated panel. Requires @alpinejs/collapse.
4 @param label string - Trigger button label text (falls back to slot:trigger, then "Toggle disclosure")
5 @param open boolean [false] - Initial expanded state
6 @param class string - Additional classes merged via tw_merge (root element only)
7 @slot trigger - Trigger button content as HTML (overrides label)
8 @slot panel - Collapsible panel content (falls back to default slot)
9#}}
10{{ _class = 'overflow-hidden w-full {class}'
11 | tw_merge }}
12<div
13 class="{{ _class }}"
14 x-data="{ isOpen: {{ open == 'true' ? 'true' : 'false' }} }"
15 x-id="['disclosure-trigger', 'disclosure-panel']"
16>
17 {{# ===== Trigger ===== #}}
18 <button
19 type="button"
20 class="group border-foreground hover:bg-muted focus-visible:border-focus-text focus-visible:outline-focus active:border-focus active:bg-focus active:text-focus-text flex h-10 w-full cursor-pointer items-center justify-between gap-3 border-2 px-4 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-4 aria-expanded:font-bold"
21 :id="$id('disclosure-trigger')"
22 :aria-expanded="isOpen"
23 :aria-controls="$id('disclosure-panel')"
24 @click="isOpen = !isOpen"
25 >
26 {{ if label }}
27 {{ label }}
28 {{ elseif slot:trigger }}
29 {{ slot:trigger }}
30 {{ else }}
31 Toggle disclosure
32 {{ /if }}
33 <span
34 class="text-muted-foreground shrink-0 transition-transform duration-200 group-aria-expanded:rotate-180"
35 aria-hidden="true"
36 >
37 {{ svg src="icons/chevron-down" class="size-5" }}
38 </span>
39 </button>
40 {{# ==== Panel ===== #}}
41 <div
42 x-show="isOpen"
43 x-collapse
44 class="border-foreground border border-t-0"
45 :id="$id('disclosure-panel')"
46 role="region"
47 :aria-labelledby="$id('disclosure-trigger')"
48 style="display: none"
49 >
50 <div class="text-muted-foreground px-4 pt-3 pb-4 text-sm leading-relaxed">
51 {{ if slot:panel }}
52 {{ slot:panel }}
53 {{ else }}
54 {{ slot }}
55 {{ /if }}
56 </div>
57 </div>
58</div>
{{#
@name Disclosure
@desc Expand/collapse primitive with accessible trigger and animated panel. Requires @alpinejs/collapse.
@param label string - Trigger button label text (falls back to slot:trigger, then "Toggle disclosure")
@param open boolean [false] - Initial expanded state
@param class string - Additional classes merged via tw_merge (root element only)
@slot trigger - Trigger button content as HTML (overrides label)
@slot panel - Collapsible panel content (falls back to default slot)
#}}
{{ _class = 'overflow-hidden w-full {class}'
| tw_merge }}
<div
class="{{ _class }}"
x-data="{ isOpen: {{ open == 'true' ? 'true' : 'false' }} }"
x-id="['disclosure-trigger', 'disclosure-panel']"
>
{{# ===== Trigger ===== #}}
<button
type="button"
class="group border-foreground hover:bg-muted focus-visible:border-focus-text focus-visible:outline-focus active:border-focus active:bg-focus active:text-focus-text flex h-10 w-full cursor-pointer items-center justify-between gap-3 border-2 px-4 text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-4 aria-expanded:font-bold"
:id="$id('disclosure-trigger')"
:aria-expanded="isOpen"
:aria-controls="$id('disclosure-panel')"
@click="isOpen = !isOpen"
>
{{ if label }}
{{ label }}
{{ elseif slot:trigger }}
{{ slot:trigger }}
{{ else }}
Toggle disclosure
{{ /if }}
<span
class="text-muted-foreground shrink-0 transition-transform duration-200 group-aria-expanded:rotate-180"
aria-hidden="true"
>
{{ svg src="icons/chevron-down" class="size-5" }}
</span>
</button>
{{# ==== Panel ===== #}}
<div
x-show="isOpen"
x-collapse
class="border-foreground border border-t-0"
:id="$id('disclosure-panel')"
role="region"
:aria-labelledby="$id('disclosure-trigger')"
style="display: none"
>
<div class="text-muted-foreground px-4 pt-3 pb-4 text-sm leading-relaxed">
{{ if slot:panel }}
{{ slot:panel }}
{{ else }}
{{ slot }}
{{ /if }}
</div>
</div>
</div>
Dependencies
Packages
1composer require marcorieser/tailwind-merge-statamic
2npm install alpinejs @alpinejs/collapse
composer require marcorieser/tailwind-merge-statamic npm install alpinejs @alpinejs/collapse