Skip to content

htmforge.components

Complete component reference for htmforge v0.3.0+.

Block F: DataTable Enhancements

htmforge.components.table.DataTable(**data)

Bases: Component

Rendert eine einfache Datentabelle mit optionalem HTMX-Reload.

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt div.table-wrapper > table.table mit thead/tbody.

Source code in htmforge/components/table.py
def render(self) -> Element:
    """Erstellt ``div.table-wrapper > table.table`` mit ``thead``/``tbody``."""
    # Build header row
    header_row = self._render_header_row()

    # Build body rows
    body_rows = self._render_body_rows()

    attrs: dict[str, object] = {}
    if self.hx_url is not None:
        attrs["hx_get"] = self.hx_url
        attrs["hx_trigger"] = HxTrigger.LOAD

    return div(
        table(
            thead(header_row),
            tbody(*body_rows),
            cls="table",
            **attrs,
        ),
        cls="table-wrapper",
    )

htmforge.components.table.ColumnDef

Bases: BaseModel

Spaltenkonfiguration fuer DataTable.

Fields

key: Schlüssel für dict_rows oder Index-Position label: Angezeigter Spaltenname (default = key) sortable: Ob die Spalte klickbar/sortierbar ist width: Optionale CSS-Breite z.B. "120px" oder "10%"

display_label property

Gibt label zurück, falls gesetzt, sonst key.

Block G: New Components

Spinner

htmforge.components.spinner.Spinner(**data)

Bases: Component

Barrierefreier Ladeindikator mit Groessenvarianten.

Example

Spinner().to_html() '

'

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert den Spinner mit Größenklasse und Accessibility-Attributen.

Source code in htmforge/components/spinner.py
def render(self) -> Element:
    """Rendert den Spinner mit Größenklasse und Accessibility-Attributen."""
    return div(
        cls=f"spinner spinner-{self.size.value}",
        role="status",
        aria_label=self.label,
    )

htmforge.components.spinner.SpinnerSize

Bases: StrEnum

Verfügbare Spinner-Größen.

Tabs

htmforge.components.tabs.Tabs(**data)

Bases: Component

Rendert eine Tab-Leiste. Jeder Tab laedt seinen Inhalt via HTMX.

Fields

tabs: list of (label, hx_url) tuples active: index of the active tab (0-based) target: CSS selector for the content panel to swap into tab_cls: extra CSS class on the tab bar wrapper div

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert die Tab-Leiste mit aktiven/inaktiven Tabs.

Source code in htmforge/components/tabs.py
def render(self) -> Element:
    """Rendert die Tab-Leiste mit aktiven/inaktiven Tabs."""
    tab_buttons: list[Element] = []
    for i, (label, url) in enumerate(self.tabs):
        if i == self.active:
            # Aktiver Tab: disabled, keine HTMX-Attribute
            tab_buttons.append(
                button(
                    label,
                    cls="tab tab-active",
                    disabled=True,
                )
            )
        else:
            # Inaktiver Tab: mit HTMX-Attributen
            tab_buttons.append(
                button(
                    label,
                    cls="tab",
                    hx_get=url,
                    hx_target=self.target,
                    hx_swap=HxSwap.INNER_HTML,
                )
            )

    tab_cls = f"tabs {self.tab_cls}".strip()
    return div(*tab_buttons, cls=tab_cls)

Toast

htmforge.components.toast.Toast(**data)

Bases: Component

Timed notification box, HTMX OOB-swap kompatibel.

Renders a div with id=toast_id for HTMX out-of-band swapping. Use hx-swap-oob="true" in HTMX responses to inject toasts.

Fields

message: str — notification text variant: ToastVariant — visual style toast_id: str — HTML id, default "toast" duration_ms: int — auto-dismiss after ms via JS, 0 = no auto-dismiss

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert die Toast-Benachrichtigung mit OOB-Swap-Support.

Source code in htmforge/components/toast.py
def render(self) -> Element:
    """Rendert die Toast-Benachrichtigung mit OOB-Swap-Support."""
    attrs: dict[str, object] = {
        "id": self.toast_id,
        "cls": f"toast toast-{self.variant.value}",
        "hx_swap_oob": "true",
    }
    if self.duration_ms > 0:
        attrs["data_duration"] = str(self.duration_ms)

    return div(self.message, **attrs)

htmforge.components.toast.ToastVariant

Bases: StrEnum

Verfügbare Toast-Varianten.

Accordion

