{#-
Buttons.
Shared arguments (every component except tooltip and kbd takes them):
class extra classes, appended to the component's own. Use them for layout (margins,
widths, flex); the look comes from variant, size and tone.
attrs a mapping of extra HTML attributes, for names that are not identifiers:
attrs={"@click": "open = true", ":disabled": "busy"}.
kwargs more extra attributes, with "_" turned into "-": hx_post=url -> hx-post="...".
For both, None and False drop the attribute and True renders it bare (disabled).
button(label, variant="secondary", size="md", icon=None, href=None, type="button",
disabled=False, class="", attrs=None, **kwargs)
A text button. variant: primary | secondary | ghost | danger. size: sm (32px) | md (36px).
icon: a Lucide name shown before the label. With href it renders an ; a disabled link
loses its href and gets aria-disabled="true". Called with {% call %}, the block replaces
the label as the button content.
icon_button(icon, label, variant="ghost", size="md", href=None, type="button",
disabled=False, class="", attrs=None, **kwargs)
A square icon-only button. label is required and becomes the aria-label. Variants are as
for button, except that ghost uses muted text and danger is a ghost button in the danger
colour (a solid red square reads as an alarm, not an action).
button_classes(variant="secondary", size="md", square=False)
The class string of a button (square=True: of an icon_button), for elements these macros
do not render, such as popover and menu triggers or action buttons.
Examples:
{{ m.button("Save", "primary", type="submit") }}
{{ m.button("New task", "primary", icon="plus", href=new_url) }}
{{ m.button("Save and add another", type="submit", name="_then", value="another") }}
{{ m.icon_button("x", "Close", size="sm", attrs={"@click": "open = false"}) }}
-#}
{% from "components/icon.html" import icon as _icon %}
{% set _base = "inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors select-none disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50" %}
{% set _variants = {
"primary": "bg-accent text-accent-fg shadow-sm hover:bg-accent/90",
"secondary": "border border-border bg-surface text-fg shadow-sm hover:bg-surface-2",
"ghost": "text-fg hover:bg-surface-2",
"danger": "bg-danger text-danger-fg shadow-sm hover:bg-danger/90",
} %}
{% set _square_variants = {
"primary": "bg-accent text-accent-fg shadow-sm hover:bg-accent/90",
"secondary": "border border-border bg-surface text-fg shadow-sm hover:bg-surface-2",
"ghost": "text-fg-muted hover:bg-surface-2 hover:text-fg",
"danger": "text-danger hover:bg-danger-soft",
} %}
{% set _sizes = {"sm": "h-8 gap-1.5 px-2.5", "md": "h-9 gap-2 px-3"} %}
{% set _square_sizes = {"sm": "size-8", "md": "size-9"} %}
{% macro button_classes(variant="secondary", size="md", square=False) -%}
{%- if square -%}
{{ _base }} {{ hx_pick(_square_variants, variant, "button variant") }} {{ hx_pick(_square_sizes, size, "button size") }}
{%- else -%}
{{ _base }} {{ hx_pick(_variants, variant, "button variant") }} {{ hx_pick(_sizes, size, "button size") }}
{%- endif -%}
{%- endmacro %}
{% macro _element(classes, href, type, disabled, attrs, kwargs) -%}
{%- if href is not none -%}
{{ caller() }}
{%- else -%}
{%- endif -%}
{%- endmacro %}
{% macro button(label, variant="secondary", size="md", icon=None, href=None, type="button", disabled=False, class="", attrs=None) -%}
{%- set content = caller() if caller else label -%}
{%- call _element((button_classes(variant, size) ~ " " ~ class)|trim, href, type, disabled, attrs, kwargs) -%}
{%- if icon %}{{ _icon(icon, "size-4 shrink-0") }}{% endif %}{{ content }}
{%- endcall -%}
{%- endmacro %}
{% macro icon_button(icon, label, variant="ghost", size="md", href=None, type="button", disabled=False, class="", attrs=None) -%}
{%- call _element((button_classes(variant, size, True) ~ " " ~ class)|trim, href, type, disabled, dict({"aria-label": label}, **(attrs or {})), kwargs) -%}
{{ _icon(icon, "size-4") }}
{%- endcall -%}
{%- endmacro %}