diff --git a/app/components/elements/Dialog.browser.test.tsx b/app/components/elements/Dialog.browser.test.tsx
index 1d073b907..10b0d737c 100644
--- a/app/components/elements/Dialog.browser.test.tsx
+++ b/app/components/elements/Dialog.browser.test.tsx
@@ -120,6 +120,54 @@ describe("SendouDialog", () => {
expect(openDialog().open).toBe(true);
});
+ test("locks page scrolling while open without changing the page width", async () => {
+ const tall = document.createElement("div");
+ tall.style.height = "300vh";
+ document.body.appendChild(tall);
+ cleanupFns.push(() => tall.remove());
+ const root = document.documentElement;
+ const widthBefore = root.clientWidth;
+
+ const screen = await render(
+ withRouter(
+ Open}
+ showCloseButton
+ >
+ Content
+ ,
+ ),
+ );
+
+ await screen.getByRole("button", { name: "Open" }).click();
+ await expect.element(screen.getByText("Content")).toBeVisible();
+ await vi.waitFor(() => expect(root.style.overflow).toBe("hidden"));
+ expect(root.clientWidth).toBe(widthBefore);
+
+ await screen.getByRole("button", { name: "Close" }).click();
+ await vi.waitFor(() => expect(root.style.overflow).toBe(""));
+ expect(root.style.scrollbarGutter).toBe("");
+ expect(root.clientWidth).toBe(widthBefore);
+ });
+
+ test("releases the scroll lock when an open dialog unmounts", async () => {
+ const screen = await render(
+ withRouter(
+ {}}>
+ Content
+ ,
+ ),
+ );
+ await expect.element(screen.getByText("Content")).toBeVisible();
+ await vi.waitFor(() =>
+ expect(document.documentElement.style.overflow).toBe("hidden"),
+ );
+
+ await screen.unmount();
+ expect(document.documentElement.style.overflow).toBe("");
+ });
+
test("focuses the dialog itself instead of the close button on open", async () => {
await render(
withRouter(
diff --git a/app/components/elements/Dialog.tsx b/app/components/elements/Dialog.tsx
index 5f6df54a7..3bbdd53c3 100644
--- a/app/components/elements/Dialog.tsx
+++ b/app/components/elements/Dialog.tsx
@@ -67,9 +67,19 @@ function DialogElement({
children,
ref,
}: DialogElementProps) {
+ const dialogRef = React.useRef(null);
+ useScrollLockWhileOpen(dialogRef);
+
return (