htmforge.components.accordion.Accordion(**data)

Bases: Component

Mehrere aufklappbare Abschnitte auf Basis von details/summary.

Fields

items: list of (title, content) tuples open_index: int | None — index of initially open item, None = all closed item_cls: str — extra CSS class on each details element

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert das Accordion mit aufklappbaren Items.

Source code in htmforge/components/accordion.py
def render(self) -> Element:
    """Rendert das Accordion mit aufklappbaren Items."""
    accordion_items: list[Element] = []
    for i, (title, content) in enumerate(self.items):
        item_cls = f"accordion-item {self.item_cls}".strip()
        is_open = i == self.open_index
        accordion_items.append(
            details(
                summary(title, cls="accordion-title"),
                div(content, cls="accordion-content"),
                cls=item_cls,
                open=True if is_open else None,
            )
        )

    return div(*accordion_items, cls="accordion")

htmforge.components.dropdown.Dropdown(**data)

Bases: Component

Trigger-Button mit verstecktem Menü, HTMX-togglebar.

Renders

div(cls="dropdown") button(label, hx_get=toggle_url, hx_target="#dropdown_id-menu", hx_swap=HxSwap.OUTER_HTML, cls="dropdown-trigger") div(id=f"{dropdown_id}-menu", cls="dropdown-menu") For each (label, url): a(label, href=url, cls="dropdown-item")

Fields

label: str — trigger button label items: list of (label, url) tuples dropdown_id: str — unique HTML id prefix, default "dropdown" toggle_url: str — HTMX URL to reload/toggle menu, default ""

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert das Dropdown-Menü mit Trigger-Button.

Source code in htmforge/components/dropdown.py
def render(self) -> Element:
    """Rendert das Dropdown-Menü mit Trigger-Button."""
    menu_id = f"{self.dropdown_id}-menu"

    # Trigger-Button mit optionalen HTMX-Attributen
    button_attrs: dict[str, object] = {"cls": "dropdown-trigger"}
    if self.toggle_url:
        button_attrs["hx_get"] = self.toggle_url
        button_attrs["hx_target"] = f"#{menu_id}"
        button_attrs["hx_swap"] = HxSwap.OUTER_HTML

    trigger_button = button(self.label, **button_attrs)

    # Menü-Items
    menu_items: list[Element] = []
    for item_label, url in self.items:
        menu_items.append(a(item_label, href=url, cls="dropdown-item"))

    # Menü-Div
    menu_div = div(*menu_items, id=menu_id, cls="dropdown-menu")

    # Root-Div
    return div(trigger_button, menu_div, cls="dropdown")

Block H: Forms System

htmforge.components.forms.SelectField(**data)

Bases: Component

Dropdown-Auswahlliste mit typisierten Optionen.

Fields

name: str — name-Attribut des select options: list[tuple[str, str]] — (label, value) Paare selected: str — aktuell ausgewählter value label_text: str — Beschriftung über dem Select required: bool — Pflichtfeld error: str — Fehlermeldung (rendert div.field-error) field_id: str — HTML id, default = name

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert ein beschriftetes select-Element.

Source code in htmforge/components/forms.py
def render(self) -> Element:
    """Rendert ein beschriftetes select-Element."""
    fid = self.field_id or self.name
    children: list[Element] = []
    if self.label_text:
        children.append(label(self.label_text, for_=fid))
    opts = [
        option(
            lbl,
            value=val,
            selected=True if val == self.selected else None,
        )
        for lbl, val in self.options
    ]
    sel = select(
        *opts,
        name=self.name,
        id=fid,
        required=True if self.required else None,
    )
    children.append(sel)
    if self.error:
        children.append(div(self.error, cls="field-error"))
    return div(*children)

htmforge.components.forms.CheckboxField(**data)

Bases: Component

Einzelne Checkbox mit Label.

Fields

name: str — name-Attribut label_text: str — Beschriftung neben der Checkbox checked: bool — Vorausgewählt value: str — value-Attribut, default "1" required: bool — Pflichtfeld error: str — Fehlermeldung field_id: str — HTML id, default = name

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert eine Checkbox mit Label.

Source code in htmforge/components/forms.py
def render(self) -> Element:
    """Rendert eine Checkbox mit Label."""
    fid = self.field_id or self.name
    children: list[Element] = [
        input(
            type="checkbox",
            name=self.name,
            id=fid,
            value=self.value,
            checked=True if self.checked else None,
            required=True if self.required else None,
        ),
        label(self.label_text, for_=fid),
    ]
    if self.error:
        children.append(div(self.error, cls="field-error"))
    return div(*children, cls="checkbox-field")

