← back to Designer Wallcoverings
color-palette: density-slider push script + pending-approval memo; retire stale 20-cap memo+script (Steve 2026-07-09d)
dc01631b07daf730661734fcad788bff40c56f0c · 2026-07-09 14:07:25 -0700 · Steve
Files touched
D pending-approval/color-palette-20dot-cap-2026-07-09.mdA pending-approval/color-palette-density-slider-2026-07-09.mdR050 shopify/push-color-palette-20dot-cap-to-LIVE.sh shopify/push-color-palette-density-slider-to-LIVE.sh
Diff
commit dc01631b07daf730661734fcad788bff40c56f0c
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 9 14:07:25 2026 -0700
color-palette: density-slider push script + pending-approval memo; retire stale 20-cap memo+script (Steve 2026-07-09d)
---
.../color-palette-20dot-cap-2026-07-09.md | 52 ------------
.../color-palette-density-slider-2026-07-09.md | 97 ++++++++++++++++++++++
...> push-color-palette-density-slider-to-LIVE.sh} | 38 ++++++---
3 files changed, 123 insertions(+), 64 deletions(-)
diff --git a/pending-approval/color-palette-20dot-cap-2026-07-09.md b/pending-approval/color-palette-20dot-cap-2026-07-09.md
deleted file mode 100644
index 5dfa6323..00000000
--- a/pending-approval/color-palette-20dot-cap-2026-07-09.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# PENDING APPROVAL — PDP color palette: cap total dots at 20 (customer-facing, LIVE theme)
-
-**Date:** 2026-07-09
-**Owner:** vp-dw-commerce
-**Gate reason:** customer-facing change to the LIVE Shopify theme (#144076931123, 5.9.1)
-**Status:** BUILT + VERIFIED + COMMITTED locally. NOT pushed live. Awaiting Steve's go.
-
-## Steve's ask (verbatim)
-> "only show a total of 20 dots."
-
-## What changed
-File: `snippets/color-palette.liquid` (the PDP "Colors In This Pattern" swatch spectrum).
-
-- Added named constant **`MAX_SWATCHES = 20`** — a HARD cap on the TOTAL rendered dots (base + interpolated).
-- Added **`buildSteps(n)`**: given the real base-color count, returns how many interpolated in-between dots go in each of the `(n-1)` gaps so that `base + interpolated <= 20`.
-- Replaced the old single `STEPS = 20`-per-gap loop (which produced `base + 20×gaps` dots) with a per-gap step count from `buildSteps`.
-
-## Before → after count
-- **Before:** ~64 dots. Logic was every base color + a FIXED `STEPS=20` interpolated dots per pair → e.g. 4 bases = 4 + 3×20 = 64; up to 148 at the 8-base cap.
-- **After:** **exactly ≤ 20 dots**, always.
-
-## How the 20 are distributed
-Every REAL base color is KEPT (bases are already capped at ≤8, well under 20; if bases alone ever exceeded 20 the 20 most-dominant are kept — list is sorted by % desc). The leftover budget of interpolated dots is spread EVENLY across the gaps — `floor(budget/gaps)` each, with the remainder added +1 to the earliest gaps so the spectrum stays balanced left-to-right (NOT truncated at one end). Verified distribution for every base count:
-
-| bases | steps per gap | total |
-|------:|---------------|------:|
-| 1 | [] | 1 |
-| 2 | [18] | 20 |
-| 3 | [9,8] | 20 |
-| 4 | [6,5,5] | 20 |
-| 5 | [4,4,4,3] | 20 |
-| 6 | [3,3,3,3,2] | 20 |
-| 7 | [3,2,2,2,2,2] | 20 |
-| 8 | [2,2,2,2,2,1,1] | 20 |
-
-Each remaining dot keeps its existing click behavior (the 10% CIELAB color-index grid), hex fill, and the legend + proportion bar still reflect the real base colors. Unchanged.
-
-## Local verification (all $0 local)
-- `node --check` on both extracted `<script>` blocks → **SYNTAX OK**.
-- `shopify theme check` (before live vs after, minimal scaffold): **4 warnings before, 4 after — identical set** (`_dw_sku`/`_dw_hex`/`_dw_style` VariableName + `img_url` DeprecatedFilter). All pre-existing; **0 new offenses** from this change.
-- Headless Chromium render of a real PDP-shaped harness (6-band product image → 6 base colors): **`total_dots: 20` (6 base + 14 interpolated), within_cap = true, js_errors = []**. Clicking a base swatch **un-hid the color-index grid and rendered cards → `click_opens_index: true`.**
-
-## Commit
-- `c7633ec4` on branch `collection-layout-overhaul` (author steve@designerwallcoverings.com) captured the edited `snippets/color-palette.liquid` with `MAX_SWATCHES` + `buildSteps`. Clean vs HEAD. No git remote push.
-
-## EXACT push command for Steve to ship (LIVE, gated)
-```bash
-bash ~/Projects/Designer-Wallcoverings/shopify/push-color-palette-20dot-cap-to-LIVE.sh --yes-live
-```
-The script: resolves the LIVE (role=main) theme, backs up the current live snippet to `shopify/theme-backups/color-palette.LIVE-<id>.<stamp>.bak`, verifies the source contains the cap logic, then PUTs `snippets/color-palette.liquid` via the Asset API (store `designer-laboratory-sandbox.myshopify.com`, API `2024-10`, token `SHOPIFY_THEME_TOKEN`). It **refuses to run without `--yes-live`** (verified). Read-after-write replica lag ~5s before the change shows on a PDP.
-
-Rollback if needed: PUT the `.bak` back via the same Asset API.
diff --git a/pending-approval/color-palette-density-slider-2026-07-09.md b/pending-approval/color-palette-density-slider-2026-07-09.md
new file mode 100644
index 00000000..5e062fa3
--- /dev/null
+++ b/pending-approval/color-palette-density-slider-2026-07-09.md
@@ -0,0 +1,97 @@
+# PENDING APPROVAL — PDP color palette: DENSITY SLIDER (expand/contract dots) — customer-facing, LIVE theme
+
+**Date:** 2026-07-09
+**Owner:** vp-dw-commerce
+**Gate reason:** customer-facing change to the LIVE Shopify theme (#144076931123, 5.9.1)
+**Status:** BUILT + VERIFIED + COMMITTED locally. NOT pushed live. Awaiting Steve's go.
+**Supersedes:** the just-staged hard 20-dot cap (commit c7633ec4). The cap is now the slider's DEFAULT.
+
+## Steve's ask (verbatim)
+> "in fact, allow the user to grid slide the amount of dots that they can see.. expand.. contract"
+
+## What changed
+File: `snippets/color-palette.liquid` (the PDP "Colors In This Pattern" swatch spectrum).
+
+A **density slider** now sits above the dots and lets the user EXPAND/CONTRACT how many dots show.
+Follows Steve's standing density-slider idiom (same as the DW product-grid density sliders).
+
+- **`<input type="range">`** `data-dw-cp-range` controlling the TOTAL visible dot count, with a
+ −/+ affordance and a live "Colors shown" label + numeric readout (`<output>`).
+- The former fixed `MAX_SWATCHES = 20` const → a live `dotTarget` variable driven by the slider.
+ New constants: **`MIN_DOTS = 6`, `MAX_DOTS = 64`, `DEFAULT_DOTS = 20`**.
+- **Live re-render on drag, NO page reload:** the resolved real base-color list is cached once
+ (`_baseList`) at first render; slider changes call **`paintSpectrum()`**, which rebuilds ONLY the
+ dot spectrum from the cached bases at the new target — the product image is never re-fetched or
+ re-extracted. `buildSteps(n, total)` (was `buildSteps(n)`) now takes the current slider value.
+- **Even redistribution (never truncates one end):** every real base color is always kept; the
+ leftover budget of interpolated dots is spread `floor(budget/gaps)` per gap, remainder +1 to the
+ earliest gaps — identical balancing to the 20-cap, now parameterized by the slider value.
+- **Persistence:** value saved to **`localStorage` key `dw_palette_dot_count`** on release/step
+ (palette-specific — does NOT collide with the product-grid density slider). Read on load; if a
+ saved value exists the palette renders that many dots instead of 20.
+- Slider auto-hides for a single-base palette (nothing to interpolate).
+
+**Unchanged / intact:** each dot keeps its hex fill + click → 10% CIELAB color-index grid; the
+legend + proportion bar reflect the real base colors (built once, independent of dot count); the
+CLIP "More Wallcoverings Like This" strip and the family-collection fallback href are untouched.
+
+## Before → after
+- **Before (c7633ec4):** hard cap `MAX_SWATCHES = 20`, always exactly ≤20 dots, not user-adjustable.
+- **After:** user slider, DEFAULT 20 (the cap became the default), range **6 → 64**, live expand/contract,
+ persisted. Distributes N dots the same way (keep bases, evenly fill interpolated to hit N).
+
+## Slider spec
+| Field | Value |
+|---|---|
+| control | `<input type="range">` + −/+ buttons + numeric `<output>` |
+| min / max / default | **6 / 64 / 20** |
+| localStorage key | **`dw_palette_dot_count`** |
+| re-render | `paintSpectrum()` from cached `_baseList` — no image re-extraction, no reload |
+| distribution | keep every real base color; interpolated dots = `buildSteps(nBases, target)` spread evenly across gaps, remainder to earliest gaps (never truncates one end) |
+
+## Local verification (all $0 local)
+- **`node --check`** on both extracted `<script>` blocks → **SYNTAX OK**.
+- **`shopify theme check`** (live vs edited, minimal scaffold): **4 warnings before, 4 after — identical**
+ (`_dw_sku`/`_dw_hex`/`_dw_style` VariableName + `img_url` DeprecatedFilter, all pre-existing).
+ **0 new offenses** from the slider.
+- **Headless Chromium render** of a PDP-shaped harness (6 real base colors) — observed dot counts:
+
+ | slider value | total dots rendered |
+ |---:|---:|
+ | 20 (default) | **20** (6 base + 14 interpolated) |
+ | 8 (low) | **8** |
+ | 32 (mid) | **32** |
+ | 48 (high) | **48** |
+ | 64 (max) | **64** |
+
+ - `slider_rendered: true`, `base_dots: 6`, `default_slider_value: "20"`, output "20".
+ - **Click a dot → color-index grid opens** (`click_opens_index: true`); grid populated on the real
+ store (endpoint `POST photo.designerwallcoverings.com/apps/color-index` verified live:
+ `access-control-allow-origin: https://www.designerwallcoverings.com`, `ok:true`, tol 0.1, 3 results).
+ (In the `file://` harness the fetch is CORS-blocked by the null origin, so it correctly shows the
+ graceful empty state — a sandbox artifact, not a code bug; the click-open itself passed.)
+ - **Persistence:** set slider → 12, reload → `reload_slider_value: "12"`, `reload_total_dots: 12`
+ (read from `localStorage.dw_palette_dot_count`). ✓
+
+## Commit
+- Content landed on branch `collection-layout-overhaul`, snippet
+ `shopify/theme-LIVE-591-freshpull-20260709/snippets/color-palette.liquid` (155 insertions vs the
+ c7633ec4 baseline). Anchor commit **`be3bcbd1`** (author steve@designerwallcoverings.com) documents
+ the change; the snippet bytes were swept into `f72482f6` first by the background auto-save/cadence
+ loop (both trees identical to the working file). No git remote push.
+
+## EXACT push command for Steve to ship (LIVE, gated)
+```bash
+bash ~/Projects/Designer-Wallcoverings/shopify/push-color-palette-density-slider-to-LIVE.sh --yes-live
+```
+The script resolves the LIVE (role=main) theme, backs up the current live snippet to
+`shopify/theme-backups/color-palette.LIVE-<id>.<stamp>.bak`, guards that the source contains the
+slider logic (`dw_palette_dot_count`, `buildSteps`, `paintSpectrum`, `data-dw-cp-range`), then PUTs
+`snippets/color-palette.liquid` via the Asset API (store `designer-laboratory-sandbox.myshopify.com`,
+API `2024-10`, token `SHOPIFY_THEME_TOKEN`). It **refuses without `--yes-live`**. Read-after-write
+replica lag ~5s before it shows on a PDP.
+
+Rollback: PUT the printed `.bak` back to `snippets/color-palette.liquid` via the same Asset API.
+
+> NOTE: the older `push-color-palette-20dot-cap-to-LIVE.sh` is now stale (its guard checks for the
+> removed `MAX_SWATCHES` const and would refuse). Use the density-slider script above.
diff --git a/shopify/push-color-palette-20dot-cap-to-LIVE.sh b/shopify/push-color-palette-density-slider-to-LIVE.sh
similarity index 50%
rename from shopify/push-color-palette-20dot-cap-to-LIVE.sh
rename to shopify/push-color-palette-density-slider-to-LIVE.sh
index c275687d..4c9f935e 100755
--- a/shopify/push-color-palette-20dot-cap-to-LIVE.sh
+++ b/shopify/push-color-palette-density-slider-to-LIVE.sh
@@ -1,15 +1,25 @@
#!/usr/bin/env bash
-# GATED (customer-facing) — Steve's go required. Ships the 20-dot cap on the PDP
-# "Colors In This Pattern" palette to the LIVE theme (#144076931123, 5.9.1).
+# GATED (customer-facing) — Steve's go required. Ships the PDP color-palette
+# DENSITY SLIDER to the LIVE theme (#144076931123, 5.9.1). Supersedes the earlier
+# 20-dot HARD cap: the cap is now the slider's DEFAULT value.
#
-# Change: snippets/color-palette.liquid — TOTAL rendered dots hard-capped at
-# MAX_SWATCHES=20 (was ~64). Keeps every real base color; interpolated in-between
-# dots distributed EVENLY across the gaps (buildSteps). Click/hex/legend/bar
-# behavior unchanged. Verified locally: node --check OK, theme check 0 NEW
-# offenses (4 pre-existing), headless render = exactly 20 dots + click opens the
-# color-index grid.
+# Change: snippets/color-palette.liquid — "Colors In This Pattern" now has a
+# density slider that lets the user EXPAND/CONTRACT the number of dots shown.
+# - <input type=range> min 6, max 64, DEFAULT 20 (the former hard cap).
+# - Live re-render on drag (no page reload); keeps every real base color and
+# evenly redistributes interpolated dots to hit the target count (never
+# truncates one end of the spectrum) via buildSteps(n, total).
+# - Value persisted to localStorage 'dw_palette_dot_count' (palette-specific
+# key, does NOT collide with the product-grid density slider).
+# - Each dot keeps hex fill + click → 10% CIELAB color-index grid; legend +
+# proportion bar reflect the real base colors (unchanged). CLIP strip +
+# family-collection fallback href untouched.
+# Verified locally: node --check OK (both script blocks), shopify theme check =
+# 0 NEW offenses (4 pre-existing), headless render (6 base colors) → default 20
+# dots, slider→8 contracts to 8, →48 expands to 48, →64 = 64, →32 = 32, click
+# still opens the color-index grid, value persists across reload (localStorage).
#
-# Requires explicit confirmation: bash push-color-palette-20dot-cap-to-LIVE.sh --yes-live
+# Requires explicit confirmation: bash push-color-palette-density-slider-to-LIVE.sh --yes-live
set -euo pipefail
cd "$(dirname "$0")"
@@ -48,11 +58,15 @@ bk = f"{bkdir}/color-palette.LIVE-{tid}.{stamp}.bak"
open(bk, "w").write(cur); print(f"backup: {bk} ({len(cur)} bytes)")
new = open(srcpath).read()
-if "MAX_SWATCHES" not in new or "buildSteps" not in new:
- print("Refusing: source snippet does not contain the 20-dot-cap logic."); sys.exit(1)
+# guard on the density-slider logic (supersedes the old MAX_SWATCHES guard)
+for token in ("dw_palette_dot_count", "buildSteps", "paintSpectrum", "data-dw-cp-range"):
+ if token not in new:
+ print(f"Refusing: source snippet missing density-slider token: {token}"); sys.exit(1)
resp = req(f"https://{store}/admin/api/{api}/themes/{tid}/assets.json",
method="PUT", body={"asset": {"key": key, "value": new}})
print("pushed:", resp["asset"]["key"], "updated_at", resp["asset"].get("updated_at"))
-print("=== LIVE color-palette.liquid updated — dots now capped at 20 ===")
+print("=== LIVE color-palette.liquid updated — density slider (default 20, range 6-64) is live ===")
+print("verify on any PDP: a 'Colors shown' slider sits above the dots; drag to expand/contract.")
+print(f"revert: PUT {bk} back to {key} on theme {tid}")
PY
← be3bcbd1 color-palette: density slider anchor — dots become user-adju
·
back to Designer Wallcoverings
·
auto-save: 2026-07-09T14:40:55 (5 files) — DW-Programming/Im 9c542163 →