move to penguinui continues

This commit is contained in:
Priec
2026-06-18 00:20:39 +02:00
parent ed2eb036ae
commit 1538d870b9
5 changed files with 36 additions and 50 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,4 @@
{% import "macros/ui.html" as ui %}
<!doctype html> <!doctype html>
<html lang="{{ lang | default(value='sk') }}" data-theme="dark"> <html lang="{{ lang | default(value='sk') }}" data-theme="dark">
<head> <head>
@@ -105,14 +106,7 @@
<!-- content column --> <!-- content column -->
<div class="flex min-h-screen flex-col md:ml-60"> <div class="flex min-h-screen flex-col md:ml-60">
<header class="sticky top-0 z-20 flex h-16 items-center gap-4 border-b border-outline bg-surface/95 px-4 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95"> <header class="sticky top-0 z-20 flex h-16 items-center gap-4 border-b border-outline bg-surface/95 px-4 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
<button type="button" @click="showSidebar = !showSidebar" :aria-expanded="showSidebar" {{ ui::icon_button(aria_label=t(key='menu', lang=lang | default(value='sk')), attrs='@click="showSidebar = !showSidebar" :aria-expanded="showSidebar"', extra="md:hidden", icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></svg>') }}
aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt md:hidden dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
<span class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong"> <span class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">
{% block crumb %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock crumb %} {% block crumb %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock crumb %}

View File

@@ -1,3 +1,4 @@
{% import "macros/ui.html" as ui %}
<!doctype html> <!doctype html>
<html lang="{{ lang | default(value='sk') }}" data-theme="dark"> <html lang="{{ lang | default(value='sk') }}" data-theme="dark">
<head> <head>
@@ -66,13 +67,7 @@
class="sticky top-0 z-30 border-b border-outline bg-surface/95 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95"> class="sticky top-0 z-30 border-b border-outline bg-surface/95 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
<nav x-data="{ mobile: false }" class="mx-auto flex max-w-7xl items-center gap-4 px-4 py-3"> <nav x-data="{ mobile: false }" class="mx-auto flex max-w-7xl items-center gap-4 px-4 py-3">
<!-- category sidebar toggle (mobile only) --> <!-- category sidebar toggle (mobile only) -->
<button type="button" @click="cats = !cats" :aria-expanded="cats" {{ ui::icon_button(aria_label=t(key='categories', lang=lang | default(value='sk')), attrs='@click="cats = !cats" :aria-expanded="cats"', extra="lg:hidden", icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></svg>') }}
aria-label="{{ t(key='categories', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt lg:hidden dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
<a href="/" <a href="/"
class="text-lg font-bold tracking-tight text-on-surface-strong dark:text-on-surface-dark-strong"> class="text-lg font-bold tracking-tight text-on-surface-strong dark:text-on-surface-dark-strong">
{{ t(key="brand", lang=lang | default(value='sk')) }} {{ t(key="brand", lang=lang | default(value='sk')) }}
@@ -102,7 +97,7 @@
x-init="count = cartCount(); ['htmx:afterSwap', 'htmx:afterRequest'].forEach(function (e) { window.addEventListener(e, function () { count = cartCount() }) })" x-init="count = cartCount(); ['htmx:afterSwap', 'htmx:afterRequest'].forEach(function (e) { window.addEventListener(e, function () { count = cartCount() }) })"
aria-label="{{ t(key='cart-title', lang=lang | default(value='sk')) }}" aria-label="{{ t(key='cart-title', lang=lang | default(value='sk')) }}"
title="{{ t(key='cart-title', lang=lang | default(value='sk')) }}" title="{{ t(key='cart-title', lang=lang | default(value='sk')) }}"
class="relative inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark-alt"> class="relative inline-flex size-9 shrink-0 items-center justify-center rounded-radius bg-transparent text-secondary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary active:opacity-100 active:outline-offset-0 dark:text-secondary-dark dark:focus-visible:outline-secondary-dark">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
</svg> </svg>
@@ -115,14 +110,7 @@
</div> </div>
<!-- mobile hamburger --> <!-- mobile hamburger -->
<button type="button" @click="mobile = !mobile" :aria-expanded="mobile" {{ ui::icon_button(aria_label=t(key='menu', lang=lang | default(value='sk')), attrs='@click="mobile = !mobile" :aria-expanded="mobile"', extra="md:hidden", icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></svg>') }}
aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt md:hidden dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</button>
</div> </div>
<!-- mobile menu panel --> <!-- mobile menu panel -->

View File

@@ -11,17 +11,7 @@
The host template provides the wrapper The host template provides the wrapper
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative ..."> <div x-data="{ open: false }" @keydown.escape="open = false" class="relative ...">
so it controls its own positioning (e.g. ml-auto in admin). #} so it controls its own positioning (e.g. ml-auto in admin). #}
<button type="button" @click="open = !open" :aria-expanded="open" {{ ui::icon_button(aria_label=t(key='settings', lang=lang | default(value='sk')), attrs='@click="open = !open" :aria-expanded="open"', icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>') }}
aria-label="{{ t(key='settings', lang=lang | default(value='sk')) }}"
title="{{ t(key='settings', lang=lang | default(value='sk')) }}"
class="inline-flex size-9 items-center justify-center rounded-radius text-on-surface transition hover:bg-surface-alt focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-outline-strong dark:text-on-surface-dark dark:hover:bg-surface-dark-alt dark:focus-visible:outline-outline-dark-strong">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</button>
<div x-show="open" x-cloak @click.outside="open = false" x-transition.origin.top.right <div x-show="open" x-cloak @click.outside="open = false" x-transition.origin.top.right
class="absolute right-0 mt-2 flex w-56 flex-col overflow-hidden rounded-radius border border-outline bg-surface-alt py-1 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt" class="absolute right-0 mt-2 flex w-56 flex-col overflow-hidden rounded-radius border border-outline bg-surface-alt py-1 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt"
role="menu"> role="menu">

View File

@@ -289,26 +289,40 @@ This is copy-pasted 5 times.
**Penguin UI: `buttons/default-button.html`, `outline-button.html`, `ghost-button.html`, `button-with-icon.html`** **Penguin UI: `buttons/default-button.html`, `outline-button.html`, `ghost-button.html`, `button-with-icon.html`**
- Exact upstream mirrors at `assets/views/penguinui/buttons/*.html` (reference only). - Exact upstream mirrors at `assets/views/penguinui/buttons/*.html` (reference only).
- One macro `ui::button(label, variant="primary", type, href, attrs, extra, pad="px-4 py-2")` - Macros in `assets/views/macros/ui.html`:
in `assets/views/macros/ui.html`. The per-variant class strings are the `ui::button(label, variant="primary", type, href, attrs, extra, icon, size="px-4 py-2 text-sm")`
**verbatim** Penguin variants (solid `primary|secondary|danger|success|warning|info`, and `ui::icon_button(icon, variant="ghost-secondary", aria_label, attrs, …)`.
`outline-*`, `ghost-*`) — only `inline-flex items-center justify-center` is added The per-variant class strings are the **verbatim** Penguin variants (solid
so `<a>`/`w-full` render, and upstream's `text-onDanger`/`text-onSuccess`… token `primary|secondary|danger|success|warning|info`, `outline-*`, `ghost-*`) — only
typos are fixed to our real `text-on-*` tokens. `href``<a>` else `<button>`; `inline-flex items-center justify-center gap-2` is added so `<a>`/`w-full`/`icon`
`attrs` is raw (htmx / `:disabled` / name / value). render, and upstream's `text-onDanger`/`text-onSuccess`… token typos are fixed to
- **Sizes are NOT normalized**: `pad` defaults to Penguin's `px-4 py-2` but each our real `text-on-*` tokens. `href``<a>` else `<button>`; `attrs` is raw
call site that was a different size keeps it (`px-3 py-2` form-header cancels & (htmx / `:disabled` / name / value); `icon` is a raw `<svg>` rendered before the
label (Penguin button-with-icon).
- **Sizes are NOT normalized**: `size` defaults to Penguin's `px-4 py-2 text-sm`
but each call site that differed keeps it (`px-3 py-2` form-header cancels &
order back, `px-5 py-2` add-to-cart / cart-checkout / order-confirmed continue, order back, `px-5 py-2` add-to-cart / cart-checkout / order-confirmed continue,
`px-6 py-2.5` checkout place-order). `px-6 py-2.5` checkout place-order, `px-3 py-1.5 text-xs` table actions).
- Adopted across every standard filled/outline/submit button: login, product & - Adopted across every standard filled/outline/submit button: login, product &
category forms (save / cancel = `outline-secondary`), products/categories "new" + category forms (save / cancel = `outline-secondary`), products/categories "new" +
empty-state CTAs, orders detail (back/ship/status), shipping save, cart empty-state CTAs, orders detail (back/ship/status), shipping save, cart
(continue/checkout/empty), checkout place-order (`:disabled` via `attrs`), (continue/checkout/empty), checkout place-order (`:disabled` via `attrs`),
product detail add-to-cart, order-confirmed continue. product detail add-to-cart, order-confirmed continue.
- Left inline (these are other Penguin variants/components, not this button): - Icon-only buttons now use `ui::icon_button(icon, variant="ghost-secondary",
icon-only ghost buttons (#51 gear/hamburger/chevron), compact table row-action aria_label, attrs, …)` — Penguin ghost treatment, square. Converted: settings
buttons (`edit`/`view`/`delete`, `px-3 py-1.5 text-xs`), the `text-danger` gear, both hamburgers (site + admin), admin sidebar toggle, mobile category
"Remove" text link (#52), the file-input button (#13), and nav menu links. toggle. The cart link (live `x-init` badge) and the category-accordion chevron
keep the same Penguin ghost classes **inline** only because their markup mixes
single+double quotes that can't be passed through a Tera macro arg — visually
identical to `icon_button`.
- Table row-actions (`edit`/`view`/`delete`/`View`/`label`) → `ui::button`
`outline-secondary` / `outline-danger` at `size="px-3 py-1.5 text-xs"`; cart
"Remove" → `ghost-danger`; card add-to-cart → `ui::button` with the cart `icon`.
- Still genuinely not this component (tracked elsewhere): toast dismiss/Reply
buttons (part of the vendored toast mirror, already Penguin), settings dropdown
menu items (Penguin dropdown items), gallery thumbnail buttons (carousel),
sidebar logout/exit (Penguin sidebar link treatment), and navbar nav-menu
links/logout (belong to §1 Navbar). The file-input button is §13.
> Gotcha for future macro use: Tera renders `{% include %}` in the **includer's** > Gotcha for future macro use: Tera renders `{% include %}` in the **includer's**
> macro scope, so a template that includes a partial which calls `ui::` must also > macro scope, so a template that includes a partial which calls `ui::` must also