Accordion
Vertically stacked interactive sections to organize content.
TailwindCSS
Alpine.js
Yes. It adheres to the WAI-ARIA design pattern.
Yes. It comes with default styles that match the other components' aesthetic.
Yes. It's animated by default, but you can disable it if you prefer.
package showcase
import "github.com/axzilla/templui/pkg/components"
templ Accordion() {
@components.Accordion(components.AccordionProps{
Items: []components.AccordionItem{
{
ID: "item-1",
Trigger: templ.Raw("Is it accessible?"),
Content: templ.Raw("Yes. It adheres to the WAI-ARIA design pattern."),
},
{
ID: "item-2",
Trigger: templ.Raw("Is it styled?"),
Content: templ.Raw("Yes. It comes with default styles that match the other components' aesthetic."),
},
{
ID: "item-3",
Trigger: templ.Raw("Is it animated?"),
Content: templ.Raw("Yes. It's animated by default, but you can disable it if you prefer."),
},
},
Class: "w-full sm:max-w-[70%]",
})
}
package components
import (
"github.com/axzilla/templui/pkg/icons"
"github.com/axzilla/templui/pkg/utils"
)
type AccordionItem struct {
ID string // Unique identifier for state management
Trigger templ.Component // Header content that toggles section
Content templ.Component // Expandable section content
}
// AccordionProps configures the Accordion component
type AccordionProps struct {
Items []AccordionItem // Array of accordion sections
Class string // Additional CSS classes
Attributes templ.Attributes // Additional HTML attributes
}
templ AccordionScript() {
{{ handle := templ.NewOnceHandle() }}
@handle.Once() {
<script defer nonce={ templ.GetNonce(ctx) }>
document.addEventListener('alpine:init', () => {
Alpine.data('accordion', () => ({
activeItem: null,
toggleItem(event) {
const itemId = event.target.getAttribute('data-accordion-item');
this.activeItem = this.activeItem === itemId ? null : itemId;
},
isActive() {
const itemId = this.$el.getAttribute('data-accordion-item');
return this.activeItem === itemId
},
}));
});
</script>
}
}
// Accordion renders a collapsible content container
templ Accordion(props AccordionProps) {
<div
x-data="accordion"
class={ utils.TwMerge(
// Layout
"divide-y rounded-md",
// Styling
"divide-border border",
// Custom
props.Class,
) }
{ props.Attributes... }
>
for _, item := range props.Items {
<div class="group">
<h3>
<button
type="button"
@click="toggleItem"
data-accordion-item={ item.ID }
class={ utils.TwMerge(
// Layout
"flex w-full items-center justify-between py-4 px-5",
// Styling
"text-left font-medium",
// States
"transition-all hover:underline [&[aria-expanded=true]>svg]:rotate-180",
) }
>
@item.Trigger
@icons.ChevronDown(icons.IconProps{Size: "16"})
</button>
</h3>
<div
data-accordion-item={ item.ID }
x-show="isActive"
class={ utils.TwMerge(
// Layout
"px-5 pb-4 pt-0",
) }
>
@item.Content
</div>
</div>
}
</div>
}
Usage
1. Add the script to your page/layout:
// Option A: All components (recommended)
@utils.ComponentScripts()
// Option B: Just Accordion
@components.AccordionScript()
2. Use the component:
@components.Accordion(components.AccordionProps{...})