{% comment %} The modal's behaviour, and the one helper it shares with the lists. Included once by templates/base.html, after htmx and before the deferred Alpine, so the definitions exist by the time Alpine boots — on every page, because a create button can sit on any of them. The markup is components/modal.html, rendered by the `{% templatetag openblock %} modal {% templatetag closeblock %}` tag in apps/common/templatetags/common_extras.py. **`$modal(id)`.** Opens a dialog by id from anywhere. Most triggers are not inside the dialog's own Alpine scope — a header button, or the empty state of a rows partial that htmx replaces on every mutation — so reaching it through `$refs` would depend on where each one happens to sit. **`bbModal(closeOn)`.** The dialog's own x-data. It closes after a request whose HX-Trigger names `closeOn` and stays open otherwise, which is the reason it reads the header rather than `detail.successful`: see `bbChanged` below. With no `closeOn` it never closes itself — the right thing for a form that navigates away on success (an HX-Redirect, or a plain POST). Its forms are reset as it OPENS, not as it closes, so every open starts from the page's own values whichever way the last one ended — Escape, the × button, Cancel, the backdrop or a successful submit. Resetting on the `close` event looked equivalent and is not: Chrome dispatches that event with the next rendering update, so it is late or absent in a tab that is not being painted, and a form reset that depends on it is a form reset that sometimes does not happen. A page re-rendered with a refusal loses nothing to it either: reset() returns each field to its `value` attribute, which is what the server filled in. Hidden inputs are kept as they are — see resetTyping(). The backdrop closes it only when the press AND the release both land on the backdrop. A click's target is the nearest common ancestor of the two, so selecting the text in a field and letting go over the backdrop is a "click on the dialog" — and without the press check it would throw the typing away. It finds its dialog ONCE, in init(), and keeps it. `$root` is the nearest x-data to wherever it is read from, and a modal's body may hold an x-data of its own — the flows "Start from" picker does — so a Cancel button inside it calling close() got that form as `$root`, and a form has no close(). Read in init(), the only call that always runs in the dialog's own scope, it is the dialog. **`bbChanged`.** Whether a response actually changed anything. apps/common/htmx answers both success and refusal with a 204, so `detail.successful` is true for "Tag created" AND for "That tag already exists" — a create form keyed on it wiped what the operator typed on the one response where they needed it back. Only the success path sets the HX-Trigger, which is what this reads. It lived in partials/_list_behaviours.html until the modal needed it on pages that never include that partial. {% endcomment %}