← Commit history

2.4.0: measure the gutter against the panel's containing block, not documentElement - the old row failed every app-shell dashboard by one scrollbar width

John Lauer ·ad62370c1c ·1mo ago ·parent e967edb
3 files changed +40−13
assets/module/newbie-panel.js+16−5
@@ -427,12 +427,23 @@      * column while the other three rendered TWO, at an identical pane width. The panel was      * conformant in all four; the PAGE around it was not. The fleet gutter is 16px a side,      * so the panel's border box is the pane minus 32. */-    var vw = document.documentElement.clientWidth;-    var want32 = vw - 32, gotW = Math.round(panel.getBoundingClientRect().width);+    /* Measure against the panel's OWN CONTAINING BLOCK, never documentElement.+     * The first version compared to documentElement.clientWidth, which silently assumes the+     * PAGE scrolls. An app-shell dashboard (position:fixed shell, 44px header, scroller on an+     * inner .app-body) has no document scrollbar, so clientWidth is the full pane and the+     * inner scrollbar is charged to content instead: a correctly built app-shell page fails+     * by exactly one scrollbar width, and only pages that happen to scroll at document level+     * pass. Since app-shell is the prescribed structure, the check was failing the apps that+     * followed the skill. The containing block is scrollbar-agnostic and still catches the+     * real defect, which is a page adding its own padding around the mount point.+     * (parts-search, 2026-08-25, from reading the implementation rather than the prose.) */+    var host = (panel.parentElement && panel.parentElement.parentElement) || document.body;+    var hostW = host.clientWidth;+    var want32 = hostW - 32, gotW = Math.round(panel.getBoundingClientRect().width);     var gutterOk = Math.abs(gotW - want32) <= 2;     if (!gutterOk) ok = false;-    rows.push({ what: 'panel gutter', want: want32 + 'px wide (16px a side)',-                got: gotW + 'px (' + Math.round((vw - gotW) / 2) + 'px a side)', pass: gutterOk });+    rows.push({ what: 'panel gutter', want: want32 + 'px wide (16px a side of its container)',+                got: gotW + 'px (' + Math.round((hostW - gotW) / 2) + 'px a side)', pass: gutterOk });     var fam = getComputedStyle(panel.querySelector('.anp-phrase span') || panel).fontFamily;     var famOk = /Satoshi/.test(fam);     if (!famOk) ok = false;@@ -440,6 +451,6 @@     return { ok: ok, rows: rows };   } -  root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, version: '2.3.0',+  root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, version: '2.4.0',                            TWO_COL_MIN: TWO_COL_MIN, PHRASE_BUDGET: PHRASE_BUDGET }; })(typeof window !== 'undefined' ? window : this);
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-ui-design",-  "version": "2.3.3",+  "version": "2.4.0",   "type": "skill",   "description": "The Adom UI Design System plus a bundle of focused UI skills (tooltips, floating-windows, toasts, responsive-ui, app-shell, ai-driven-apps, clickable-paths, dark-default, input-feedback, settings-layout). One install drops the whole set of skill files onto your container, plus shared icon assets (the settings cog) so every Adom app draws the same glyph.",   "dependencies": {},
skills/newbie-panel/SKILL.md+23−7
@@ -316,15 +316,31 @@ genuinely need to react to it, and then branch on `source`. **Mount the panel in a container with exactly 16px of horizontal padding, and nothing else.**  ```-panel border-box  ==  document.documentElement.clientWidth - 32     (+/- 2)+panel border-box  ==  <the container you mount into>.clientWidth - 32     (+/- 2) ``` -**⛔ NEVER REASON FROM `innerWidth`, AND NEVER QUOTE AN ABSOLUTE TARGET.** On a scrolling-dashboard `innerWidth` includes the scrollbar and `clientWidth` does not. At John's pane those-are **744 and 734**, so the target there is **702, not 712**. I told three apps "712" by doing-the arithmetic against the wrong one; kicad read the implementation before acting and caught-it, and had it coded to my number it would have shipped a 10px miss and failed the very row it-was trying to pass. Compute from `clientWidth` at runtime, every time.+**⛔ NEVER QUOTE AN ABSOLUTE TARGET, AND NEVER MEASURE AGAINST THE DOCUMENT.** Two versions of+this rule were wrong before this one, both because of scrollbars:++1. I told three apps "712 at his pane" by reasoning from `innerWidth` (744) when the check+   read `clientWidth` (734, excluding the document scrollbar), so the real target was 702.+   kicad read the implementation before acting and caught it; had it coded to my number it+   would have shipped a 10px miss and failed the very row it was trying to pass.+2. Measuring against `documentElement` at all was the deeper mistake. **It silently assumes+   the PAGE scrolls.** An `app-shell` dashboard, which is the structure this pack prescribes,+   is a `position:fixed` shell with a 44px header and the scroller on an inner `.app-body`, so+   there is no document scrollbar: `clientWidth` is the full pane and the inner scrollbar is+   charged to content instead. **A correctly built app-shell page failed by exactly one+   scrollbar width, and only pages that happened to scroll at document level passed.** The+   check was failing the apps that followed the skill. Found by parts-search, from reading the+   implementation rather than the prose, and reproduced before the fix: an app-shell test page+   measured `documentElement` 1673 against a host of 1658, a 15px miss on a panel with a+   perfect 16px gutter.++So the yardstick is **the panel's own containing block**, which is scrollbar-agnostic, works+identically for both structures, and still catches the real defect: a page adding its own+padding around the mount point. Negative-tested on an app-shell page with an inner scroller:+16px passes, 50px red, 0px red, restored green.  This is not cosmetic. Four conformant apps measured at the SAME 744px pane reported panel widths of **734, 735, 702 and 694**, because each page put a different amount of its own