v3: modals stop shifting the page — scroll lock moves to <html> and holds the scrollbar's gutter only while locked (and only if a bar was showing), shared by Modal and ConfirmModal with stacked-lock counting; scrollbars take a theme-tinted thumb, inner scrollers go thin and contain their overscroll
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 40s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-19 21:12:19 +00:00
parent a9edd8fbb8
commit 5205097bb6
5 changed files with 50 additions and 8 deletions

View File

@@ -0,0 +1,31 @@
import { useEffect } from "react";
let locks = 0;
/**
* Freezes the page's scroll while the calling component is mounted — for
* modals, whose backdrop shouldn't let the feed slide underneath.
*
* Hiding the viewport's overflow drops its scrollbar, which would widen the
* page and shift everything behind the backdrop. If a bar is showing, its
* gutter is held for the duration of the lock instead, so the layout stays
* put; no gutter is reserved the rest of the time. Locks are counted so a
* confirm stacked on a modal doesn't release the page when it closes first.
*/
export function useScrollLock() {
useEffect(() => {
const root = document.documentElement;
if (locks++ === 0) {
if (window.innerWidth > root.clientWidth) {
root.style.scrollbarGutter = "stable";
}
root.style.overflowY = "hidden";
}
return () => {
if (--locks === 0) {
root.style.overflowY = "";
root.style.scrollbarGutter = "";
}
};
}, []);
}