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&apos; aesthetic.
        </AccordionContent>
    </AccordionItem>
    <AccordionItem value="item-3">
        <AccordionTrigger>Is it animated?</AccordionTrigger>
        <AccordionContent>
            Yes. It&apos;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&apos; aesthetic.
        </AccordionContent>
    </AccordionItem>
    <AccordionItem value="item-3">
        <AccordionTrigger>Is it animated?</AccordionTrigger>
        <AccordionContent>
            Yes. It&apos;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>