← back to Designerlookbooks
persist sort + density to localStorage on lookbook and search pages
2a95466d905277f3cbb347e02b02c03494f7e916 · 2026-05-19 15:20:04 -0700 · Steve Abrams
Standing rule: every product grid must have sort + density controls
whose value persists across reloads, and the saved value must hydrate
BEFORE the first grid render. The lookbook + search pages already had
sort + density UI, but only density was persisted; sort was never
written to localStorage and never restored. Fix:
- Sort onchange handler now writes dl-sort to localStorage before reload.
- New hydration IIFE on both pages: when URL has no sort param and a
saved sort exists (and it isn't the server default 'newest'), redirect
once via location.replace so the server can re-sort the grid in the
user's chosen order. Density still hydrates inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 2a95466d905277f3cbb347e02b02c03494f7e916
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 19 15:20:04 2026 -0700
persist sort + density to localStorage on lookbook and search pages
Standing rule: every product grid must have sort + density controls
whose value persists across reloads, and the saved value must hydrate
BEFORE the first grid render. The lookbook + search pages already had
sort + density UI, but only density was persisted; sort was never
written to localStorage and never restored. Fix:
- Sort onchange handler now writes dl-sort to localStorage before reload.
- New hydration IIFE on both pages: when URL has no sort param and a
saved sort exists (and it isn't the server default 'newest'), redirect
once via location.replace so the server can re-sort the grid in the
user's chosen order. Density still hydrates inline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
server.js | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 51 insertions(+), 11 deletions(-)
diff --git a/server.js b/server.js
index 8b4bd4b..2b8b6ea 100644
--- a/server.js
+++ b/server.js
@@ -702,7 +702,7 @@ app.get('/lookbook/:slug', (req, res) => {
<div class="toolbar" id="grid">
<div class="group">
<label for="sort">Sort</label>
- <select id="sort" onchange="(function(s){var u=new URL(location);u.searchParams.set('sort',s.value);u.searchParams.delete('page');location=u.toString();})(this)">
+ <select id="sort" onchange="(function(s){try{localStorage.setItem('dl-sort',s.value);}catch(e){}var u=new URL(location);u.searchParams.set('sort',s.value);u.searchParams.delete('page');location=u.toString();})(this)">
${[
['newest', 'Newest'],
['color', 'Color'],
@@ -754,15 +754,30 @@ app.get('/lookbook/:slug', (req, res) => {
</nav>` : ''}
</main>
<script>
- // Restore density preference if user saved one previously and the URL didn't override.
- try {
- var saved = localStorage.getItem('dl-cm');
- var hasUrl = new URL(location).searchParams.get('cm');
- if (saved && !hasUrl) {
- document.documentElement.style.setProperty('--card-min', saved + 'px');
- var el = document.getElementById('cm'); if (el) el.value = saved;
- }
- } catch (e) {}
+ // Hydrate sort + density preferences from localStorage when the URL
+ // doesn't already specify them. Sort needs a server round-trip
+ // because the grid is server-rendered — redirect once with the
+ // saved value so the page renders in the user's chosen order.
+ (function() {
+ try {
+ var url = new URL(location);
+ var qs = url.searchParams;
+ var savedSort = localStorage.getItem('dl-sort');
+ var validSorts = ['newest','color','style','sku','title','price-up','price-down'];
+ if (savedSort && validSorts.indexOf(savedSort) >= 0 && !qs.get('sort') && savedSort !== 'newest') {
+ qs.set('sort', savedSort);
+ location.replace(url.toString());
+ return;
+ }
+ var savedCm = localStorage.getItem('dl-cm');
+ if (savedCm && !qs.get('cm')) {
+ document.documentElement.style.setProperty('--card-min', savedCm + 'px');
+ var grid = document.querySelector('.grid');
+ if (grid) grid.style.setProperty('--card-min', savedCm + 'px');
+ var el = document.getElementById('cm'); if (el) el.value = savedCm;
+ }
+ } catch (e) {}
+ })();
</script>
`;
res.set('Content-Type', 'text/html; charset=utf-8').send(layout({
@@ -837,7 +852,7 @@ app.get('/search', (req, res) => {
<div class="toolbar">
<div class="group">
<label for="sort">Sort</label>
- <select id="sort" onchange="(function(s){var u=new URL(location);u.searchParams.set('sort',s.value);u.searchParams.delete('page');location=u.toString();})(this)">
+ <select id="sort" onchange="(function(s){try{localStorage.setItem('dl-sort',s.value);}catch(e){}var u=new URL(location);u.searchParams.set('sort',s.value);u.searchParams.delete('page');location=u.toString();})(this)">
${[
['newest', 'Newest'],
['color', 'Color'],
@@ -882,6 +897,31 @@ app.get('/search', (req, res) => {
${cur < pages ? `<a href="?${new URLSearchParams({ ...req.query, page: cur + 1 }).toString()}">Next ›</a>` : ''}
</nav>` : ''}
</main>
+ <script>
+ // Hydrate sort + density from localStorage when the URL doesn't
+ // already specify them. Mirrors the lookbook page so user choice
+ // sticks across the whole storefront.
+ (function() {
+ try {
+ var url = new URL(location);
+ var qs = url.searchParams;
+ var savedSort = localStorage.getItem('dl-sort');
+ var validSorts = ['newest','color','style','sku','title'];
+ if (savedSort && validSorts.indexOf(savedSort) >= 0 && !qs.get('sort') && savedSort !== 'newest') {
+ qs.set('sort', savedSort);
+ location.replace(url.toString());
+ return;
+ }
+ var savedCm = localStorage.getItem('dl-cm');
+ if (savedCm && !qs.get('cm')) {
+ document.documentElement.style.setProperty('--card-min', savedCm + 'px');
+ var grid = document.querySelector('.grid');
+ if (grid) grid.style.setProperty('--card-min', savedCm + 'px');
+ var el = document.getElementById('cm'); if (el) el.value = savedCm;
+ }
+ } catch (e) {}
+ })();
+ </script>
`;
res.set('Content-Type', 'text/html; charset=utf-8').send(layout({
title: q ? `Search: ${q}` : 'Search',
← 8a72dd2 add noreferrer to all target=_blank external links
·
back to Designerlookbooks
·
404-guard snapshot/backup paths and broaden .gitignore 2764ec6 →