Two modal pipelines coexisted after the URL-addressable pilot — modal.js
(inline, ~30 lines) and modal-router.js (routed, ~150 lines) — operating
on different attribute namespaces and exposing different APIs. Future
modal authors had two systems to learn with no naming convention to
help them pick the right one for a given use case.
Consolidates both into static/js/modals.js with two clearly-named
pipelines and a single window.modals.* API:
Inline modal — content pre-rendered in the page.
Hooks: data-inline-modal-open="<dialog-id>"
data-inline-modal-close
API: window.modals.openInline(idOrEl)
window.modals.closeInline(idOrEl)
Use: confirmations, transient prompts, in-page forms without
URL value.
Routed modal — content fetched from a URL, ?modal=<path> in URL,
with history + share-link + refresh-survival.
Hooks: <a data-routed-modal href="<path>">
data-routed-modal-dismiss
API: window.modals.openRouted(path)
window.modals.closeRouted()
Use: content with standalone-page meaning.
Single document-level click delegation handles all four attribute
hooks; one DOMContentLoaded handler binds dialog 'close' / 'cancel' /
backdrop on the routed slot; shared popstate and htmx:responseError
listeners. Behaviour unchanged — pure rename + colocation.
Renamed across 11 templates and files-overlay.js. Old data-modal-*
attributes and window.openModal/closeModal globals are gone — clean
break (no back-compat shims). AGENTS.md "Modals: inline vs routed"
section documents the decision guide for new modals.
Verified: 573 backend tests pass. 5/5 Chromium smoke checks pass
(inline open/close, Esc, backdrop, routed open+save, routed Esc).
Console clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9 lines
611 B
HTML
9 lines
611 B
HTML
{# Routed-modal layout. Templates that extend `base_layout` render through
|
|
this when the request carries `HX-Modal: 1` (see `inject_base_layout` in
|
|
app.py). The persistent <dialog id="modal-container"> in base.html provides
|
|
top-layer + backdrop + focus-trap + Esc-to-close semantics. Templates that
|
|
extend base_layout MUST NOT wrap their content in a <dialog> — nested
|
|
<dialog> collapses to 2px. Use a <div> root and let the outer slot own
|
|
dialog semantics. See AGENTS.md "Modals: inline vs routed" for the full
|
|
convention including hooks and JS API. #}
|
|
{% block content %}{% endblock %}
|