From 7afa1adef020154f48038279f876cbf9d063b4e8 Mon Sep 17 00:00:00 2001 From: paoloredis Date: Tue, 22 Sep 2026 10:58:38 +0200 Subject: [PATCH] Write the widget-era html width without viewport units The Qualified widget's docking.js rewrites every vw/dvw unit in the page's stylesheets into calc(1 * var(--qdvw)), where --qdvw is 100dvw minus the scrollbar width N it measured. The full-width rule from DOC-7044 used width: 100dvw on html, so after the rewrite html was N pixels short of the window. Two visible effects for every visitor with classic scrollbars: a white N-pixel band at the window edge with body's scrollbar sitting inboard of it, and, because the swap from root scroller to body scroller happens ~0.5-2s after every page load, the centered layout shifting ~8px left on every navigation ('the scroll bar disappears and reappears with a jump'). Express full width as the sum of the widget's own variables, var(--qdvw) + var(--q-docked-viewport-offset), which is exactly 100dvw and contains nothing the rewriter touches. Verified on the live pages in Chrome and Firefox at 1200px and 2200px windows: html and body span the full window, body's scrollbar lands precisely where the root scrollbar was and is draggable there, and main, sidebar and heading keep the same pixel positions across the widget takeover. Docked-panel geometry is untouched (rule is scoped to :not([q-docked])). Co-Authored-By: Claude Fable 5.1 --- assets/css/index.css | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index 3998ef92d2..721eea8909 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -1879,11 +1879,19 @@ a[href*="#no-click"], img[src*="#no-click"] { /* While the chat is closed, neutralize the width/margin compensation on both elements so body fills the window exactly and its scrollbar sits at the window edge, unclipped. While the panel is docked open (q-docked) the - widget's own geometry must win, so none of this applies then. */ + widget's own geometry must win, so none of this applies then. + + Full width is written with the widget's own variables rather than 100dvw: + its docking.js rewrites every vw/dvw unit in the page's stylesheets into + calc(1 * var(--qdvw)), and --qdvw is 100dvw minus N. With 100dvw here, + html ended up N short of the window, body's scrollbar appeared N inboard + of where the root scrollbar had been, and the centered layout shifted + ~8px on every page load. --qdvw plus the widget's viewport offset is + exactly 100dvw, and contains nothing the rewriter touches. */ html[q-docked-target]:not([q-docked]) { - width: 100dvw !important; - min-width: 100dvw !important; - max-width: 100dvw !important; + width: calc(var(--qdvw) + var(--q-docked-viewport-offset)) !important; + min-width: calc(var(--qdvw) + var(--q-docked-viewport-offset)) !important; + max-width: calc(var(--qdvw) + var(--q-docked-viewport-offset)) !important; margin-right: 0 !important; }