[object Object]

← back to Linenwallpaper

front-page-steward: ux-primitives v1.1.1 — Cody slow-load fix (empty-state retracts on late products; never strands over a populated grid) TK-12031

6d57cb0cee9ab29af55c34dc3f0e196496c4eb47 · 2026-09-22 16:45:46 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 6d57cb0cee9ab29af55c34dc3f0e196496c4eb47
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 22 16:45:46 2026 -0700

    front-page-steward: ux-primitives v1.1.1 — Cody slow-load fix (empty-state retracts on late products; never strands over a populated grid) TK-12031
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/index.html               |  4 ++--
 public/ux-primitives.bundle.css |  2 +-
 public/ux-primitives.bundle.js  | 29 ++++++++++++++++++++++-------
 3 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/public/index.html b/public/index.html
index 1d31961..2b53ea9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -279,7 +279,7 @@ html.hf-sample  [data-f="sample"]  { display:none !important }
 <!-- noscript fallback is added by set-fb-pixel.sh on flip (omitted here to avoid unflipped img phone-home) -->
 <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1431180262113856<!-- End Meta Pixel -->ev=PageView<!-- End Meta Pixel -->noscript=1"/></noscript>
 <!-- End Meta Pixel -->
-  <!-- ux-primitives.bundle v1.1.0#aeb58948a264 (front-page-steward TK-12031) -->
+  <!-- ux-primitives.bundle v1.1.1#cf1a82202107 (front-page-steward TK-12031) -->
   <link rel="stylesheet" href="ux-primitives.bundle.css">
   <script src="ux-primitives.bundle.js" defer></script>
 </head>
@@ -632,7 +632,7 @@ html.hf-sample  [data-f="sample"]  { display:none !important }
     <button data-sort-col="price">Price <span class="arr"></span></button>
   </div>
   <div id="rails" style="display:none"></div>
-  <div class="grid" id="grid" data-ux-grid data-ux-grid-skeleton="card" data-ux-grid-count="8" data-ux-grid data-ux-grid-skeleton="card" data-ux-grid-count="8"></div>
+  <div class="grid" id="grid" data-ux-grid data-ux-grid-skeleton="card" data-ux-grid-count="8" data-ux-grid data-ux-grid-skeleton="card" data-ux-grid-count="8" data-ux-grid data-ux-grid-skeleton="card" data-ux-grid-count="8"></div>
   <div class="loading" id="loading">Loading more…</div>
   <div class="sentinel" id="sentinel"></div>
 </section>
