From adfef5c83079ed3ba1242efbdb81d507712964c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Sun, 14 Jun 2026 20:46:22 +0200 Subject: [PATCH] fix(twenty-front): prevent stale transition page from blocking scroll (#21551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Switching from the app to Settings sometimes makes every settings page unscrollable until a full page reload. ## Cause `MainAppLayoutOutlet` cross-fades between the app and settings with `AnimatePresence`, rendering both pages into the same grid cell (`grid-area: 1 / 1`). When an exit animation doesn't clean up, the outgoing page stays mounted on top of the active one. With the default `pointer-events: auto`, that (now invisible) stale node intercepts wheel/scroll events before they reach the page underneath — so the page won't scroll even though its own scroll container is fine. The node lives in the always-mounted layout, which is why it survives in-app navigation and only a reload clears it. A broken vs. working DOM snapshot differs only in the number of children in the transition grid cell (2 vs 1) and which element sits under the cursor; the scroll container, its CSS, and the whole flex/height chain are identical. ## Fix Set `pointer-events: none` on exiting pages so a stale exit node can't capture input from the active page. Tested by reproducing the stale-node-on-top state and confirming `pointer-events: none` lets wheel/scroll reach the live page across the content area, while the entering page stays interactive. Review in cubic --- .../ui/layout/page/components/MainAppLayoutWithSidePanel.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx b/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx index 4b28d9f212..4f23343d79 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx @@ -61,7 +61,10 @@ const MainAppLayoutOutlet = () => { key={routeSection} initial={{ opacity: 0, y: 4 }} animate={{ opacity: 1, y: 0 }} - exit={{ opacity: 0, y: -4 }} + // The outgoing page shares this grid cell with the incoming one, so it + // must not capture pointer/scroll events — including when AnimatePresence + // leaves a stale exit node mounted on top of the active page. + exit={{ opacity: 0, y: -4, pointerEvents: 'none' }} transition={{ duration: APP_TO_SETTINGS_TRANSITION_DURATION_IN_SECONDS, ease: 'easeInOut',