BasecoatUI components for htmy.

Accordion

Example:

Accordion summary 1

1. Multiline content for accordion 1

2. Multiline content for accordion 1

3. Multiline content for accordion 1

Accordion summary 2

1. Multiline content for accordion 2

2. Multiline content for accordion 2

3. Multiline content for accordion 2

Accordion summary 3

1. Multiline content for accordion 3

2. Multiline content for accordion 3

3. Multiline content for accordion 3

Accordion summary 4

1. Multiline content for accordion 4

2. Multiline content for accordion 4

3. Multiline content for accordion 4

Accordion summary 5

1. Multiline content for accordion 5

2. Multiline content for accordion 5

3. Multiline content for accordion 5


Code example:

from htmy import ComponentType, html

from htmui.basecoat import accordion


def example() -> ComponentType:
    return accordion.accordion(
        *(
            (
                _summary_template.format(i=i),
                html.div(*(html.p(_content_template.format(line=line, i=i)) for line in range(1, 4))),
            )
            for i in range(1, 6)
        ),
        root_class="w-full",
    )


_summary_template = "Accordion summary {i}"
_content_template = "{line}. Multiline content for accordion {i}"

Component implementation:

For more details, see the BasecoatUI documentation.

from __future__ import annotations

from typing import TYPE_CHECKING

from htmy import ComponentType, SafeStr, html
from htmy.utils import join

if TYPE_CHECKING:
    from typing import TypeAlias

    AccordionContent: TypeAlias = ComponentType
    AccordionSummary: TypeAlias = ComponentType
    AccordionItem: TypeAlias = tuple[AccordionSummary, AccordionContent]


__version__ = "0.1.0"
__framework__ = "BasecoatUI"
__framework_version__ = "0.3"
__framework_url__ = "https://basecoatui.com/components/accordion/"


accordion_icon = SafeStr(
    '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
    'stroke="currentColor" class="size-5 shrink-0 transition-transform duration-200 group-open:rotate-180">'
    '<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /></svg>'
)
"""`chevron-down` icon from https://heroicons.com/ with additional styling."""


def summary_content(children: ComponentType) -> ComponentType:
    return html.h2(
        children,
        accordion_icon,
        class_=(
            "flex flex-1 items-start justify-between gap-4 py-4 "
            "text-left text-sm font-medium hover:underline"
        ),
    )


def summary(children: AccordionSummary, *, class_: str | None = None) -> ComponentType:
    return html.summary(
        summary_content(children),
        class_=join(
            (
                "w-full focus-visible:border-ring "
                "focus-visible:ring-ring/50 focus-visible:ring-[3px] "
                "transition-all outline-none rounded-md"
            ),
            class_,
        ),
    )


def accordion(
    *children: AccordionItem,
    content_class: str | None = None,
    item_class: str | None = None,
    root_class: str | None = None,
    summary_class: str | None = None,
) -> ComponentType:
    return html.section(
        *(
            html.details(
                summary(s, class_=summary_class),  # Summary
                html.section(c, class_=join("pb-4", content_class)),  # Content
                class_=join("group border-b last:border-b-0", item_class),
            )
            for s, c in children
        ),
        class_=join("accordion", root_class),
    )