diff --git a/public/ux-primitives.bundle.css b/public/ux-primitives.bundle.css
index f321f17..4c7a844 100644
--- a/public/ux-primitives.bundle.css
+++ b/public/ux-primitives.bundle.css
@@ -1,4 +1,4 @@
-/* ux-primitives.bundle.css — canonical Bundle-B UX primitives (generated 2026-09-22T22:59:00Z)
+/* ux-primitives.bundle.css — canonical Bundle-B UX primitives (generated 2026-09-22T23:33:51Z)
    Shared ux-primitives.css tokens (md5 614018b) included ONCE, then each
    primitive's own CSS. All selectors namespaced .ux-* / .ux-bento* / .ux-modal*
    so nothing collides with corner-nav (cnav-*), hero-4grid, or site styles.
diff --git a/public/ux-primitives.bundle.js b/public/ux-primitives.bundle.js
index 9d9d0ec..6924a03 100644
--- a/public/ux-primitives.bundle.js
+++ b/public/ux-primitives.bundle.js
@@ -1,4 +1,4 @@
-/* ux-primitives.bundle.js — canonical Bundle-B UX primitives (generated 2026-09-22T22:59:00Z)
+/* ux-primitives.bundle.js — canonical Bundle-B UX primitives (generated 2026-09-22T23:33:51Z)
    7 zero-dependency IIFE primitives (window globals Toast, Skeleton, EmptyState,
    Copy, CommandPalette, ModalRig, Bento) + ONE idempotent declarative auto-init
    (UXPrimitives.init). No-ops on a page using none of them. Globals are distinct
@@ -1210,7 +1210,10 @@
     var wantEmpty = (opts.empty !== false) && (c.getAttribute('data-ux-grid-empty') !== 'off');
     var toAttr = parseInt(c.getAttribute('data-ux-grid-timeout') || '0', 10);
     var skelTimeout = (typeof global.__uxGridTimeout === 'number' ? global.__uxGridTimeout : 0) || opts.skeletonTimeout || toAttr || 8000;
-    var hardCap = opts.hardCap || Math.max(15000, skelTimeout + 6000);
+    // hardCap is a LEAK GUARD only — it may disconnect the observer ONLY when no empty-state
+    // is being displayed (see below). It must NEVER tear down the observer while an empty-state
+    // banner is up, or a late-arriving product would be stranded UNDER a permanent "No results".
+    var hardCap = (typeof global.__uxGridHardCap === 'number' ? global.__uxGridHardCap : 0) || opts.hardCap || Math.max(15000, skelTimeout + 6000);
     var emptyOpts = {
       variant: 'no-results',
       title: opts.emptyTitle || c.getAttribute('data-ux-grid-empty-title') || undefined,
@@ -1230,19 +1233,31 @@
       if (c.removeAttribute) c.removeAttribute('aria-busy');
       c.__uxGridDone = true;
     }
+    // The observer is the SINGLE source of truth for "products arrived" and lives until they
+    // do. Products appearing at ANY time (before the skeleton timeout, during an empty-state
+    // banner, or long after hardCap) always clear skeleton + empty-state and finish. This is
+    // what makes empty-state safe: it can NEVER remain stranded over a populated grid.
     try {
       obs = new MutationObserver(function () {
         if (realCount(c) > 0) { clearSkel(c); clearEmpty(c); finish(); }
       });
       obs.observe(c, { childList: true });
-    } catch (e) { /* no MutationObserver → timeout path still clears */ }
+    } catch (e) { /* no MutationObserver → timeout path still clears the skeleton */ }
 
     tSkel = setTimeout(function () {
-      clearSkel(c);
+      clearSkel(c);                                  // never leave a skeleton stuck
       if (realCount(c) === 0 && wantEmpty && !emptyShown) { showEmpty(c, emptyOpts); emptyShown = true; }
-      // keep observing until a late product arrives (removes empty) or hardCap
+      // Do NOT finish here — the observer stays alive so a late product (even a very slow API
+      // or a facet re-query that later returns rows) always retracts the empty-state.
     }, skelTimeout);
-    tCap = setTimeout(finish, hardCap);
+
+    // Leak guard: disconnect ONLY if we never showed an empty-state (nothing to strand) and the
+    // grid is still empty. If an empty-state IS up, we deliberately keep watching — a stranded
+    // "No results" over real products is a worse failure than one long-lived idle observer on a
+    // grid that receives no mutations (which costs nothing and is GC'd with the element).
+    tCap = setTimeout(function () {
+      if (!emptyShown && realCount(c) === 0) finish();
+    }, hardCap);
     return { finish: finish };
   }
 
@@ -1337,6 +1352,6 @@
     return true;
   }
 
-  global.UXPrimitives = { init: initAll, version: '1.1.0' };
+  global.UXPrimitives = { init: initAll, version: '1.1.1' };
   ready(function () { if (global.__uxPrimitivesAutoInit === false) return; try { initAll(doc); } catch (e) { console.warn('[ux-primitives] auto-init', e); } });
 })(typeof window !== 'undefined' ? window : this);

← f16cc5c front-page-steward: ux-primitives v1.1.0 lifecycle grid acti  ·  back to Linenwallpaper  ·  TK-12031: bump ux-primitives bundle to canonical v1.1.1#7d74 73ca0a2 →