← 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
M deploy/nginx-thedesignerlibrary.confM public/bookshelf.jsM public/browse.htmlM public/og-image.jpg
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 →