Files
kompress_eshop/hardcoded-inventory.md
2026-06-17 21:52:43 +02:00

22 KiB
Raw Blame History

Handcoded UI Components — Penguin UI Replacement Index

Scope: Every handcoded UI component. Each item maps to a Penguin UI component that duplicates the same purpose with fewer lines and better accessibility.

Vendoring convention

When a Penguin UI component can replace a handcoded one, we vendor its source and then use it (instead of hand-rolling):

  1. Copy the component's source byte-for-byte from the Penguin UI repo into assets/views/penguinui/, mirroring the upstream repo hierarchy (e.g. toast-notification/stacking-toast-notification.html). This directory is reserved exclusively for vendored Penguin UI components and is kept an exact, unmodified mirror of upstream — demo triggers, bugs and all. It's a reference, not the rendered markup.
  2. Adapt it where it's actually used (strip docs-only demo triggers, fix obvious upstream bugs, wire data bindings). Note the deviations in a comment next to the adapted copy.
  3. Rebuild Tailwind (make css) so any new utility classes get compiled.
  4. Mark the section below as DONE.

0. Toast — DONE

Penguin UI: toast-notification/stacking-toast-notification.html

  • Exact upstream mirror at assets/views/penguinui/toast-notification/stacking-toast-notification.html (reference only)
  • Adapted/rendered copy lives inline in assets/views/base.html (demo triggers removed; the upstream dismiss-button <svg> quote bugs fixed)
  • The global toast('message') JS helper now dispatches the component's notify event ({ variant: 'success', message }), so existing callsites (shop/show.html, shop/_card.html) keep working unchanged.

1. Navbar

Penguin UI: navbar/

# Location What it is Size
1 assets/views/base.html:63-191 Full site navbar: brand, desktop nav links, cart icon+badge, settings dropdown, mobile hamburger → mobile panel ~130 lines
2 assets/views/admin/base.html:102-114 Admin top bar: hamburger toggle + breadcrumb text ~13 lines

Details for #1 (site navbar):

  • Brand/logo: base.html:74-77 — plain <a> with text
  • Desktop nav links: base.html:80-92<ul> with 45 items, manual aria-current routing
  • Cart icon + badge: base.html:96-109 — hand-rolled SVG cart icon + an Alpine x-data badge that reads document.cookie directly
  • Settings dropdown: base.html:110-162 — gear-icon trigger + language-switcher <form> + theme tristate (system/light/dark)
  • Mobile hamburger: base.html:164-172 — hamburger SVG button
  • Mobile menu panel: base.html:175-190 — dropdown <ul> with duplicated nav links

Penguin navbar variants: default-navbar.html, with-call-to-action.html, with-search.html, with-user-profile.html


2. Sidebar (Admin) — DONE