htmforge.components.forms.RadioGroup(**data)

Bases: Component

Gruppe von Radio-Buttons aus einer Options-Liste.

Fields

name: str — gemeinsames name-Attribut aller Radios options: list[tuple[str, str]] — (label, value) Paare selected: str — aktuell ausgewählter value legend_text: str — Gruppenbezeichnung im fieldset required: bool — Pflichtfeld auf erstem Radio error: str — Fehlermeldung

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert eine Gruppe von Radio-Buttons im Fieldset.

Source code in htmforge/components/forms.py
def render(self) -> Element:
    """Rendert eine Gruppe von Radio-Buttons im Fieldset."""
    children: list[Element] = []
    if self.legend_text:
        children.append(legend(self.legend_text))

    for i, (lbl, val) in enumerate(self.options):
        radio_id = f"{self.name}-{val}"
        is_first = i == 0
        children.append(
            div(
                input(
                    type="radio",
                    name=self.name,
                    id=radio_id,
                    value=val,
                    checked=True if val == self.selected else None,
                    required=True if self.required and is_first else None,
                ),
                label(lbl, for_=radio_id),
                cls="radio-item",
            )
        )

    if self.error:
        children.append(div(self.error, cls="field-error"))

    return fieldset(*children)

htmforge.components.forms.FormGroup(**data)

Bases: Component

Layout-Container fuer mehrere Formularfelder.

Fields

fields: list[Component] — Felder die gerendert werden legend_text: str — optionale Gruppenbezeichnung group_cls: str — extra CSS-Klasse auf dem wrapper div

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert eine Feldgruppe mit optionaler Legend.

Source code in htmforge/components/forms.py
def render(self) -> Element:
    """Rendert eine Feldgruppe mit optionaler Legend."""
    children: list[Element] = []
    if self.legend_text:
        children.append(div(self.legend_text, cls="form-group-legend"))

    for field in self.fields:
        children.append(field.render())

    group_cls = f"form-group {self.group_cls}".strip()
    return div(*children, cls=group_cls)

htmforge.components.forms.Form(**data)

Bases: Component

Formular-Container mit HTMX-Submit-Unterstuetzung.

Fields

action: str — form action URL method: str — "get" or "post", default "post" fields: list[Component] — Formularfelder submit_label: str — Beschriftung des Submit-Buttons, default "Absenden" errors: dict[str, str] — Validierungsfehler {field_name: message} Wenn gesetzt, werden Fehler automatisch an passende Felder weitergegeben. hx_post: str — HTMX POST URL (optional, overrides action for HTMX) hx_target: str — HTMX target selector hx_swap: HxSwap | None — HTMX swap strategy

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert das Formular mit auto-error-Injection.

Source code in htmforge/components/forms.py
def render(self) -> Element:
    """Rendert das Formular mit auto-error-Injection."""
    # Apply errors to matching fields
    rendered_fields: list[Element] = []
    for field in self.fields:
        field_to_render = field
        # Check if field has a 'name' attribute and matching error
        if hasattr(field, "name") and hasattr(field, "error"):
            field_name = field.name
            if field_name in self.errors:
                # Clone field with error
                field_to_render = field.clone(error=self.errors[field_name])
        rendered_fields.append(field_to_render.render())

    # Build form attributes
    form_attrs: dict[str, object] = {
        "action": self.action,
        "method": self.method,
    }
    if self.hx_post:
        form_attrs["hx_post"] = self.hx_post
    if self.hx_target:
        form_attrs["hx_target"] = self.hx_target
    if self.hx_swap:
        form_attrs["hx_swap"] = self.hx_swap

    # Build form content
    form_children: list[Element] = rendered_fields + [
        button(self.submit_label, type="submit")
    ]

    return form(*form_children, **form_attrs)

v0.2.x Components

Layout & Structure

htmforge.components.page.Page(**data)

Bases: Component

Abstrakte Basisklasse fuer vollstaendige HTML-Dokumente mit DOCTYPE.

Subklassen implementieren :meth:_body_content um den Seiteninhalt bereitzustellen. :meth:to_html haengt automatisch <!DOCTYPE html> voran.

Example

from htmforge.components.page import Page from htmforge.core.element import Element

