ln-modal is applied to a native
<dialog data-ln-modal id="..."> element, paired with trigger buttons
(data-ln-modal-for="...") and dismiss controls (data-ln-modal-close). When
triggered, it sets data-ln-modal="open", invokes dialog.showModal() for native
focus trapping, and locks background scrolling via .ln-modal-open on <body>.
On dismiss clicks, backdrop clicks, or pressing Escape, it cleanly closes the dialog, restores
keyboard focus, and clears the scroll lock.
In ln-ashlar, this follows the Three-Layer Architecture: ln-modal
is a Layer 1 Simple Component that manages the native overlay state and accessibility, while
ln-ui-coordinator is the Layer 2 Coordinator that handles triggers, hash navigation
(#modalId), record filling (ln-fill), and auto-closing on AJAX success.
The Modal & UI Coordinator Doctrine
The contract is absolute: ln-modal (on <dialog data-ln-modal>) manages
ONLY the binary overlay state. ln-ui-coordinator (placed on
<dialog data-ln-ui-coordinator> or container) acts as the Layer 2 Coordinator,
orchestrating inner forms (<form data-ln-form>), triggers
(data-ln-modal-for), deep links (#modalId:param), toasts, and auto-closing on
ln-ajax:success.
Focus Trapping & Accessibility (WAI-ARIA)
Native Focus Trap: Tabbing cycles keyboard focus strictly inside the native
<dialog> when opened via showModal().
Background Scroll Lock: Opening a modal suppresses background scrolling by applying the
ln-modal-open class to <body>.
Escape Key Dismissal: Pressing ESC routes through the attribute
synchronizer to ensure before-close lifecycle validation always runs.
Focus Return: When closed, keyboard focus returns precisely to the trigger element that
launched it.
Demonstrates codec isolation: ln-tabs and ln-modal share the same URL hash using
the ln-core hash codec. Each component reads and writes ONLY its own namespace segment —
foreign segments are preserved on every write. The modal's namespace is its id
(demo-hash-modal).
Manual tests:
Click Edit #5, switch to the Members tab. The URL becomes
#demo-hash-modal:5&demo-tab:members, the modal STAYS open, and the
Title field is filled by the ln-ui-coordinator.
Reverse order (regression direction): switch to the Members tab FIRST, then
click Edit #5. The URL becomes #demo-tab:members&demo-hash-modal:5 and the tab
STAYS on Members — the modal anchor click MERGES its segment instead of replacing the whole
fragment.
Reload with #demo-tab:members&demo-hash-modal:42 in the URL. The Members tab
should be active AND the modal open with Title "Internal audit log" filled.
With the modal open, press Back → modal closes, tab state intact.
Overview panel content. Switch tabs to see the modal hash segment preserved.
Members panel content. Switch tabs to see the modal hash segment preserved.