import { useEffect } from "react"; // `overflow: hidden` on is not enough on iOS Safari — the page still // rubber-bands behind the overlay. Pinning the body and restoring scroll on // release is the only approach that holds across engines. let locks = 0; let savedScrollY = 0; let saved: Partial = {}; function lock() { if (locks++ > 0) return; const body = document.body; savedScrollY = window.scrollY; saved = { position: body.style.position, top: body.style.top, left: body.style.left, right: body.style.right, width: body.style.width, overflow: body.style.overflow, }; body.style.position = "fixed"; body.style.top = `-${savedScrollY}px`; body.style.left = "0"; body.style.right = "0"; body.style.width = "100%"; body.style.overflow = "hidden"; } function unlock() { if (--locks > 0) return; locks = 0; Object.assign(document.body.style, saved); window.scrollTo(0, savedScrollY); } export function useScrollLock(active: boolean) { useEffect(() => { if (!active) return; lock(); return unlock; }, [active]); }