class MyPage(Page): ... users: list[str] = [] ... ... def _body_content(self) -> list[Element | str | None]: ... from htmforge.elements import li, ul ... return [ul(*[li(u) for u in self.users])] ... page = MyPage(title="Users", users=["Ada", "Grace"]) page.to_html().startswith("<!DOCTYPE html>") True

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Rendert das vollstaendige <html>-Dokument ohne DOCTYPE.

Source code in htmforge/components/page.py
def render(self) -> Element:
    """Rendert das vollstaendige ``<html>``-Dokument ohne DOCTYPE."""
    head_children: list[Any] = [meta(charset=self.charset)]

    if self.description:
        head_children.append(meta(name="description", content=self.description))

    head_children.append(title(self.title))

    for css_url in self.css_urls:
        head_children.append(link(rel="stylesheet", href=css_url))

    if self.inline_css:
        head_children.append(style(raw(self.inline_css)))

    body_children: list[Any] = [c for c in self._body_content() if c is not None]

    for js_url in self.js_urls:
        body_children.append(script(src=js_url))

    return html(
        head(*head_children),
        body(*body_children),
    )

to_html()

Rendert das vollstaendige Dokument inklusive <!DOCTYPE html>.

Source code in htmforge/components/page.py
def to_html(self) -> str:
    """Rendert das vollstaendige Dokument inklusive ``<!DOCTYPE html>``."""
    return "<!DOCTYPE html>" + self.render().to_html()

Data Display

htmforge.components.alert.Alert(**data)

Bases: Component

Rendert eine Alert-Box mit optionalem Dismiss-Button.

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt ein <div> mit Variantenklasse und optionalem Schliessen.

Source code in htmforge/components/alert.py
def render(self) -> Element:
    """Erstellt ein ``<div>`` mit Variantenklasse und optionalem Schliessen."""
    children: list[Element | str] = [self.message]
    if self.dismissible:
        children.append(
            button(
                "×",
                type="button",
                cls="alert-close",
                aria_label=self.close_label,
                onclick="this.closest('.alert').remove()",
            )
        )
    return div(*children, cls=f"alert alert-{self.variant.value}")

htmforge.components.alert.AlertVariant

Bases: StrEnum

Unterstuetzte Alert-Varianten.

htmforge.components.badge.Badge(**data)

Bases: Component

Rendert ein kleines Inline-Label mit Variantenklasse.

Example

Badge(text="3", variant=BadgeVariant.DANGER).to_html() '3'

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt ein <span> mit Variantenklasse.

Source code in htmforge/components/badge.py
def render(self) -> Element:
    """Erstellt ein ``<span>`` mit Variantenklasse."""
    return span(self.text, cls=f"badge badge-{self.variant.value}")

htmforge.components.badge.BadgeVariant

Bases: StrEnum

Unterstuetzte Badge-Varianten.

htmforge.components.breadcrumb.Breadcrumb(**data)

Bases: Component

Rendert eine Breadcrumb-Navigation als <nav> mit geordneter Liste.

Items sind (label, url)-Tupel. url=None markiert die aktuelle Seite und wird als <span> gerendert.

Example

Breadcrumb(items=[("Home", "/"), ("Aktuell", None)]).to_html()

contains Home and Aktuell

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt <nav> mit <ol> und <li>-Eintraegen.

Source code in htmforge/components/breadcrumb.py
def render(self) -> Element:
    """Erstellt ``<nav>`` mit ``<ol>`` und ``<li>``-Eintraegen."""
    list_items: list[Element] = []
    last_index = len(self.items) - 1

    for index, (label, href) in enumerate(self.items):
        is_current = href is None or index == last_index
        if is_current:
            list_items.append(
                li(
                    span(label, aria_current="page"),
                    cls="breadcrumb-item active",
                )
            )
        else:
            list_items.append(
                li(
                    a(label, href=href),
                    cls="breadcrumb-item",
                )
            )

    return nav(ol(*list_items, cls="breadcrumb"), aria_label="breadcrumb")

htmforge.components.pagination.Pagination(**data)

Bases: Component

Rendert Previous/Next und Seitenlinks fuer HTMX-Navigation.

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt eine <ul> mit Seitenlinks inklusive Previous/Next.

