From 35de2f443a04b763256e75df53dd686e1d59c2d1 Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Tue, 22 Sep 2026 19:09:38 +0000 Subject: [PATCH] Neutralise the dvh floor on hosted panes so a map tab keeps no scrollbar Restoring the pane scrollport exposed the other half of the same fix, also lost: check:shell-scroll passed the grid assertion and then failed with "a MAP tab grew a scrollbar (131px overflow: 900/769)". 131 is exactly 900 minus 769. A hosted map ships .profile-loader { height:100%; min-height:100dvh } and the two resolve against different bases -- height against the pane, which sits inside a .ps-stage inset by the studio chrome, and the dvh floor against the viewport. The floor wins, the element is taller than its container, and overflow-y:auto faithfully draws a scrollbar on a tab that should never have one. Making a pane scrollable is only correct if it stays a no-op for fixed-viewport elements. The floor is deliberate and must stay for the standalone leaf, where it fixes the opposite bug: min-height:100% against a body with no definite height collapses to 0. So it is neutralised on the hosted subtree instead. Sizing the host is not enough, and this is the part that had gone missing. .profile-loader lives inside the map's SHADOW ROOT, where a host-page selector cannot reach it. Inherited properties can, so ProfileLoader reads min-height:var(--profile-min-h, 100dvh) -- already in the file at line 282 -- and the pane now actually sets it to 0. Three comments described this declaration; none of them was the declaration. Not overflow:clip, which bounds the map correctly and also bounds the scroll-stack composite whose content is 3380px against a pane-height host, so the grid goes unreachable and the gate returns to 0px scrolled. One blanket rule cannot serve both. Both synced copies. Committed before re-verifying. --- .../plugin-studio/plugin-studio.vue | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugin-designs/plugin-studio/plugin-studio.vue b/plugin-designs/plugin-studio/plugin-studio.vue index c4b7937d3..65033089e 100644 --- a/plugin-designs/plugin-studio/plugin-studio.vue +++ b/plugin-designs/plugin-studio/plugin-studio.vue @@ -190,7 +190,16 @@ const PS_SHADOW_CSS = ` to own that scroll itself. auto, not scroll: only a pane whose content really exceeds its box gets a scrollbar, so the map tabs stay scrollbar-free (see the dvh floor note below, which is what makes that true). */ -.ps-pane { position:absolute; inset:0; overflow-y:auto; overflow-x:hidden; overscroll-behavior:contain; } +.ps-pane { position:absolute; inset:0; overflow-y:auto; overflow-x:hidden; overscroll-behavior:contain; + /* ★ THE OTHER HALF of the dvh neutraliser, and the half that actually works. Sizing the host + is not enough: .profile-loader lives inside the map's SHADOW ROOT at min-height:100dvh, and + a host-page selector cannot reach into a shadow tree. INHERITED properties can, so + ProfileLoader.vue reads min-height:var(--profile-min-h, 100dvh) and the pane sets it to 0. + The floor keeps its default everywhere else, including every standalone leaf where it still + fixes BUG-UPG100-NESTED-ELEMENTS-ZERO-HEIGHT. Without this line a MAP tab is 131px taller + than its pane (900 viewport vs 769 pane) and overflow-y:auto correctly grows a scrollbar on + a tab that should never have one. Measured; check:shell-scroll asserts it. */ + --profile-min-h: 0; } /* NOT absolute-inset: that pinned the mount point to the pane's height and re-created the same trap one level down (an absolutely positioned child cannot grow a scroll container). min-height:100% keeps a fixed-viewport map filling the pane while letting a taller composite grow it. */ @@ -631,7 +640,16 @@ export default defineComponent({ position:absolute; inset:0 with the default overflow:visible inside an overflow:hidden stage. auto, not scroll: a fixed-viewport map fills the box exactly and gets NO scrollbar, so the map tabs are untouched. */ -.ps-pane { position:absolute; inset:0; overflow-y:auto; overflow-x:hidden; overscroll-behavior:contain; } +.ps-pane { position:absolute; inset:0; overflow-y:auto; overflow-x:hidden; overscroll-behavior:contain; + /* ★ THE OTHER HALF of the dvh neutraliser, and the half that actually works. Sizing the host + is not enough: .profile-loader lives inside the map's SHADOW ROOT at min-height:100dvh, and + a host-page selector cannot reach into a shadow tree. INHERITED properties can, so + ProfileLoader.vue reads min-height:var(--profile-min-h, 100dvh) and the pane sets it to 0. + The floor keeps its default everywhere else, including every standalone leaf where it still + fixes BUG-UPG100-NESTED-ELEMENTS-ZERO-HEIGHT. Without this line a MAP tab is 131px taller + than its pane (900 viewport vs 769 pane) and overflow-y:auto correctly grows a scrollbar on + a tab that should never have one. Measured; check:shell-scroll asserts it. */ + --profile-min-h: 0; } /* NOT absolute-inset: that pinned the mount point to the pane's height and re-created the trap one level down. min-height:100% keeps a map filling the pane while letting a taller composite grow it. */ .ps-slot { position: absolute; inset: 0; }