ui: prevent layout shift when mobile search overlay opens
- SearchPanel: focus input with { preventScroll: true } so the browser
doesn't auto-scroll the underlying page when the overlay mounts.
- SearchPanel: add overscroll-contain on the scroll container so
reaching the panel's edges does not chain into the body on Android.
- PublicLayout: lock background scroll while the mobile search overlay
is open using the iOS-compatible position-fixed + restore pattern,
so the page underneath cannot move at all.
This commit is contained in:
@@ -70,7 +70,11 @@ export function SearchPanel({
|
||||
const langParam = useMemo(() => langQuery(lang), [lang]);
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
// Avoid scroll-into-view: browsers default-scroll the focused element
|
||||
// into the viewport, which moves the underlying page when the search
|
||||
// overlay opens from a scrolled position. `preventScroll` keeps the page
|
||||
// exactly where it was.
|
||||
inputRef.current?.focus({ preventScroll: true });
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -133,7 +137,7 @@ export function SearchPanel({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ark-header-menu-enter fixed inset-x-0 bottom-0 top-[64px] z-50 overflow-y-auto bg-[#0f0f13] md:hidden">
|
||||
<div className="ark-header-menu-enter fixed inset-x-0 bottom-0 top-[64px] z-50 overflow-y-auto overscroll-contain bg-[#0f0f13] md:hidden">
|
||||
<div className="border-t border-white/10 px-5 pb-6 pt-3 max-[360px]:px-3">
|
||||
<div className="flex h-12 items-center gap-2">
|
||||
<div className="flex h-11 min-w-0 flex-1 items-center gap-2 rounded-full border border-ark-gold bg-[#191921] px-3 shadow-[0_0_0_2px_rgba(245,180,53,0.12)]">
|
||||
|
||||
@@ -331,6 +331,35 @@ export function PublicLayout() {
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
// Lock background scroll while the mobile search overlay is open.
|
||||
// Uses the iOS-compatible position-fixed pattern so the underlying page
|
||||
// doesn't move at all (overflow:hidden alone is not enough on iOS Safari).
|
||||
useEffect(() => {
|
||||
if (!mobileSearchOpen) return;
|
||||
const scrollY = window.scrollY;
|
||||
const body = document.body;
|
||||
const prev = {
|
||||
position: body.style.position,
|
||||
top: body.style.top,
|
||||
left: body.style.left,
|
||||
right: body.style.right,
|
||||
width: body.style.width,
|
||||
};
|
||||
body.style.position = "fixed";
|
||||
body.style.top = `-${scrollY}px`;
|
||||
body.style.left = "0";
|
||||
body.style.right = "0";
|
||||
body.style.width = "100%";
|
||||
return () => {
|
||||
body.style.position = prev.position;
|
||||
body.style.top = prev.top;
|
||||
body.style.left = prev.left;
|
||||
body.style.right = prev.right;
|
||||
body.style.width = prev.width;
|
||||
window.scrollTo(0, scrollY);
|
||||
};
|
||||
}, [mobileSearchOpen]);
|
||||
|
||||
return (
|
||||
<div className="min-h-full flex flex-col">
|
||||
<DocumentMeta />
|
||||
|
||||
Reference in New Issue
Block a user