Require a complete dotted email before inline validation reports success, preserve the typed email after valid revalidation, and update progress via the actual progress element so visible progress is not stuck at 100%. req: htmx_equivalents/005 req: examples/001 req: test/006
hemx HTML examples
A copy-pasteable pattern gallery for the boring HTML UX patterns popularized by
htmx. The point is not to clone htmx attributes; it is to show the hemx idiom:
plain .heml, generated resources, server-owned Rust state, keyed partials, and
tiny runtime behavior. req: htmx_equivalents/001 req: htmx_equivalents/005 req: examples/001
Run it:
cargo run -p hemx-html-examples
Open http://127.0.0.1:3029.
Pattern matrix
Names match the htmx example URL slug exactly, e.g. modal-custom from
https://htmx.org/examples/modal-custom/. Rust resource names use normal
identifier spelling only where the language requires it.
Status legend:
- implemented: copyable
.hemland server handlers exist in this example. - integration-owned: use hemx generated resources plus app/host/browser policy; do not grow hemx core for the policy.
- refused: would clone htmx/client framework behavior or a third-party UI kit.
- deferred: useful, but needs a later vertical slice and proof before becoming a copyable hemx pattern.
| htmx example slug | Status | hemx idiom / boundary | Proof anchor |
|---|---|---|---|
click-to-edit |
implemented | A read view and edit form are the same generated contact_card partial; the server toggles editing and returns gallery::contact_card.replace(...). |
templates/partials/contact_card.heml, contact_card_handlers::edit_contact, save_contact |
bulk-update |
deferred | Same generated-form path as inline validation, but needs a real multi-row selection/write slice so batch semantics are tested instead of claimed. | Next slice should add keyed batch rows plus one server-owned bulk command. |
click-to-load |
implemented | The server owns the loaded count and returns generated keyed loaded_row replacements plus status text. |
gallery_handlers::load_more, LoadedRow |
delete-row |
implemented | Server state removes the row and returns gallery::editable_row.remove(id). |
editable_row_handlers::delete_row |
edit-row |
implemented | A table row is a keyed .heml partial with generated edit/save forms; no selector target strings. |
templates/partials/editable_row.heml, editable_row_handlers::edit_row, save_row |
lazy-load |
implemented | data-hemx-revealed dispatches a generated form once when visible; the server swaps a generated lazy panel. |
gallery.heml, gallery_handlers::lazy_load, LazyPanel |
inline-validation |
implemented | A generated form reports field failure with validate_email_form.error(...), focuses the field, and updates status text. |
templates/gallery.heml, gallery_handlers::validate_email |
infinite-scroll |
implemented | A revealed sentinel form posts to the same server-owned loading model and replaces generated keyed rows. | gallery_handlers::infinite_scroll, data-hemx-revealed, infinite_row |
active-search |
implemented | The search form posts a query; the server derives result rows and replaces generated search_result keyed partials. |
gallery_handlers::search, SearchResult |
progress-bar |
implemented | data-hemx-interval ticks a server-owned progress value and replaces a generated progress partial. |
gallery_handlers::tick_progress, ProgressMeter |
value-select |
implemented | The first select posts a generated form; the server derives and replaces generated option rows for the second select. | gallery_handlers::choose_category, ValueOption |
animations |
integration-owned | CSS transitions are presentation policy around generated replacements; hemx should only preserve stable DOM boundaries. | Use keyed partials and app CSS; no core animation framework. |
file-upload |
integration-owned | Upload transport, size limits, progress, storage, and security are app/integration policy. | Needs product-owned upload route before becoming copyable. |
file-upload-input |
integration-owned | Preserving file inputs after errors is browser/security policy; hemx should not fake file state in core effects. | Use app-owned upload form policy. |
reset-user-input |
implemented | A generated form updates status and returns .clear() after successful submission. |
gallery_handlers::reset_message |
dialogs |
integration-owned | Browser alert/confirm/prompt are app policy; hemx can expose event boundaries but should not own dialog UX. |
Use native controls or host/app code. |
modal-uikit |
refused | Third-party UI kit integration is not a hemx core pattern. | Keep as app-owned integration. |
modal-bootstrap |
refused | Third-party UI kit integration is not a hemx core pattern. | Keep as app-owned integration. |
modal-custom |
deferred | A custom modal can be a generated partial plus focus/escape policy, but needs accessibility proof before copy/paste. | Later slice should include keyboard/focus tests. |
tabs-hateoas |
deferred | Good hemx fit: server-owned selected tab and generated tab panel replacement; needs a focused slice. | Later slice should add one tab group. |
tabs-javascript |
refused | Client-owned tab state is exactly what generated server-owned state is meant to avoid unless a product needs it. | Prefer tabs-hateoas. |
keyboard-shortcuts |
integration-owned | Keyboard policy belongs to the app/host; hemx should only receive explicit events. | Use generated app-level events / app JS when needed. |
sortable |
integration-owned | Drag/drop ordering needs a browser library or pointer policy plus server reorder command. | Keep Sortable.js as app-owned integration until proven reusable. |
update-other-content |
implemented | Generated effects can update multiple slots from one handler; validation and search already update status plus rows/errors. | validate_email, search handlers. |
confirm |
integration-owned | Confirmation wording and irreversible-action policy belong to the app; hemx should not own a global confirm system. | Use native confirm/app dialog around generated delete forms. |
async-auth |
integration-owned | Token refresh/auth sessions belong to auth/session integration, not hemx core. | See auth/session recipe boundary. |
web-components |
integration-owned | Shadow DOM/custom elements are host integration; hemx can emit events but should not pierce component internals. | Use app-owned web component adapters. |
move-before |
refused | Experimental DOM preservation API is not a stable hemx contract. | Avoid until browser support and a product need make it boring. |
Boundary
Implemented rows must remain runnable hemx behavior. Deferred/integration-owned/refused
rows are not failures; they prevent a trophy checklist from turning hemx into a
client framework. Promote a deferred row only when the slice proves a reusable,
boring contract with .heml, generated resources, server-owned state, and tests.