← back to Dw Pairs Well
Design Coordinate v7: 6+6 cards, rename AI→Auto Generated (no AI wording), compact CTA/headings; fix /api/ai-coordinates to honor ?limit
2d70921d77d3ad3ebe9940b823fadc2ad3c966af · 2026-06-26 10:31:57 -0700 · Steve
Files touched
M deploy/shopify/design-coordinate.liquidM public/preview.htmlM server.js
Diff
commit 2d70921d77d3ad3ebe9940b823fadc2ad3c966af
Author: Steve <steve@designerwallcoverings.com>
Date: Fri Jun 26 10:31:57 2026 -0700
Design Coordinate v7: 6+6 cards, rename AI→Auto Generated (no AI wording), compact CTA/headings; fix /api/ai-coordinates to honor ?limit
---
deploy/shopify/design-coordinate.liquid | 45 +++++++++++++++++----------------
public/preview.html | 32 +++++++++++------------
server.js | 6 ++++-
3 files changed, 44 insertions(+), 39 deletions(-)
diff --git a/deploy/shopify/design-coordinate.liquid b/deploy/shopify/design-coordinate.liquid
index e6f1e6d..91eec81 100644
--- a/deploy/shopify/design-coordinate.liquid
+++ b/deploy/shopify/design-coordinate.liquid
@@ -1,10 +1,11 @@
{%- comment -%}
- Design Coordinate v6 — Steve's spec:
- • Catalog grid: 24 stripe/plaid/check coordinates that use the source palette
- • Density slider 3-12 cols (default 8), localStorage-persisted
- • AI Generated Coordinates: 24 SVG-poster placeholders seeded from palette
+ Design Coordinate v7 — Steve's spec:
+ • Catalog grid: 6 stripe/plaid/check coordinates that use the source palette ("active patterns")
+ • Density slider 3-12 cols (default 6), localStorage-persisted
+ • Auto Generated: 6 SVG-poster placeholders seeded from palette (no "AI" wording, customer-facing)
• Vendor picker: SW / DE / BM / F&B (default on) + Behr / PPG / RAL (opt-in)
• Each card shows palette dots + selected vendors' paint chips
+ • Compact: smaller CTA pill + headings (v7 "debig"); place AFTER the product image in the theme.
Service: https://pairs.designerwallcoverings.com (commit history at ~/Projects/dw-pairs-well)
CSS namespace kept as .pairs-well__* — only the button TEXT is "Design Coordinate".
{%- endcomment -%}
@@ -43,8 +44,8 @@
<h3>Stripes & Plaids from our catalog <span class="count" id="pairs-well-catalog-count"></span></h3>
<div class="pairs-well__controls">
<label for="pairs-well-cols-catalog">Columns</label>
- <input id="pairs-well-cols-catalog" type="range" min="3" max="12" value="8" step="1">
- <span class="pairs-well__cols-val" id="pairs-well-cols-catalog-val">8</span>
+ <input id="pairs-well-cols-catalog" type="range" min="3" max="12" value="6" step="1">
+ <span class="pairs-well__cols-val" id="pairs-well-cols-catalog-val">6</span>
</div>
</div>
<div id="pairs-well-grid" class="pairs-well__grid"></div>
@@ -52,11 +53,11 @@
<div class="pairs-well__ai" id="pairs-well-ai" hidden>
<div class="pairs-well__panel-head">
- <h3>AI Generated Coordinates <span class="ai-badge">Preview · 24 options</span></h3>
+ <h3>Auto Generated <span class="ai-badge">Preview · 6 options</span></h3>
<div class="pairs-well__controls">
<label for="pairs-well-cols-ai">Columns</label>
- <input id="pairs-well-cols-ai" type="range" min="3" max="12" value="8" step="1">
- <span class="pairs-well__cols-val" id="pairs-well-cols-ai-val">8</span>
+ <input id="pairs-well-cols-ai" type="range" min="3" max="12" value="6" step="1">
+ <span class="pairs-well__cols-val" id="pairs-well-cols-ai-val">6</span>
</div>
</div>
<div id="pairs-well-ai-grid" class="pairs-well__grid pairs-well__grid--ai"></div>
@@ -64,13 +65,13 @@
</section>
<style>
- #pairs-well-with { font-family: inherit; --cols-catalog: 8; --cols-ai: 8; }
+ #pairs-well-with { font-family: inherit; --cols-catalog: 6; --cols-ai: 6; }
.pairs-well__cta {
- display: flex; align-items: center; justify-content: space-between;
- width: 100%; padding: 18px 24px; margin: 0;
+ display: inline-flex; align-items: center; justify-content: center; gap: 8px;
+ width: auto; max-width: 100%; padding: 10px 18px; margin: 0;
background: #111; color: #fff;
border: 0; border-radius: 999px;
- font-size: 15px; font-weight: 500; letter-spacing: 0.06em;
+ font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
text-transform: uppercase; cursor: pointer;
transition: background 160ms ease, transform 120ms ease;
}
@@ -102,9 +103,9 @@
.pairs-well__panel-head {
display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
- gap: 12px; margin: 24px 0 12px;
+ gap: 12px; margin: 16px 0 10px;
}
- .pairs-well__panel-head h3 { margin: 0; font: 500 18px/1.2 ui-serif, Georgia, serif; }
+ .pairs-well__panel-head h3 { margin: 0; font: 500 15px/1.2 ui-serif, Georgia, serif; }
.pairs-well__panel-head .count { color: #888; font-size: 12px; margin-left: 6px; font-family: -apple-system, sans-serif; font-weight: 400; }
.pairs-well__panel-head .ai-badge {
display: inline-block; padding: 3px 8px; background: #2a2a2a; color: #fff;
@@ -263,7 +264,7 @@
function renderCard(p, isAi) {
var img = p.image_url ? esc(p.image_url) : '';
- var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? 'AI ' + esc(p.motif) : '');
+ var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? esc(p.motif) : '');
var palette = (p.palette || []).map(function (c) {
var hex = typeof c === 'string' ? c : c.hex;
var sw = c && c.paints && c.paints.sw, de = c && c.paints && c.paints.de;
@@ -288,7 +289,7 @@
+ '<div class="pairs-well__img-wrap">' + (img ? '<img class="pairs-well__img" src="' + img + '" alt="' + esc(p.title) + '" loading="lazy">' : '') + '</div>'
+ '<div class="pairs-well__meta">'
+ '<p class="pairs-well__title">' + esc(p.title || '') + '</p>'
- + '<p class="pairs-well__vendor">' + esc(p.vendor || (isAi ? 'AI-Generated' : '')) + '</p>'
+ + '<p class="pairs-well__vendor">' + esc(p.vendor || (isAi ? 'Auto Generated' : '')) + '</p>'
+ (why ? '<span class="pairs-well__why">' + why + '</span>' : '')
+ (palette ? '<div class="pairs-well__palette">' + palette + '</div>' : '')
+ paintsBlock
@@ -318,12 +319,12 @@
function load() {
if (loaded) return Promise.resolve();
loaded = true;
- renderSkeletons(catalogGrid, 12);
- renderSkeletons(aiGrid, 12);
+ renderSkeletons(catalogGrid, 6);
+ renderSkeletons(aiGrid, 6);
catalogPanel.hidden = false;
aiPanel.hidden = false;
- var pairsPromise = fetch(buildUrl(endpointPairs, 24), { credentials: 'omit' })
+ var pairsPromise = fetch(buildUrl(endpointPairs, 6), { credentials: 'omit' })
.then(function (r) { return r.ok ? r.json() : Promise.reject(new Error('http ' + r.status)); })
.then(function (d) {
if (!d || d.ok === false) throw new Error((d && d.error) || 'bad payload');
@@ -338,14 +339,14 @@
})
.catch(function () { catalogGrid.innerHTML = '<div class="pairs-well__empty">Could not load coordinates right now.</div>'; });
- var aiPromise = fetch(buildUrl(endpointAi, 24), { credentials: 'omit' })
+ var aiPromise = fetch(buildUrl(endpointAi, 6), { credentials: 'omit' })
.then(function (r) { return r.ok ? r.json() : Promise.reject(new Error('http ' + r.status)); })
.then(function (d) {
if (!d || d.ok === false) throw new Error('bad payload');
cachedAi = d.items || [];
renderGrid(aiGrid, cachedAi, true);
})
- .catch(function () { aiGrid.innerHTML = '<div class="pairs-well__empty">AI generator not ready.</div>'; });
+ .catch(function () { aiGrid.innerHTML = '<div class="pairs-well__empty">Auto Generated coordinates not ready.</div>'; });
return Promise.all([pairsPromise, aiPromise]);
}
diff --git a/public/preview.html b/public/preview.html
index 5b56ed0..98b9081 100644
--- a/public/preview.html
+++ b/public/preview.html
@@ -7,7 +7,7 @@
<style>
:root {
--gutter: 40px; --text: #1a1a1a; --muted: #777; --line: #e7e7e7;
- --cols: 8; /* density slider sets this */
+ --cols: 6; /* density slider sets this */
}
* { box-sizing: border-box; }
body { margin: 0; color: var(--text); background: #fafafa;
@@ -65,7 +65,7 @@
gap: 12px; margin-bottom: 14px;
}
.dc-section h2 {
- margin: 0; font: 500 22px/1.2 ui-serif, Georgia, serif;
+ margin: 0; font: 500 16px/1.2 ui-serif, Georgia, serif;
}
.dc-section h2 .count { color: var(--muted); font-size: 13px; margin-left: 6px; }
.dc-controls {
@@ -79,10 +79,10 @@
}
.pairs-well__cta {
- display: flex; align-items: center; justify-content: space-between;
- width: 100%; padding: 18px 24px; margin: 0;
+ display: inline-flex; align-items: center; justify-content: center; gap: 8px;
+ width: auto; max-width: 100%; padding: 10px 18px; margin: 0;
background: #111; color: #fff; border: 0; border-radius: 999px;
- font-size: 15px; font-weight: 500; letter-spacing: 0.06em;
+ font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
text-transform: uppercase; cursor: pointer;
transition: background 160ms ease, transform 120ms ease;
}
@@ -200,8 +200,8 @@
<h2>Stripes & Plaids from our catalog <span class="count" id="catalog-count"></span></h2>
<div class="dc-controls">
<label for="catalog-cols">Columns</label>
- <input id="catalog-cols" type="range" min="3" max="12" value="8" step="1">
- <span class="colcount" id="catalog-cols-val">8</span>
+ <input id="catalog-cols" type="range" min="3" max="12" value="6" step="1">
+ <span class="colcount" id="catalog-cols-val">6</span>
</div>
</div>
<div id="catalog-grid" class="pairs-well__grid" hidden></div>
@@ -210,11 +210,11 @@
<!-- AI-GENERATED COORDINATES -->
<section id="ai-coords-section" class="dc-section ai-coords" hidden>
<div class="dc-header">
- <h2>AI Generated Coordinates <span class="ai-badge">Preview · 24 options</span></h2>
+ <h2>Auto Generated <span class="ai-badge">Preview · 6 options</span></h2>
<div class="dc-controls">
<label for="ai-cols">Columns</label>
- <input id="ai-cols" type="range" min="3" max="12" value="8" step="1">
- <span class="colcount" id="ai-cols-val">8</span>
+ <input id="ai-cols" type="range" min="3" max="12" value="6" step="1">
+ <span class="colcount" id="ai-cols-val">6</span>
</div>
</div>
<div id="ai-grid" class="pairs-well__grid"></div>
@@ -323,7 +323,7 @@
function renderCard(p, opts) {
opts = opts || {};
var img = p.image_url ? esc(p.image_url) : '';
- var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? 'AI ' + esc(p.motif) : '');
+ var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? esc(p.motif) : '');
var palette = (p.palette || []).map(function (c) {
var hex = typeof c === 'string' ? c : c.hex;
var sw = c && c.paints && c.paints.sw, de = c && c.paints && c.paints.de;
@@ -392,7 +392,7 @@
var qs = buildQuery(key);
try {
- var r = await fetch(endpointPairs + '?' + qs + '&limit=24');
+ var r = await fetch(endpointPairs + '?' + qs + '&limit=6');
if (!r.ok) throw new Error('http ' + r.status);
var d = await r.json();
if (d && d.ok === false) throw new Error(d.error || 'bad payload');
@@ -416,15 +416,15 @@
}
// Kick off AI generation in parallel — show its section as soon as it loads
- showSkeletons(aiGrid, 24);
+ showSkeletons(aiGrid, 6);
aiSection.hidden = false;
try {
- var ar = await fetch(endpointAi + '?' + qs + '&limit=24');
+ var ar = await fetch(endpointAi + '?' + qs + '&limit=6');
if (!ar.ok) throw new Error('http ' + ar.status);
var ad = await ar.json();
if (ad && ad.ok === false) throw new Error(ad.error || 'bad payload');
- if (ad && ad.items) renderGrid(aiGrid, ad.items, { aiBadge: 'AI-Generated' });
- else aiGrid.innerHTML = '<div class="pairs-well__empty">AI generator not ready yet.</div>';
+ if (ad && ad.items) renderGrid(aiGrid, ad.items, { aiBadge: 'Auto Generated' });
+ else aiGrid.innerHTML = '<div class="pairs-well__empty">Auto Generated coordinates not ready yet.</div>';
} catch (e) {
console.error('[preview] ai fetch failed:', e, 'url=', endpointAi + '?' + qs);
aiGrid.innerHTML = '<div class="pairs-well__empty">AI fetch failed: ' + esc(e && e.message ? e.message : 'unknown') + ' (key: ' + esc(key) + ')</div>';
diff --git a/server.js b/server.js
index af03800..268b113 100644
--- a/server.js
+++ b/server.js
@@ -946,6 +946,10 @@ app.get('/api/ai-coordinates', async (req, res) => {
return res.status(400).json({ ok: false, error: 'pass dw_sku, handle, or palette' });
}
const tile_in = Math.max(2, Math.min(60, parseFloat(req.query.tile_in) || 24));
+ // Honor ?limit= the same way /api/pairs does (default 6, hard cap 24) so the
+ // Auto Generated grid renders exactly what the caller asks for — without this,
+ // generateCoordinates() always returned its full set and the client showed 24.
+ const requestedK = Math.max(1, Math.min(24, parseInt(req.query.limit, 10) || 6));
let source, palette;
if (paletteParam.length) {
@@ -960,7 +964,7 @@ app.get('/api/ai-coordinates', async (req, res) => {
}
if (!palette.length) return res.json({ ok: true, source: { dw_sku: source.dw_sku, handle: source.handle }, items: [] });
- const items = generateCoordinates({ palette, tile_in });
+ const items = generateCoordinates({ palette, tile_in }).slice(0, requestedK);
res.set('Cache-Control', 'public, max-age=300');
res.json({
← adf33a8 auto-save: 2026-06-26T10:14:48 (5 files) — tools/pattern-id-
·
back to Dw Pairs Well
·
collection-grouped feed: emit color/style facets (only popul fc5135e →