Source code in htmforge/components/pagination.py
def render(self) -> Element:
    """Erstellt eine ``<ul>`` mit Seitenlinks inklusive Previous/Next."""
    items: list[Element] = [self._previous_link()]

    for page in range(1, self.total_pages + 1):
        if page == self.current_page:
            items.append(li(a(str(page), href="#"), cls="active"))
        else:
            items.append(
                li(
                    a(
                        str(page),
                        href="#",
                        **self._link_attrs(self.hx_url.format(page=page)),
                    )
                )
            )

    items.append(self._next_link())
    return ul(*items, cls="pagination")

htmforge.components.modal.Modal(**data)

Bases: Component

Trigger-Button + leeres Dialog-Overlay, Inhalt wird per HTMX geladen.

Renders
Fields

modal_id: str — unique HTML id for the

trigger_label: str — label on the trigger button hx_url: str — URL to load modal content from hx_target: str = "" — overrides default target if set close_label: str = "Schließen"

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt den Trigger-Button und das <dialog>-Overlay.

Source code in htmforge/components/modal.py
def render(self) -> Element:
    """Erstellt den Trigger-Button und das ``<dialog>``-Overlay."""
    body_id = f"{self.modal_id}-body"
    target = self.hx_target or f"#{body_id}"

    return div(
        button(
            self.trigger_label,
            type="button",
            data_modal_target=self.modal_id,
            cls="modal-trigger",
            hx_get=self.hx_url,
            hx_target=target,
            hx_swap=HxSwap.INNER_HTML,
        ),
        dialog(
            div(id=body_id, cls="modal-body"),
            form(
                button(self.close_label, cls="modal-close"),
                method="dialog",
            ),
            id=self.modal_id,
            cls="modal",
        ),
        raw(
            "<script>"
            "document.querySelectorAll('[data-modal-target]').forEach(function(btn){"
            "btn.addEventListener('click',function(){"
            "var id=btn.getAttribute('data-modal-target');"
            "var dlg=document.getElementById(id);"
            "if(dlg)dlg.showModal();"
            "});"
            "});"
            "</script>"
        ),
        cls="modal-wrapper",
    )

htmforge.components.search_input.SearchInput(**data)

Bases: Component

Text-Input mit automatischem hx-trigger keyup-Debounce.

     </div>
Fields

name: str — input name attribute search_url: str — URL for hx-get (custom field, not Component.hx_get) search_target: str — CSS selector for swap target (custom field, not Component.hx_target) placeholder: str = "Suchen…" debounce_ms: int = 300 indicator: str = "" — optional hx-indicator selector

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt den Such-Input mit Debounce und optionalem Indicator.

Source code in htmforge/components/search_input.py
def render(self) -> Element:
    """Erstellt den Such-Input mit Debounce und optionalem Indicator."""
    return div(
        input(
            type="search",
            name=self.name,
            placeholder=self.placeholder,
            hx_get=self.search_url,
            hx_trigger=hx_keyup_delay(self.debounce_ms),
            hx_target=self.search_target,
            hx_indicator=self.indicator or None,
        ),
        cls="search-input-wrapper",
    )

Legacy FormField

htmforge.components.form_field.FormField(**data)

Bases: Component

Rendert ein beschriftetes Eingabefeld mit optionaler Fehleranzeige.

Example

from htmforge.components.form_field import FormField, InputType field = FormField( ... name="username", ... label_text="Benutzername", ... required=True, ... ) "required" in field.to_html() True

Source code in htmforge/core/component.py
def __init__(self, **data: Any) -> None:  # noqa: ANN401
    """Initialisiert die Komponente und blockiert Klassen ohne ``render``."""
    if getattr(type(self), "__htmforge_missing_render__", False):
        raise TypeError(
            f"Can't instantiate abstract class {type(self).__name__} "
            "without a concrete render() implementation"
        )
    super().__init__(**data)

render()

Erstellt div > label + input [+ div.field-error].

Source code in htmforge/components/form_field.py
def render(self) -> Element:
    """Erstellt ``div > label + input [+ div.field-error]``."""
    fid = self.field_id or self.name.replace(" ", "-")

    children: list[Element] = [
        label(
            self.label_text,
            for_=fid,
            aria_required="true" if self.required else None,
        ),
        input(
            type=self.input_type.value,
            name=self.name,
            id=fid,
            value=self.value or None,
            placeholder=self.placeholder or None,
            required=True if self.required else None,
            aria_required="true" if self.required else None,
        ),
    ]

    if self.error:
        children.append(div(self.error, cls="field-error"))

    return div(*children)

htmforge.components.form_field.InputType

Bases: StrEnum

Unterstuetzte <input>-Typen.