Repository navigation
Domma v0.57.0
v0.57.0 - Focus trapping in overlays (2026-10-24)
Focus stays in modals, slideovers, dialogs and popovers
E.modal,E.slideoverandE.alert/E.confirm/E.promptnow keep Tab and Shift+Tab inside while they
are open, and put focus back where it was when they close. The modal's cycle runs through its X, the content
you put in it and the footer buttons in the order they show. A slideover focuses its panel on open, so the
first Tab reaches its first control; a field you focus afteropen()keeps focus.- New options
trapFocus(defaulttrue) andreturnFocus(defaulttrue) on all three. A modal or slideover
without a backdrop is not trapped unless you passtrapFocus: true, since the page beside it stays usable. - Overlays stack: a modal from a slideover, a confirm from a modal, a slideover from a slideover. The newest
holds Tab; closing it hands focus back to where it was in the one beneath, which holds Tab again. An overlay
closed underneath a newer one leaves focus alone and passes its own return target up. - Toasts and their buttons, popovers, context menus, date pickers and other floating panels opened from an
overlay keep focus and clicks; Tab moves through them and back into the overlay. The page behind, and an
overlay beneath, stay out of reach. Mark anything else that should take focus withdata-focus-trap-allow. - Tab is left alone when something used it first: a textarea that indents on Tab, a grid that moves between
cells, an editor's own Tab handling (anything that callspreventDefault()). - Popovers keep their non-modal Tab behaviour. Inside an overlay, Tab off the end of one goes to the next
control in that overlay, not the page behind.trapFocus: trueon a popover now uses the focus trap. - Escape and the close buttons still call
close(), so a guard you put onclosestill runs.destroy()
removes the trap. Nothing is madeinert. - Focus trap: Tab is decided as the key reaches
window, so handlers on the way can claim it; new options
allowOutsideFocus(false|true|'layers'| function) andregister; a trap whose containers left the
page lets go of focus.
Types generated from the source
- The TypeScript declarations (
assets/types/, the package'stypes) are now generated from the JSDoc insrc/
bynpm run build:types(part ofnpm run build) instead of being written by hand. They cover what they did
before and more: the router (R), auth (A), forms (F), effects, blueprint helpers (B), web components,
everyE.*factory and its instance, and the Schema Builder.H.get<T>()and the other HTTP methods are
generic,$()events are typed by name ('click'gives aMouseEvent), andisDomma()is a type guard. - Every alias Domma puts on
windowis now declared as a global (E,H,I,T,R,A,F,Bjoined
$,_,M,D,S), plusFLfrom domma-flags. - Types now follow the code where the old hand-written ones did not: names and members that never existed are
gone (HttpResponse,ThemeChangeEvent,BackToTopScrollEvent,BackToTopShowEvent,H.setDefaults,
S.setPrefix, several DataTable methods), and signatures that were wrong are fixed (Tfilter/filterBy,
PageInfo.page,theme.preview(theme)). Module values such asutilsorelementsare type-only exports, since
the ES module never exported them at runtime. - The separate hand-kept copy in
public/assets/ide/phpstorm/is retired; point an IDE atassets/types/index.d.ts. npm run check:types(inmake checkand CI) fails when the committed declarations differ from a fresh
generation, and type-checks a small consumer undertsc --strict. See docs/Types.md.
Fixes
-
A click, key or input handler inside a modal, slideover or dialog may move focus outside it on purpose - an "Insert" button that focuses the page editor behind its slideover, inserts, then closes keeps working (
allowScriptFocus, on for the overlays). Focus that escapes any other way is still brought back. -
A date picker or dropdown opened from inside a slideover or modal draws above it instead of behind it.
-
A.createUserInfo()renders the signed-in user and wires its logout button; it had no constructor, so it never did. The button keeps working after a login or logout re-renders it, anddestroy()stops listening for auth changes.