Lock page scroll while cookie panel is open

The preference panel is a fixed, backdrop-less card whose only
scrollable region is the category list. Wheel events over the
header, footer, padding, or at the list edges chained to the host
page, so the page scrolled instead of the panel.

Treat the open panel as the modal it already claims to be: lock
document scroll while state is "panel" (compensating for scrollbar
width to avoid layout shift) and add overscroll-behavior: contain
to the list. Restore scroll on close and on disconnect so the page
is never left frozen.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-10 16:53:58 +02:00
parent 1e3c1342e3
commit e3ccb76b03
2 changed files with 34 additions and 0 deletions

View File

@@ -106,6 +106,7 @@ export const THEMED_STYLES = `
probo-preference-panel probo-category-list {
overflow-y: auto;
overscroll-behavior: contain;
flex: 1;
min-height: 0;
}

View File

@@ -21,6 +21,9 @@ import { THEMED_STYLES } from "./styles";
export class ProboThemedBanner extends HTMLElement {
private shadow: ShadowRoot;
private scrollLocked = false;
private prevOverflow = "";
private prevPaddingRight = "";
constructor() {
super();
@@ -150,6 +153,11 @@ export class ProboThemedBanner extends HTMLElement {
}
});
root.addEventListener("probo-state", (e: Event) => {
const { state } = (e as CustomEvent).detail;
this.setScrollLock(state === "panel");
});
this.shadow.querySelector("[data-action=back]")?.addEventListener("click", () => {
root.setState(root.client.hasConsent ? "hidden" : "banner");
});
@@ -176,6 +184,31 @@ export class ProboThemedBanner extends HTMLElement {
});
}
disconnectedCallback(): void {
this.setScrollLock(false);
}
private setScrollLock(locked: boolean): void {
if (locked === this.scrollLocked) return;
const root = document.documentElement;
if (locked) {
const scrollbarWidth = window.innerWidth - root.clientWidth;
this.prevOverflow = root.style.overflow;
this.prevPaddingRight = root.style.paddingRight;
root.style.overflow = "hidden";
if (scrollbarWidth > 0) {
root.style.paddingRight = `${scrollbarWidth}px`;
}
} else {
root.style.overflow = this.prevOverflow;
root.style.paddingRight = this.prevPaddingRight;
}
this.scrollLocked = locked;
}
private applyTexts(config: BannerConfig): void {
const texts = config.texts ?? {};