Skip to content

fix(Backdrop, Modal, Offcanvas): scroll lock and modal-open per owner, backdrop after its own fade, hide ends with the transitions - #327

Merged
xidedix merged 1 commit into
v5.7from
fix/backdrop-scroll-lock-owners
Oct 22, 2026
Merged

xidedix merged 1 commit into
v5.7from
fix/backdrop-scroll-lock-owners

Conversation

@xidedix

@xidedix xidedix commented Oct 22, 2026

Copy link
Copy Markdown
Member

Body scroll lock and modal-open are held per owner and released when the hide transitions end, not when the backdrop goes. The backdrop is one element per type, removed after its own fade and brought back while fading, so switching modals no longer flashes 0.5 → 0 → 0.5. No timers: the end comes from the element's running CSS transitions (getAnimations), or a microtask when nothing transitions (reduced motion, transition=false, jsdom, server).

Behaviour changes:

  • Offcanvas: backdrop off/on keeps the lock; scroll applies while open. Without a backdrop, padding-left stayed on body after closing in RTL; with one, the service timer used to remove it.
  • Modal: modal-open kept while another modal holds it, removed on destroy; under reduced motion display: none and portal detached at once (was display: block); with transition=false the backdrop shows and goes without a fade; a modal closed during a server render leaves a clean body; a modal that never opened keeps its own display.
  • Offcanvas: the host becomes inert only once the hide ends, its content still at close: a click on the sliding panel lands on the panel instead of passing through to the fading backdrop, the panel's controls stay inactive and the host takes no focus; the host has tabindex only while open or laid out in place. Responsive offcanvas laid out in place stays without inert. Modal keeps inert at close.
  • Backdrop goes after its own fade (~150 ms, was a 300 ms timer), so from then a click outside the closing offcanvas panel or over the transparent closing modal reaches the page.
  • BackdropService: new lockScroll(owner)/unlockScroll(owner); hideScrollbar()/resetScrollbar() deprecated, they lock on behalf of the service, so an outside resetScrollbar() no longer releases a component lock and clearBackdrop() no longer touches the lock. unlockScroll() removes the padding side the lock set; the page's own inline padding on that side is overwritten while locked and not restored, as before. scrollbarWidth is measured when the first owner locks, not on setBackdrop(). setBackdrop() returns the shared element of its type, pair it with clearBackdrop(); new optional fade argument. A backdrop released before its delayed show no longer flashes, one set again before it waits for it instead of popping in, and closing and reopening a modal in one tick keeps one backdrop.
  • ModalService: addOpenClass(modal)/removeOpenClass(modal).
  • OffcanvasModule, ModalModule: services no longer in providers, a lazy module or a routed standalone component gets the root instances: one backdrop and one backdropClick$ per page, and opened$ and id toggles reach modals across the former module boundary.

Offcanvas specs run the component in a test host, no hosts or backdrops left in the shared document.

…, backdrop after its own fade, hide ends with the transitions

- BackdropService: lockScroll(owner)/unlockScroll(owner); hideScrollbar()/resetScrollbar() deprecated, lock on behalf of the service; clearBackdrop() no longer touches the lock; scrollbarWidth measured at the first lock; unlock removes only the padding side it set.
- Backdrop: one element per type, removed after its own fade, revived while fading, no 0.5 → 0 → 0.5 flash; setBackdrop(type, fade); no flash when released or revived before its delayed show.
- Modal: lock and modal-open (ModalService.addOpenClass/removeOpenClass) held until the host and dialog transitions end, released at once on destroy; reduced motion and transition=false hide at once; a close during a server render leaves a clean body; a never-opened modal keeps its display.
- Offcanvas: lock follows visible and scroll, not the backdrop; rtl padding removed without a backdrop; content inert at close, the host takes clicks until the hide ends and focus only while its content is active.
- shared/transition.utils: afterTransitions(element), the running CSS transitions, no timers.
- OffcanvasModule, ModalModule: no services in providers, root instances.
- Offcanvas specs run in a test host.
@xidedix
xidedix merged commit 5f9fe97 into v5.7 Oct 22, 2026
9 checks passed
@xidedix
xidedix deleted the fix/backdrop-scroll-lock-owners branch October 22, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant