[object Object]

← back to Thedesignerlibrary

contrarian fixes: hex format guard on style attr, SSR cards stay until first API page, tighter View-hotspot tap zone

122f00afee65927dfb78e247a71b1cea733a6d35 · 2026-08-07 08:21:53 -0700 · Steve Abrams

Files touched

Diff

commit 122f00afee65927dfb78e247a71b1cea733a6d35
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Aug 7 08:21:53 2026 -0700

    contrarian fixes: hex format guard on style attr, SSR cards stay until first API page, tighter View-hotspot tap zone
---
 deploy/nginx-thedesignerlibrary.conf |   4 ++++
 public/bookshelf.js                  |  10 +++++-----
 public/browse.html                   |   9 +++++----
 public/og-image.jpg                  | Bin 51797 -> 51727 bytes
 4 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/deploy/nginx-thedesignerlibrary.conf b/deploy/nginx-thedesignerlibrary.conf
index ae5f28f..d0a65c9 100644
--- a/deploy/nginx-thedesignerlibrary.conf
+++ b/deploy/nginx-thedesignerlibrary.conf
@@ -8,6 +8,10 @@ server {
     ssl_certificate     /etc/letsencrypt/live/thedesignerlibrary.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/thedesignerlibrary.com/privkey.pem;
 
+    add_header Strict-Transport-Security "max-age=31536000" always;
+    add_header X-Content-Type-Options nosniff always;
+    add_header Referrer-Policy strict-origin-when-cross-origin always;
+
     location / {
         proxy_pass http://127.0.0.1:9807;
         proxy_set_header Host $host;
diff --git a/public/bookshelf.js b/public/bookshelf.js
index c440f55..52fe2e5 100644
--- a/public/bookshelf.js
+++ b/public/bookshelf.js
@@ -336,15 +336,15 @@ export class BookshelfCatalog {
       const hits = this.raycaster.intersectObjects(this._bookGroups.map(g => g.userData.body), false);
       if (hits.length) { this.openBook(hits[0].object.userData.index); return; }
     } else if (this.state === 'reading') {
-      // right page: tap flips forward; ONLY the "View ›" strip (bottom-left of
-      // the page texture) opens the store link — otherwise a touch user can
-      // never see page 2 because every page has an href. left page: back.
-      // backdrop: close.
+      // right page: tap flips forward; ONLY the "View ›" hotspot — the bottom
+      // ~14% of the page, left third, where the label is drawn — opens the
+      // store link. Otherwise a touch user can never see page 2 because every
+      // page has an href. left page: back. backdrop: close.
       const hits = this.raycaster.intersectObjects([this._leftPage, this._rightPage], false);
       if (hits.length) {
         const page = this.books[this.activeIndex].pages?.[this.pageIndex];
         const uv = hits[0].uv;
-        const onViewStrip = uv && uv.y < 0.14 && uv.x < 0.5;
+        const onViewStrip = uv && uv.y < 0.14 && uv.x < 0.38;
         if (hits[0].object === this._rightPage && page && page.href && onViewStrip) {
           const ok = this.opts.onLinkClick ? this.opts.onLinkClick(page, this.books[this.activeIndex]) : true;
           if (ok !== false) window.open(page.href, '_blank', 'noopener,noreferrer');
diff --git a/public/browse.html b/public/browse.html
index 03dd7b9..d4ff7d7 100644
--- a/public/browse.html
+++ b/public/browse.html
@@ -132,13 +132,17 @@ async function loadPage() {
       d.innerHTML = `
         <img loading="lazy" src="${esc(pr.image)}" alt="${esc(pr.title)}">
         <div class="meta">
-          <a class="t" href="${esc(pr.url)}" target="_blank" rel="nofollow sponsored noopener">${pr.hex ? `<span class="dot" style="background:${esc(pr.hex)}" title="more in this hue"></span>` : ''}${esc(pr.title)}</a>
+          <a class="t" href="${esc(pr.url)}" target="_blank" rel="nofollow sponsored noopener">${pr.hex ? `<span class="dot" style="background:${/^#[0-9a-fA-F]{3,6}$/.test(pr.hex) ? pr.hex : 'transparent'}" title="more in this hue"></span>` : ''}${esc(pr.title)}</a>
           <div class="v"><a href="/browse?vendor=${encodeURIComponent(pr.vendor)}">${esc(pr.vendor)}</a>${pr.collection ? ` · <a href="/browse?vendor=${encodeURIComponent(pr.vendor)}&collection=${encodeURIComponent(pr.collection)}">${esc(pr.collection)}</a>` : ''}</div>
         </div>`;
       d.querySelector('img').onclick = () => window.open(pr.url, '_blank', 'noopener');
       if (pr.hue) d.querySelector('.dot')?.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); state.hue = pr.hue; reset(); });
       frag.appendChild(d);
     }
+    // page 1 replaces the server-rendered crawler cards — swapping here (not
+    // at script start) keeps the SSR grid visible until real data arrives,
+    // instead of wiping it and showing a blank grid for the fetch roundtrip
+    if (state.page === 1) $('#grid').innerHTML = '';
     $('#grid').appendChild(frag);
     state.page++;
     state.done = (state.page - 1) * 48 >= data.total;
@@ -163,9 +167,6 @@ $('#density').oninput = (e) => { localStorage.tdl_cols = e.target.value; documen
 window.addEventListener('scroll', () => {
   if (window.innerHeight + window.scrollY > document.body.offsetHeight - 900) loadPage();
 });
-// the server SSRs the first 48 cards for crawlers — clear them before the
-// client render so the same products don't appear twice
-$('#grid').innerHTML = '';
 loadFacets().then(() => { syncUrl(); loadPage(); });
 </script>
 <script async src="https://www.googletagmanager.com/gtag/js?id=G-LPY7H5RZ20"></script>
diff --git a/public/og-image.jpg b/public/og-image.jpg
index a4e2e2f..659e7b7 100644
Binary files a/public/og-image.jpg and b/public/og-image.jpg differ

← 5808dab agent-review refinements: GPU disposal + tap-to-flip + XSS e  ·  back to Thedesignerlibrary  ·  chore: version-up v1.1.0 (agent-refinement session close) b3943a0 →