Capture the proven whole-form hx-select swap method as a reusable skill so every later modal migration is cheaper and consistent. No app code changes. - SKILL.md: the per-migration playbook (classify → baseline → characterize → consolidate render fns → templatize → wire HTMX → collapse routes → verify → commit → feed skill) + Growth contract + non-negotiables. - reference/swap-doctrine.md: the four swap rules, focus invariant, Alpine-survives- swap hardening, target-selector strategy — worked from the real edit.clj swaps (memo no-request, account→location targeted cell, amount→totals sibling-tbody, vendor/mode/row whole-form). 0 OOB. - reference/render-functions.md: explicit-data or top-rooted cursor; the MapCursor fake + transaction-account-row-no-cursor* twin as the smell to remove. - reference/form-vs-wizard.md: classification + the data-driven session-backed (formtools SessionStorage) engine that replaces the snapshot round-trip + protocol. - reference/selmer-conventions.md: STUB, validated in Phase 2. - component-cookbook.md / gotchas.md / test-recipes.md / scorecard.md: seeded from what transaction-edit proves (7 cookbook entries, caret-survival + typeahead test recipes, scorecard baseline LOC 1608 / ~12 routes / 1 no-cursor twin / 2 faked roots / 0 OOB). Scorecard (Transaction Edit baseline, before Phase 2): LOC 1608, routes ~12, no-cursor twins 1, faked-cursor roots 2, snapshot merges ~75, OOB 0, mixed hx- 8.
69 lines
2.6 KiB
Markdown
69 lines
2.6 KiB
Markdown
# Selmer template conventions
|
|
|
|
> **Status: STUB — validated in Phase 2.** This file describes the target. The Selmer
|
|
> dependency, render helper, and interop bridge are added in Phase 2 (Transaction Edit);
|
|
> rewrite this file from the *real, verified* example once that lands, and record each
|
|
> converted component in `component-cookbook.md`.
|
|
|
|
## Why Selmer for interactive components
|
|
|
|
In Hiccup the same Alpine/HTMX attribute is sometimes a keyword and sometimes a string in
|
|
the same file — there's no rule a reader (or an LLM) can rely on:
|
|
|
|
```clojure
|
|
;; All of these appear in one component today:
|
|
:x-ref "input" "x-ref" "hidden"
|
|
:x-model "value.value" "x-model" "search"
|
|
"@keydown.down.prevent.stop" "tippy.show();" ; handlers MUST be strings
|
|
:x-init "..." ; structural attrs are keywords
|
|
```
|
|
|
|
In a Selmer template the same markup is unambiguous plain HTML:
|
|
|
|
```html
|
|
{# templates/components/typeahead.html #}
|
|
<div class="relative" x-data="{{ x_data|safe }}" x-model="{{ x_model }}">
|
|
<a class="{{ classes }}" x-ref="input" tabindex="0"
|
|
@keydown.down.prevent.stop="tippy?.show()"
|
|
@keydown.backspace="tippy?.hide(); value = {value:'', label:''}">
|
|
<span x-text="value.label"></span>
|
|
</a>
|
|
...
|
|
</div>
|
|
```
|
|
|
|
Note the `tippy?.` null-guard carried over from the swap doctrine — Selmer doesn't change
|
|
the Alpine-survives-swap requirement.
|
|
|
|
## Render helper + interop bridge (the Phase 2 foundation)
|
|
|
|
```clojure
|
|
(defn render [tpl ctx] (selmer/render-file tpl ctx))
|
|
(defn hiccup->html [h] (hiccup/html h)) ; embed hiccup inside selmer via {{ frag|safe }}
|
|
;; selmer fragment inside hiccup: [:div (hiccup/raw (render "..." ctx))]
|
|
```
|
|
|
|
The bridge must work **both ways** during the strangler transition: a Hiccup component
|
|
renders inside a Selmer template (pass `(hiccup->html h)` into the context, render with
|
|
`|safe`), and a Selmer fragment renders inside a Hiccup tree (`(hiccup/raw (render ...))`).
|
|
Prove both in Phase 2 before broad use.
|
|
|
|
## Composition
|
|
|
|
Selmer composes via `{% include %}` and `{% block %}`. Prefer small per-component
|
|
templates that the cookbook references by path. Keep `|safe` to values the server fully
|
|
controls (rendered Hiccup, JSON for `x-data`), never raw user input.
|
|
|
|
## Scope (Open decision 2)
|
|
|
|
Hybrid: convert interactive/attribute-heavy components first; static markup may stay
|
|
Hiccup. Revisit a fuller sweep in Phase 11.
|
|
|
|
## Attribute-consistency scorecard (heuristic 8)
|
|
|
|
```bash
|
|
grep -cE '"x-[a-z]|"hx-[a-z]|"@' <migrated-template> # → 0 mixed encodings in Selmer
|
|
```
|
|
A migrated Selmer template has no mixed `:x-`/`"x-"` encodings because everything is plain
|
|
HTML.
|