Files
kompress_eshop/assets/views/macros/ui.html
2026-06-17 21:52:43 +02:00

68 lines
6.7 KiB
HTML

{# Reusable UI macros adapted from vendored Penguin UI components.
These are OUR adaptation layer; the byte-for-byte upstream sources live under
assets/views/penguinui/. Tailwind sees the full literal class strings here
(assets/css/app.css has @source "../views"), so every branch must spell its
classes out in full — never build class names by concatenation.
Usage:
{% import "macros/ui.html" as ui %}
{{ ui::button_primary(label=t(key="save", lang=lang)) }}
{{ ui::button_primary(label="Add", attrs='hx-post="/x"' | safe) }}
{{ ui::button_outline(label="Cancel", href="/back") }}
{{ ui::badge(label="Published", variant="success") }}
Notes:
- Macros can't see template context vars (e.g. `lang`); pass already-translated
strings as `label`.
- `attrs` is injected raw (caller must pass it through `| safe`); use it for
htmx / name / value / @click etc. For buttons whose attrs carry nested
quotes (e.g. hx-on with toast(...)), keep them inline instead.
- Source: penguinui/buttons/{default,outline,ghost}-button.html and
penguinui/badge/soft-color-badge.html. Upstream's color-button typos
(text-onDanger etc.) are fixed to our real tokens (text-on-danger). #}
{% macro button_primary(label, type="button", href="", attrs="", extra="") -%}
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius border border-primary bg-primary px-4 py-2 text-center text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 dark:border-primary-dark dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark {{ extra }}" {{ attrs | safe }}>{{ label }}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro button_primary %}
{% macro button_outline(label, type="button", href="", attrs="", extra="") -%}
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius border border-outline bg-surface-alt px-4 py-2 text-center text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-outline-strong active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:outline-outline-dark-strong {{ extra }}" {{ attrs | safe }}>{{ label }}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro button_outline %}
{% macro button_danger(label, type="button", href="", attrs="", extra="") -%}
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius border border-danger bg-danger px-4 py-2 text-center text-sm font-medium tracking-wide text-on-danger transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 dark:focus-visible:outline-danger {{ extra }}" {{ attrs | safe }}>{{ label }}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro button_danger %}
{% macro button_ghost(label, type="button", href="", attrs="", extra="") -%}
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius bg-transparent px-4 py-2 text-center text-sm font-medium tracking-wide text-primary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 dark:text-primary-dark dark:focus-visible:outline-primary-dark {{ extra }}" {{ attrs | safe }}>{{ label }}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro button_ghost %}
{# Compact danger alert (form/inline errors). Adapted from
penguinui/alert/default-alert.html (danger variant), trimmed to a single line
with the danger icon. #}
{% macro alert_danger(message, extra="") -%}
<div class="flex w-full items-center gap-2 overflow-hidden rounded-radius border border-danger bg-danger/10 px-3 py-2 text-sm text-danger {{ extra }}" role="alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 shrink-0" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
</svg>
<span>{{ message }}</span>
</div>
{%- endmacro alert_danger %}
{# Soft-color badge. variant ∈ success | danger | warning | info | primary | neutral #}
{% macro badge(label, variant="neutral") -%}
{% if variant == "success" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-success bg-surface text-xs font-medium text-success dark:bg-surface-dark"><span class="bg-success/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "danger" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-danger bg-surface text-xs font-medium text-danger dark:bg-surface-dark"><span class="bg-danger/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "warning" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-warning bg-surface text-xs font-medium text-warning dark:bg-surface-dark"><span class="bg-warning/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "info" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-info bg-surface text-xs font-medium text-info dark:bg-surface-dark"><span class="bg-info/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "primary" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-primary bg-surface text-xs font-medium text-primary dark:border-primary-dark dark:bg-surface-dark dark:text-primary-dark"><span class="bg-primary/10 px-2 py-1 dark:bg-primary-dark/10">{{ label }}</span></span>
{%- else -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-outline bg-surface text-xs font-medium text-on-surface dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark"><span class="bg-surface-alt/40 px-2 py-1 dark:bg-surface-dark-alt/40">{{ label }}</span></span>
{%- endif %}
{%- endmacro badge %}