You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
notifyPushView.closePush() calls a11y.gotoPreviousActiveElement() unconditionally on every push close. Push notifications never grab focus on open (non-modal <dialog>, aria-modal=\"false\"). For pushes where the user did not tab into the push before it auto-closes, this fires a focus restore that has no contract to fulfil and can land focus in the wrong place.
Repro
Use a course with adapt-contrib-trickle configured with _styleAfterClick: \"hidden\" (default) on a block. Add a next block with a heavy image so the load wait is observable.
Click the trickle Continue button via keyboard / screen reader.
Trickle hides the clicked button container (u-display-none). Focus orphans to body.
~200ms later the a11y-push dialog opens and the screen reader reads "Loading".
~350ms later the push auto-closes. closePush calls gotoPreviousActiveElement. The previous active element was the trickle button which is now non-readable. focusFirst walks the DOM forward and lands on the first focusable element in Adapt.navigation (e.g. a navbar button).
Trickle's own controller.scroll -> focusFirst(\$('.next-block')) either has not run yet (next block still loading) or runs after and is overridden.
gotoPreviousActiveElement is correct for popups (modal, grab focus on open, restore on close). Push notifications are different:
Non-modal <dialog> opened via dialog.open = true (not showModal())
aria-modal=\"false\"
Never grab focus on open
May or may not receive focus during their lifetime (user can tab into the close button, click the body, etc.)
When the user never tabbed into the push, there is no focus contract to restore on close. Firing the restore can override correct focus management elsewhere (in trickle's case, the focusFirst that should land on the next block).
Proposed fix
Only restore focus on close if focus was actually inside the push at close time. Pattern already used in the same file for the Esc key handler (onKeyDown -> isFocusInPopup):
Summary
notifyPushView.closePush()callsa11y.gotoPreviousActiveElement()unconditionally on every push close. Push notifications never grab focus on open (non-modal<dialog>,aria-modal=\"false\"). For pushes where the user did not tab into the push before it auto-closes, this fires a focus restore that has no contract to fulfil and can land focus in the wrong place.Repro
adapt-contrib-trickleconfigured with_styleAfterClick: \"hidden\"(default) on a block. Add a next block with a heavy image so the load wait is observable.additionalContentLoadedmessage so trickle callsnotify.read(a11y-push) - see Update: Alert screen reader users when content has been added (fixes #232) adapt-contrib-trickle#233.u-display-none). Focus orphans to body.closePushcallsgotoPreviousActiveElement. The previous active element was the trickle button which is now non-readable.focusFirstwalks the DOM forward and lands on the first focusable element inAdapt.navigation(e.g. a navbar button).controller.scroll->focusFirst(\$('.next-block'))either has not run yet (next block still loading) or runs after and is overridden.Net: focus jumps to the navbar mid-flow.
Root cause
notifyPushView.closePush():gotoPreviousActiveElementis correct for popups (modal, grab focus on open, restore on close). Push notifications are different:<dialog>opened viadialog.open = true(notshowModal())aria-modal=\"false\"When the user never tabbed into the push, there is no focus contract to restore on close. Firing the restore can override correct focus management elsewhere (in trickle's case, the
focusFirstthat should land on the next block).Proposed fix
Only restore focus on close if focus was actually inside the push at close time. Pattern already used in the same file for the Esc key handler (
onKeyDown->isFocusInPopup):Behaviour matrix:
a11y-push(notify.read) auto-close, user never tabbed ingotoPreviousActiveElement(may fall back to navbar)pushauto-close, user never tabbed inisFocusInPopup)_openNotifyOnClick)Net: behaviour only changes for the auto-close, user-not-focused-in path - which is the path that has no business shifting focus.
Scope
js/views/notifyPushView.jsnotifyPopupView) is correct as-is and not touchedRelated
adapt-contrib-trickleexposed this vianotify.readuse - Update: Alert screen reader users when content has been added (fixes #232) adapt-contrib-trickle#233Posted via collaboration with Claude Code