Componentaccordion
Accordion
A vertically stacked set of interactive collapsible panels.
Preview
A vertically stacked set of interactive collapsible panels.
Is it accessible?
Is it styled?
Is it animated?
@ui.Accordion(ui.AccordionProps{Type: "single", Collapsible: true}) {
@ui.AccordionItem(ui.AccordionItemProps{Value: "item-1"}) {
@ui.AccordionTrigger(ui.DOMProps{}) { Is it accessible? }
@ui.AccordionContent(ui.DOMProps{}) { Yes. It adheres to the WAI-ARIA design pattern. }
}
}Installation
Add this component to your project using the CLI.
templcn add accordionUsage
1. Import the component in your Go or templ file:
import "your-module/ui"2. Use the component in your template:
@ui.Accordion(ui.AccordionProps{Type: "single", Collapsible: true}) {
@ui.AccordionItem(ui.AccordionItemProps{Value: "item-1"}) {
@ui.AccordionTrigger(ui.DOMProps{}) { Is it accessible? }
@ui.AccordionContent(ui.DOMProps{}) { Yes. It adheres to the WAI-ARIA design pattern. }
}
}Composition
Use these parts together to build the component.
Accordion
├── AccordionItem
├── AccordionTrigger
├── AccordionContentExamples
Named examples and common variations.
Single (collapsible)
One item open at a time, with collapse allowed.
Is it accessible?
Is it styled?
Is it animated?
@ui.Accordion(ui.AccordionProps{Type: "single", Collapsible: true}) {
@ui.AccordionItem(ui.AccordionItemProps{Value: "item-1"}) {
@ui.AccordionTrigger(ui.DOMProps{}) { Is it accessible? }
@ui.AccordionContent(ui.DOMProps{}) { Yes. It adheres to the WAI-ARIA design pattern. }
}
}Multiple
Multiple items can be open simultaneously.
What is templcn/ui?
Is it open source?
@ui.Accordion(ui.AccordionProps{Type: "multiple"}) {
@ui.AccordionItem(ui.AccordionItemProps{Value: "a"}) {
@ui.AccordionTrigger(ui.DOMProps{}) { Item A }
@ui.AccordionContent(ui.DOMProps{}) { Content for item A. }
}
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| Type | string | "single" | "single" allows one panel open at a time; "multiple" allows many. |
| Collapsible | bool | false | When type is single, allows the open item to be collapsed. |
| Value | []string | nil | The controlled open state. |
| DefaultValue | []string | nil | The uncontrolled default open state. |
View source
package ui
import (
"context"
"io"
"github.com/a-h/templ"
)
type AccordionProps struct {
DOMProps
Type string
Value []string
DefaultValue []string
Collapsible bool
}
type AccordionItemProps struct {
DOMProps
Value string
Disabled bool
}
type accordionRenderState struct {
openValues map[string]struct{}
}
type accordionRenderStateKey struct{}
func Accordion(props AccordionProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props.DOMProps, "accordion", "")
if props.Type != "" {
attrs["data-type"] = props.Type
}
if len(props.Value) > 0 {
attrs["data-value"] = props.Value
}
if len(props.DefaultValue) > 0 {
attrs["data-default-value"] = props.DefaultValue
}
if props.Collapsible {
attrs["data-collapsible"] = "true"
}
openValues := map[string]struct{}{}
values := props.DefaultValue
if len(props.Value) > 0 {
values = props.Value
}
for _, value := range values {
openValues[value] = struct{}{}
}
ctx = context.WithValue(ctx, accordionRenderStateKey{}, accordionRenderState{openValues: openValues})
return renderElement(ctx, w, "div", attrs, templ.GetChildren(ctx))
})
}
func AccordionItem(props AccordionItemProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props.DOMProps, "accordion-item", "border-b last:border-b-0")
if props.Value != "" {
attrs["data-value"] = props.Value
}
open := accordionValueOpen(ctx, props.Value)
attrs["data-state"] = openState(open)
if open {
attrs["open"] = true
}
if props.Disabled {
attrs["data-disabled"] = "true"
attrs["aria-disabled"] = "true"
}
ctx = context.WithValue(ctx, accordionItemOpenKey{}, open)
return renderElement(ctx, w, "details", attrs, templ.GetChildren(ctx))
})
}
type accordionItemOpenKey struct{}
func AccordionTrigger(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
ctx, ownChildren := childrenFromContext(ctx)
children := templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
if err := renderChildren(ctx, w, ownChildren); err != nil {
return err
}
_, err := io.WriteString(w, `<svg xmlns="http://www.w3.org/2000/svg" class="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180" 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"></path></svg>`)
return err
})
attrs := attrsFromDOMProps(props, "accordion-trigger", "group flex flex-1 cursor-pointer list-none items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180")
attrs["aria-expanded"] = map[bool]string{true: "true", false: "false"}[accordionItemOpen(ctx)]
return renderElement(ctx, w, "summary", attrs, children)
})
}
func AccordionContent(props DOMProps) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
attrs := attrsFromDOMProps(props, "accordion-content", "overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down")
if _, ok := attrs["data-state"]; !ok {
attrs["data-state"] = openState(accordionItemOpen(ctx))
}
inner := templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
return renderElement(ctx, w, "div", templ.Attributes{"class": "pt-0 pb-4"}, templ.GetChildren(ctx))
})
return renderElement(ctx, w, "div", attrs, inner)
})
}
func accordionValueOpen(ctx context.Context, value string) bool {
state, ok := ctx.Value(accordionRenderStateKey{}).(accordionRenderState)
if !ok || value == "" {
return false
}
_, ok = state.openValues[value]
return ok
}
func accordionItemOpen(ctx context.Context) bool {
open, ok := ctx.Value(accordionItemOpenKey{}).(bool)
return ok && open
}