Penguin UI: sidebar/simple-sidebar.html

  • Exact upstream mirror at assets/views/penguinui/sidebar/simple-sidebar.html (reference only)
  • Adapted at use-site in assets/views/admin/base.html: the nav links + bottom exit/logout now use Penguin's link treatment (hover:bg-primary/5, underline-offset-2 focus-visible:underline focus:outline-hidden) and the subtle active state (bg-primary/10 + text-on-surface-strong) mapped onto our data-nav/aria-current so markActiveNav() still drives it.
  • The fixed-rail translate-X show/hide mechanics + mobile overlay (#4) are layout scaffolding, kept as-is. Icons were intentionally not added (no verified icon set yet) — possible follow-up.

3. Sidebar (Category Accordion) — DONE

Penguin UI: sidebar/sidebar-with-collapsible-menus.html

  • Exact upstream mirror at assets/views/penguinui/sidebar/sidebar-with-collapsible-menus.html (reference only)
  • Adapted at use-site in assets/views/shop/_sidebar.html: Penguin link treatment + active state + chevron-down rotation (rotate-180); child items now sit in a bordered/indented list instead of the old padding-left:28px + . Kept our htmx partial, data-driven category_groups, auto-expand x-init, and data-nav/markActiveNav() active routing.
  • Deviations: group row keeps our link + chevron-toggle split (categories are navigable, not just expandable); uses x-show/x-transition instead of upstream's x-collapse (that Alpine plugin isn't bundled in our build).
  • The <aside> drawer + mobile overlay (#6) in base.html are layout scaffolding, kept as-is.

4. Dropdown (Settings) — DONE

Penguin UI: dropdowns/dropdown-with-click.html

  • Exact upstream mirror at assets/views/penguinui/dropdowns/dropdown-with-click.html (reference only)
  • De-duplicated: the ~103-line copy-paste is now one shared partial assets/views/partials/settings_dropdown.html, included by both base.html and admin/base.html (each host keeps its own positioning wrapper <div x-data="{ open:false }" class="relative [ml-auto]">).
  • Adopts Penguin's dropdown menu container + item treatment. Deviations: kept our gear icon-only trigger and core-Alpine open/@click.outside toggle (upstream's x-trap/$focus need the Alpine Focus plugin we don't bundle); item hover uses bg-primary/5 for consistency with the rest of the UI.

5. Country / Phone Combobox

Penguin UI: text-input/ + custom dropdown list

# Location What it is Size
9 assets/views/shop/checkout.html:49-74 Phone prefix combobox (+421, +420, …, +33) ~25 lines
10 assets/views/shop/checkout.html:102-127 Country combobox (SK, CZ, AT, DE, PL, HU) ~26 lines

Details for #9:

  • Alpine x-data with prefix, prefixOpen, opts array of { v, l } (9 country codes)
  • Manual filtered computed property
  • Inline chevron SVG that rotates via :class="prefixOpen && 'rotate-180'"
  • Dropdown list with <template x-for> and @click selection

Details for #10:

  • Same pattern as #9 but with translate-able country names (6 countries)
  • Includes +421 prefix shortcut

6. Product Card — DONE

Penguin UI: card/ecommerce-product-card.html

  • Exact upstream mirror at assets/views/penguinui/card/ecommerce-product-card.html (reference only)
  • Adapted/rendered copy is assets/views/shop/_card.html: <article> shell + Penguin image/title/price layout and the cart-icon add-to-cart button, wired to our product data + i18n + htmx hx-post add-to-cart + toast(). Demo-only rating stars, hardcoded content and max-w-sm (fights the shop grid) were dropped; whole card links to the product page; out-of-stock badge kept.

Penguin UI: carousel/ (3 variants)

# Location What it is Size
12 assets/views/shop/show.html:8-26 Image gallery with main image + thumbnail strip, Alpine x-data="{ active: 0 }" ~19 lines

Details:

  • Main image: x-show="active === {{ loop.index0 }}" with object-cover
  • Thumbnail buttons: border changes to indicate active state
  • No transition/animation between images — just x-show toggling

8. Radio-Button Groups

Penguin UI: radio (part of form inputs)

# Location What it is Size
13 assets/views/shop/checkout.html:133-165 Carrier selection radio group (each option shows name + price) ~33 lines
14 assets/views/shop/checkout.html:167-180 Payment method radio group (COD + bank transfer) ~14 lines

Details for #13:

  • {% for m in shipping_methods %} loop
  • Each <label> is a styled card with has-[:checked]:border-primary border highlight
  • Radio input triggers @change to update Alpine state (carrier, carrierPrice, requiresPoint)
  • Pickup-point sub-panel shown via x-show="requiresPoint"

Details for #14:

  • Two hardcoded radio options: COD and bank_transfer
  • x-model="paymentMethod" binding

9. Checkbox

Penguin UI: checkbox/ (3 variants)

# Location What it is Size
15 assets/views/admin/catalog/product_form.html:85-89 "Published" checkbox ~5 lines
16 assets/views/admin/catalog/category_form.html:67-71 "Published" checkbox ~5 lines
17 assets/views/admin/shipping/index.html:25-29 "Enabled" checkbox ~5 lines

10. Text Input

Penguin UI: text-input/ (8 variants)

# Location What it is Size
18 assets/views/shop/checkout.html:37-44 Email + name text inputs ~8 lines
19 assets/views/shop/checkout.html:84-99 Address, city, ZIP text inputs ~16 lines
20 assets/views/admin/login.html:34-51 Email + password inputs (with focus ring styles) ~18 lines
21 assets/views/admin/catalog/product_form.html:19-68 Name, price, currency, stock, SKU, slug inputs + textarea ~50 lines
22 assets/views/admin/catalog/category_form.html:19-55 Name, slug, position inputs + textarea ~37 lines
23 assets/views/shop/show.html:46-48 Quantity number input ~3 lines
24 assets/views/shop/_cart_body.html:30-38 Quantity number input with @change confirmation dialog ~9 lines
25 assets/views/admin/shipping/index.html:20-24 Price text input ~5 lines

Pattern: Every input is hand-styled with:

w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface
focus:outline-2 focus:outline-primary
dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark

This exact class string appears 15+ times across the codebase.


11. Textarea

Penguin UI: textarea/

# Location What it is Size
26 assets/views/shop/checkout.html:183-186 Order note textarea ~4 lines
27 assets/views/admin/catalog/product_form.html:71-73 Product description textarea ~3 lines
28 assets/views/admin/catalog/category_form.html:53-55 Category description textarea ~3 lines

12. Select/Dropdown (Native)

Penguin UI: select/ (7 variants)

# Location What it is Size
29 assets/views/admin/catalog/product_form.html:53-60 Category select ~8 lines
30 assets/views/admin/catalog/category_form.html:40-49 Parent category select (tree indented with —&nbsp;) ~10 lines
31 assets/views/admin/orders/show.html:108-112 Order status select ~5 lines

13. File Input

Penguin UI: file-input/

# Location What it is Size
32 assets/views/admin/catalog/product_form.html:78-82 Product image upload ~5 lines
33 assets/views/admin/catalog/category_form.html:58-64 Category image upload ~7 lines

Both use the same Tailwind file:mr-3 file:... prefix pattern for styling.


14. Table

Penguin UI: table/ (7 variants)

# Location What it is Size
34 assets/views/admin/orders/index.html:11-36 Orders table: number, customer, status pill, total, "View" link ~26 lines
35 assets/views/admin/orders/show.html:20-44 Order items table: product, quantity, line total + tfoot summary ~25 lines
36 assets/views/admin/catalog/products.html:20-70 Products table: image+name+category, price, stock, status pill, edit/view/delete actions ~51 lines
37 assets/views/admin/catalog/categories.html:20-59 Categories table: tree-indented name, product count, status pill, edit/delete ~40 lines
38 assets/views/shop/_cart_body.html:6-59 Cart table: product link, price, quantity input, line total, remove button + tfoot total ~54 lines

Pattern: Every table uses the same class structure:

<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70">
<tbody class="divide-y divide-outline">
<tr class="hover:bg-surface-alt">

This is copy-pasted 5 times.


15. Alert / Error Banner — DONE

Penguin UI: alert/default-alert.html

  • Exact upstream mirror at assets/views/penguinui/alert/default-alert.html (reference only)
  • Adapted into the ui::alert_danger(message, extra="") macro in assets/views/macros/ui.html (compact one-line danger alert + danger icon).
  • Adopted at both sites: admin/login.html (login error) and admin/orders/show.html (ship error).

16. Badge / Status Pill — DONE

Penguin UI: badge/soft-color-badge.html

  • Exact upstream mirror at assets/views/penguinui/badge/soft-color-badge.html (reference only)
  • Adapted into the ui::badge(label, variant) macro in assets/views/macros/ui.html (variants: success | danger | warning | info | primary | neutral).
  • Adopted at the status-pill sites: "Auth" badge (admin/login.html), order status (orders/index.html, neutral), Published/Draft pills (products.html + categories.html, success/neutral).
  • Intentionally left inline (not soft-color pills): the cart item-count notification badge in base.html (count bubble, a different Penguin badge type) and the block-style "out of stock" notice in _card.html.

17. Buttons — DONE (canonical macros + adopted)

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).
  • Canonical button macros in assets/views/macros/ui.html: ui::button_primary, ui::button_outline (neutral secondary), ui::button_danger, ui::button_ghost. Each takes label, type, href (renders <a> vs <button>), attrs (raw — htmx / :disabled / name / value), extra (extra classes). Upstream's color-button token typos (text-onDanger etc.) are fixed to our real tokens (text-on-danger).
  • Adopted across every standard filled/outline/submit button: login, product & category forms (save/cancel), products/categories "new" + empty-state CTAs, orders detail (back/ship/status), shipping save, cart (continue/checkout/empty), checkout place-order (:disabled via attrs), product detail add-to-cart, order-confirmed continue.
  • Intentionally left inline (different components, not the standard button): icon-only ghost buttons (#51 gear/hamburger/chevron), compact table row-action buttons (edit/view/delete, px-3 py-1.5 text-xs), the text-danger "Remove" text link (#52), the file-input button (#13), and nav menu links.

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 {% import "macros/ui.html" as ui %} itself (see shop/cart.htmlshop/_cart_body.html). In an {% extends %} child the import must sit directly after {% extends %} with no comment/content before it.


18. Toggle / Switch

Penguin UI: toggle/ (2 variants)

# Location What it is Size
53 assets/views/base.html:13-30 Theme toggle (dark/light/system) — inline <script> JavaScript ~18 lines
54 assets/views/admin/base.html:13-30 Exact duplicate of the theme toggle JS ~18 lines

Details:

  • applyTheme(), setTheme(), currentTheme() — reads/writes localStorage
  • matchMedia('prefers-color-scheme: dark') listener
  • All hand-written vanilla JS, duplicated twice (36 lines total)

19. Inline SVG Icons

Penguin UI: none (Penguin uses Heroicons-equivalent inline SVGs)

# Location Icon Occurrences
55 base.html:70-72,168-170 Hamburger (3-line menu) 2
56 base.html:104-105 Shopping cart 1
57 base.html:116-121 Gear/cog (settings) 1
58 base.html:220-221 Checkmark (toast success) removed — now in vendored toast component
59 checkout.html:62-64,115-117 Chevron-down (dropdown arrow) 2
60 _sidebar.html:30-33 Chevron-right (accordion expand) 1
61 admin/base.html:106-108 Hamburger (admin sidebar toggle) 1
62 admin/base.html:121-125 Gear/cog (admin settings) 1

All are raw inline <svg> with hardcoded <path d="..."> — no icon library, no partials.


20. Empty State

Penguin UI: no direct component, but table empty states exist in Penguin tables

# Location What it is Size
63 assets/views/admin/orders/index.html:38-39 "No orders" message ~2 lines
64 assets/views/admin/catalog/products.html:72-78 "No products" with CTA button ~7 lines
65 assets/views/admin/catalog/categories.html:61-67 "No categories" with CTA button ~7 lines
66 assets/views/shop/_cart_body.html:67-70 "Cart empty" with CTA button ~4 lines
67 assets/views/shop/_sidebar.html:58-59 "No categories" message ~2 lines

21. Dashboard Navigation Cards

Penguin UI: card/

# Location What it is Size
68 assets/views/admin/index.html:12-27 3 dashboard link cards (Products, Categories, Orders) ~16 lines

Details:

  • Each card is an <a> styled with border, hover effect, and nested title+description
  • Same hover pattern: hover:border-primary

22. Checkout Order Summary

Penguin UI: card/ (ecommerce-summary style)

# Location What it is Size
69 assets/views/shop/checkout.html:190-218 Cart summary aside: item list, subtotal, shipping, total, place-order button ~29 lines

Details:

  • Item list with name × quantity + line total
  • Subtotal + shipping + total with tabular-nums
  • Dynamic shipping price from Alpine carrierPrice
  • Disabled submit button when !canSubmit

23. Login Card

Penguin UI: card/

# Location What it is Size
70 assets/views/admin/login.html:6-61 Full login form: header with auth badge, email + password inputs, error alert, submit button ~56 lines

24. Checkout Fieldset Cards

Penguin UI: card/

# Location What it is Size
71 assets/views/shop/checkout.html:34-79 Contact info fieldset (email, name, phone+prefix) ~46 lines
72 assets/views/shop/checkout.html:82-130 Shipping address fieldset (address, city, zip, country) ~49 lines
73 assets/views/shop/checkout.html:133-165 Carrier selection fieldset ~33 lines
74 assets/views/shop/checkout.html:167-180 Payment method fieldset ~14 lines

Each fieldset uses <fieldset> + <legend> with the same rounded-radius border border-outline bg-surface p-6 styling.


25. Order Detail Info Panel

Penguin UI: card/

# Location What it is Size
75 assets/views/admin/orders/show.html:49-77 Customer + shipping + payment info panel ~29 lines
76 assets/views/admin/orders/show.html:79-103 Fulfillment panel (tracking, label link, ship button) ~25 lines
77 assets/views/admin/orders/show.html:106-115 Status update form panel ~10 lines

26. Shipping Method Settings Row

Penguin UI: card/

# Location What it is Size
78 assets/views/admin/shipping/index.html:14-34 Per-carrier settings: name label, price input, enabled checkbox, save button ~21 lines

27. Product/Category Form Wrapper

Penguin UI: card/

# Location What it is Size
79 assets/views/admin/catalog/product_form.html:15-99 Full product edit/create form with all fields ~84 lines
80 assets/views/admin/catalog/category_form.html:15-81 Full category edit/create form with all fields ~66 lines

Both are wrapped in a single card-style <form>.


Summary

# Component Penguin UI Directory Handcoded Instances Total Lines
1 Navbar navbar/ 2 ~143
2 Sidebar (admin) sidebar/ 2 ~46
3 Sidebar (category accordion) sidebar/ 2 ~62
4 Dropdown (settings) dropdown-menu/ 2 duplicates ~103
5 Country/Phone combobox text-input/ 2 ~51
6 Product card card/ 1 ~30
7 Image gallery carousel/ 1 ~19
8 Radio groups (form inputs) 2 ~47
9 Checkbox checkbox/ 3 ~15
10 Text input text-input/ 8 ~146
11 Textarea textarea/ 3 ~10
12 Select select/ 3 ~23
13 File input file-input/ 2 ~12
14 Table table/ 5 ~196
15 Alert/Error alert/ 2 ~9
16 Badge/Pill badge/ 6 ~17
17 Button buttons/ 50+ occurrences ~200+
18 Toggle (theme) toggle/ 2 duplicates ~36
19 Inline SVG icons N/A 8 distinct icons ~50
20 Empty state (table variants) 5 ~22
21 Dashboard cards card/ 1 ~16
22 Checkout summary card/ 1 ~29
23 Login card card/ 1 ~56
24 Checkout fieldsets card/ 4 ~142
25 Order info panels card/ 3 ~64
26 Shipping settings row card/ 1 ~21
27 Form wrappers card/ 2 ~150

Grand total: ~27 distinct handcoded UI component types across ~80 instances, representing approximately 1,600+ lines of handcoded HTML/Tailwind/Alpine that could be replaced by Penguin UI components.

Duplication hotspots:

  • Settings dropdown (base.html:110-162 and admin/base.html:117-166) — 100% copy-paste
  • Theme toggle JS (base.html:13-30 and admin/base.html:13-30) — 100% copy-paste
  • Text input class string — same 80-character Tailwind string appears 15+ times
  • Table class strings (thead, tbody, tr) — copy-pasted 5 times
  • Button variants — inconsistent hover:opacity-75 vs hover:opacity-90