Accordion
A vertically stacked set of interactive headings that each reveal a section of content.
Yes. It adheres to the WAI-ARIA design pattern.
Yes. It comes with default styles that matches the other
components' aesthetic.
Yes. It's animated by default, but you can disable it if you
prefer.
<Accordion className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that matches the other
components' aesthetic.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It's animated by default, but you can disable it if you
prefer.
</AccordionContent>
</AccordionItem>
</Accordion>
Installation
uvx --from basic-components components add accordion
pipx run --spec basic-components components add accordion
pip install basic-components && components add accordion
- Copy Accordion components below to your local environment
- Place them in the components/ui/accordion directory in your project
- Configure your jinja environment for JinjaX
- Add the cn() helper function to your global jinja environment
Usage
<Accordion className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that matches the other
components' aesthetic.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It's animated by default, but you can disable it if you
prefer.
</AccordionContent>
</AccordionItem>
</Accordion>
Code
{#def
className: str = ""
#}
<div
x-data="{
activeItem: undefined,
toggleItem(value) {
this.activeItem = this.activeItem === value ? undefined : value;
}
}"
class="{{ className }} w-full"
{{ attrs.render() }}
>
{{ content }}
</div>
{#def
className: str = ""
#}
<div
x-show="isOpen"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 max-h-0"
x-transition:enter-end="opacity-100 max-h-xl"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 max-h-xl"
x-transition:leave-end="opacity-0 max-h-0"
class="{{ className }} overflow-hidden text-sm"
{{ attrs.render() }}
>
<div class="pb-4 pt-0">{{ content }}</div>
</div>
{#def
value: str = "",
className: str = ""
#}
<div
x-data="{
get isOpen() {
return activeItem === '{{ value }}';
}
}"
class="{{ className }} border-b"
value="{{ value }}"
{{ attrs.render() }}
>
{{ content }}
</div>
{#def
className: str = ""
#}
<h3 class="flex">
<button
type="button"
class="{{ className }} flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline"
@click="toggleItem($el.closest('[value]').getAttribute('value'))"
:aria-expanded="isOpen"
{{ attrs.render() }}
>
{{ content }}
<template x-if="isOpen">
<ChevronUpIcon className="h-4 w-4 shrink-0 transition-transform duration-200"/>
</template>
<template x-if="!isOpen">
<ChevronDownIcon className="h-4 w-4 shrink-0 transition-transform duration-200"/>
</template>
</button>
</h3>