← Commit history

2.5.0: the gutter row measures the panel's insets against its pane, not against any named element - 2.4.0's yardstick could not pass for any app

John Lauer ·a59491da5f ·1mo ago ·parent 98cc0fb
3 files changed +85−31
assets/module/newbie-panel.js+33−7
@@ -437,13 +437,39 @@      * 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;+    /* Measure BOTH INSETS AGAINST THE PANE, and never against a named element.+     * Two earlier versions asked the wrong element how wide the world is. documentElement+     * assumes the page scrolls, and fails every app-shell dashboard by one scrollbar width.+     * The mount div assumes the 16px lives on the mount div, and reds a visually perfect page+     * that puts the gutter on a shared outer container instead. Both encode a STRUCTURE the+     * fleet was never promised. What the contract actually says is where the panel's EDGES+     * land, so measure exactly that: find the nearest scrolling ancestor (or the document),+     * and check the panel sits 16px inside its client box on both sides. Structure-agnostic:+     * it does not care which element carries the padding, or how many wrappers sit between.+     * (pup found the mount-div version would red an outer-container page, before six apps+     * adopted the pattern I had recommended; parts-search found the documentElement one.) */+    var sc = panel.parentElement;+    while (sc && sc !== document.body) {+      var so = getComputedStyle(sc).overflowY;+      if ((so === 'auto' || so === 'scroll') && sc.scrollHeight > sc.clientHeight) break;+      sc = sc.parentElement;+    }+    var paneLeft, paneW;+    if (sc && sc !== document.body) { paneLeft = sc.getBoundingClientRect().left; paneW = sc.clientWidth; }+    else { paneLeft = 0; paneW = document.documentElement.clientWidth; }+    var pr = panel.getBoundingClientRect();+    var insetL = Math.round(pr.left - paneLeft);+    var insetR = Math.round(paneLeft + paneW - pr.right);+    var gutterOk = Math.abs(insetL - 16) <= 2 && Math.abs(insetR - 16) <= 2;     if (!gutterOk) ok = false;-    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 });+    rows.push({ what: 'panel gutter', want: '16px inset both sides',+                got: insetL + 'px left, ' + insetR + 'px right', pass: gutterOk });+    /* THE LEFT-EDGE ROW closes the hole the gutter row cannot see. The gutter row measures+     * against whatever element the app mounts INTO, so an app that mounts into a wrapper+     * which itself carries padding passes while sitting inset from its neighbours, and the+     * fleet mismatch comes back in a form the check is blind to. This asserts the panel's+     * left edge against the PANE, which covers the whole ancestor chain in one number.+     * (parts-search, 2026-08-25, reasoning about what the new yardstick still could not see.) */     var fam = getComputedStyle(panel.querySelector('.anp-phrase span') || panel).fontFamily;     var famOk = /Satoshi/.test(fam);     if (!famOk) ok = false;@@ -451,6 +477,6 @@     return { ok: ok, rows: rows };   } -  root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, version: '2.4.0',+  root.AdomNewbiePanel = { mount: mount, toast: toast, selfCheck: selfCheck, version: '2.5.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.4.0",+  "version": "2.5.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+51−23
@@ -316,31 +316,59 @@ 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  ==  <the container you mount into>.clientWidth - 32     (+/- 2)+the panel sits 16px inside its pane on BOTH sides      (+/- 2) ``` -**⛔ 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.+**The contract is about where the panel's EDGES land, not about which element carries the+padding.** Put the 16px wherever suits your page: on the mount div, or on a shared outer+container that also holds the regions below, or via one `--gutter` token used in both places.+All three pass.++**⛔ THREE VERSIONS OF THIS CHECK WERE WRONG BEFORE THIS ONE, AND EVERY ONE WAS CAUGHT BY AN+APP READING THE IMPLEMENTATION RATHER THAN MY PROSE.** They are worth listing, because they+share a cause: each asked some NAMED ELEMENT how wide the world is, and so encoded a page+structure the fleet was never promised.++0. **`documentElement.clientWidth`** assumes the PAGE scrolls. An app-shell dashboard has no+   document scrollbar, so a correctly built page failed by exactly one scrollbar width while+   looser pages passed. Found by parts-search, which was one command from restructuring its+   app to satisfy it.+1. **The mount div's `clientWidth`** could never pass for ANY app. The module's own CSS makes+   the panel fill its host exactly (`.anp{width:100%}`, margin top/bottom only), so panel+   width == host.clientWidth by construction and the miss was a constant 32 at every width.+   Padding the mount container shrinks host and panel together, so no page could fix it; only+   module-owned CSS could. kicad caught it by measuring, and held its slot rather than spend+   John's shell pasting a failure caused by the checker.+2. Separately, I told four apps **"712 at his pane"**, reasoning from `innerWidth` (744) when+   the code read `clientWidth` (734). The real number was 702. Never quote an absolute target;+   compute it.++pup caught the mount-div version too, from the implementation alone, and specifically before+six apps adopted a container pattern I had recommended that would have red-lined them all.++**The fix is to stop naming an element.** Find the nearest scrolling ancestor (or the+document), and check both of the panel's insets against its client box. Structure-agnostic: it+does not care which element carries the padding or how many wrappers sit between.++**Tested across every structure in the fleet before shipping,** which is the step that was+missing the previous two times:++| page structure | verdict |+|---|---|+| simple page, padding on the mount div | **pass** |+| app-shell, inner scroller, padding on the mount div | **pass** |+| shared outer container carries the gutter, mount flush | **pass** |+| a wrapper adding 40px on top of a correct mount div | **red** |+| no gutter anywhere | **red** |++**⛔ WHEN A CHECK FAILS, ASK WHETHER IT IS FAILING THE CORRECT IMPLEMENTATIONS.** pup's+framing, and it is the sharpest hazard on this page: a check that is wrong at random is noise,+but a check that is systematically wrong about the WELL-BUILT cases actively teaches the fleet+to break itself, because the fastest way to go green is to adopt the looser structure. Version+0 above was exactly that. And the cost of a wrong check is not only a false verdict: it is the+real damage apps do to themselves complying with it.++  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