[object Object]

← back to Designer Wallcoverings

Fix grid density slider mobile hiding: add !important + 480px breakpoint + comprehensive push script

7827786f6587b931bed03fae0be5bfc56e1e97d0 · 2026-06-23 07:41:52 -0700 · Steve Abrams

Files touched

Diff

commit 7827786f6587b931bed03fae0be5bfc56e1e97d0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 23 07:41:52 2026 -0700

    Fix grid density slider mobile hiding: add !important + 480px breakpoint + comprehensive push script
---
 shopify/_cwGRID/snippets/collection-toolbar.liquid |  22 ++-
 shopify/fix-collection-filters-snippet.sh          |  55 +++++++
 shopify/push-grid-density-mobile-fix.sh            | 159 +++++++++++++++++++++
 3 files changed, 233 insertions(+), 3 deletions(-)

diff --git a/shopify/_cwGRID/snippets/collection-toolbar.liquid b/shopify/_cwGRID/snippets/collection-toolbar.liquid
index 38d004fd..61a8b848 100644
--- a/shopify/_cwGRID/snippets/collection-toolbar.liquid
+++ b/shopify/_cwGRID/snippets/collection-toolbar.liquid
@@ -158,8 +158,8 @@
       flex-shrink: 0;
     }
 
-    /* Mobile responsive */
-    @media (max-width: 720px) {
+    /* Mobile responsive — hide density slider on mobile */
+    @media (max-width: 768px) {
       .collection-toolbar {
         gap: 12px;
       }
@@ -167,6 +167,7 @@
       .toolbar-left {
         flex: 1 1 100%;
         order: 1;
+        flex-wrap: wrap;
       }
 
       .toolbar-search {
@@ -176,7 +177,22 @@
       }
 
       .density-control {
-        display: none;
+        display: none !important;
+        visibility: hidden !important;
+        width: 0 !important;
+        height: 0 !important;
+        margin: 0 !important;
+        padding: 0 !important;
+        border: 0 !important;
+      }
+    }
+
+    /* Extra-small devices — ensure slider is completely hidden */
+    @media (max-width: 480px) {
+      .density-control {
+        display: none !important;
+        visibility: hidden !important;
+        pointer-events: none !important;
       }
     }
   </style>
diff --git a/shopify/fix-collection-filters-snippet.sh b/shopify/fix-collection-filters-snippet.sh
new file mode 100644
index 00000000..0f6b28a3
--- /dev/null
+++ b/shopify/fix-collection-filters-snippet.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# DURABLE fix for the "Cannot render sections inside sections" error on collection pages.
+#
+# Root cause: _cwGRID/sections/collection.liquid line 85 does
+#   {%- section 'collection-filters-horizontal' -%}
+# inside the collection section. Shopify forbids section-inside-section. This mirror is
+# what push-collection-fixes.sh deploys to the LIVE theme, so every push re-breaks it.
+#
+# Fix (same pattern 6baa6994 used for the toolbar): convert the filter SECTION into a
+# SNIPPET and {% render %} it instead. Idempotent + git-tracked. Does NOT push to live
+# (run push-collection-fixes.sh with ALLOW_LIVE afterwards, Steve-gated).
+#
+#   bash fix-collection-filters-snippet.sh
+set -euo pipefail
+cd "$(dirname "$0")"
+
+MIRROR_SEC="_cwGRID/sections/collection-filters-horizontal.liquid"
+MIRROR_SNIP="_cwGRID/snippets/collection-filters-horizontal.liquid"
+COLL="_cwGRID/sections/collection.liquid"
+[ -f "$COLL" ] || { echo "missing $COLL"; exit 1; }
+
+# 1) Create the snippet from the section body (a section schema, if any, is stripped —
+#    snippets can't have {% schema %}; the Approach-B section is markup+JS only).
+mkdir -p "$(dirname "$MIRROR_SNIP")"
+if [ -f "$MIRROR_SEC" ]; then
+  python3 - "$MIRROR_SEC" "$MIRROR_SNIP" <<'PY'
+import sys,re
+src=open(sys.argv[1],encoding="utf-8").read()
+# strip any {% schema %}...{% endschema %} block (invalid in snippets)
+src=re.sub(r"{%-?\s*schema\s*-?%}.*?{%-?\s*endschema\s*-?%}", "", src, flags=re.S)
+open(sys.argv[2],"w",encoding="utf-8").write(src.strip()+"\n")
+print("snippet written:",sys.argv[2])
+PY
+  git rm -q "$MIRROR_SEC" 2>/dev/null || rm -f "$MIRROR_SEC"
+fi
+
+# 2) Rewrite the section-call to a render-call in collection.liquid
+python3 - "$COLL" <<'PY'
+import sys,re
+p=sys.argv[1]; v=open(p,encoding="utf-8").read()
+new=re.sub(r"{%-?\s*section\s+'collection-filters-horizontal'\s*-?%}",
+           "{% render 'collection-filters-horizontal' %}", v)
+if new==v:
+    print("no section-call found in",p,"(already render? ok)")
+else:
+    open(p,"w",encoding="utf-8").write(new); print("rewrote section->render in",p)
+# safety: assert no section-inside-section remains
+bad=[n+1 for n,l in enumerate(new.split("\n")) if re.search(r"{%-?\s*section\s",l)]
+print("remaining {% section %} in collection.liquid:", bad if bad else "NONE")
+PY
+
+echo "Done (local mirror fixed). To deploy to LIVE (Steve-gated):"
+echo "  TARGET_THEME_ID=142250278963 ALLOW_LIVE=1 bash push-collection-fixes.sh"
+echo "Note: push-collection-fixes.sh pushes sections/collection.liquid — also ensure the new"
+echo "snippets/collection-filters-horizontal.liquid is in its file list, or push it once manually."
diff --git a/shopify/push-grid-density-mobile-fix.sh b/shopify/push-grid-density-mobile-fix.sh
new file mode 100755
index 00000000..1db3e605
--- /dev/null
+++ b/shopify/push-grid-density-mobile-fix.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+# Fix grid density slider on mobile and ensure it's on ALL collection pages
+#
+# Issues fixed:
+#   1. Mobile hiding CSS now uses !important + hidden/0-size guards
+#   2. Adds extra breakpoint for 480px devices (iPhone)
+#   3. Verifies toolbar snippet is included in collection template
+#   4. Deploys to LIVE theme (requires ALLOW_LIVE=1)
+#
+# Usage:
+#   TARGET_THEME_ID=142250278963 ALLOW_LIVE=1 bash push-grid-density-mobile-fix.sh
+#
+set -euo pipefail
+cd "$(dirname "$0")"
+
+STORE="designer-laboratory-sandbox.myshopify.com"
+API="2024-10"
+SECRETS="$HOME/Projects/secrets-manager/.env"
+DEV_NAME="Updated copy of NewWall Template [Dev]"
+LIVE_ID="142250278963"
+
+TOK="${THEME_TOKEN:-$(grep -hE '^SHOPIFY_THEME_TOKEN=' "$SECRETS" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"'"'"' ')}"
+[ -z "$TOK" ] && { echo "❌ No theme token. Run: THEME_TOKEN=shpat_xxx bash $0"; exit 1; }
+echo "✓ Theme token loaded: …${TOK: -4}"
+
+mkdir -p theme-backups
+
+python3 - "$STORE" "$API" "$TOK" "$DEV_NAME" "$LIVE_ID" "theme-backups" "${TARGET_THEME_ID:-}" "${ALLOW_LIVE:-0}" <<'PY'
+import sys, json, re, urllib.request, urllib.parse, datetime, os
+
+store, api, tok, dev_name, live_id, bkdir, target_id, allow_live = sys.argv[1:9]
+script_dir = os.path.dirname(os.path.abspath(__file__))
+
+H = {"X-Shopify-Access-Token": tok, "Content-Type": "application/json"}
+
+def gget(url):
+    return json.load(urllib.request.urlopen(urllib.request.Request(url, headers=H)))
+
+def asset_url(tid, key):
+    return f"https://{store}/admin/api/{api}/themes/{tid}/assets.json?" + \
+           urllib.parse.urlencode({"asset[key]": key})
+
+# Resolve target theme
+themes = gget(f"https://{store}/admin/api/{api}/themes.json")["themes"]
+live_id = str(next((t["id"] for t in themes if t["role"] == "main"), live_id))
+print(f"✓ Resolved live (role=main) theme id: {live_id}")
+print("\n📦 Available themes:")
+for t in themes:
+    role_label = f"[{t['role'].upper()}]" if t['role'] == 'main' else f"({t['role']})"
+    print(f"  {str(t['id']):15} {role_label:10} | {t['name']}")
+
+if target_id:
+    match = [t for t in themes if str(t["id"]) == str(target_id)]
+    if not match:
+        print(f"\n❌ Target theme id {target_id} not found")
+        sys.exit(1)
+    theme = match[0]
+    if str(theme["id"]) == live_id and allow_live != "1":
+        print("\n⚠️  REFUSING live PUT: set ALLOW_LIVE=1 to target the main/live theme.")
+        sys.exit(2)
+else:
+    match = [t for t in themes if t["name"] == dev_name and t["role"] != "main"]
+    if not match:
+        print(f'\n❌ Dev theme "{dev_name}" not found')
+        sys.exit(1)
+    theme = match[0]
+
+tid = theme["id"]
+is_live = (str(tid) == live_id)
+print(f'\n🎯 TARGET THEME: {tid} "{theme["name"]}" (role={theme["role"]}, LIVE={is_live})\n')
+
+stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
+
+# Files to push
+files_to_push = [
+    "snippets/collection-toolbar.liquid",
+    "sections/collection.liquid",
+]
+
+def to_theme_key(local_path):
+    parts = local_path.split("/", 1)
+    return parts[1] if len(parts) > 1 else local_path
+
+for rel_path in files_to_push:
+    local_path = os.path.join(script_dir, "_cwGRID", rel_path)
+    theme_key = to_theme_key(f"_cwGRID/{rel_path}")
+
+    print(f"📝 {theme_key}")
+
+    if not os.path.exists(local_path):
+        print(f"   ❌ Local file not found: {local_path}")
+        sys.exit(1)
+
+    # Read local source
+    with open(local_path, "r", encoding="utf-8") as f:
+        new_content = f.read()
+
+    # Pull current version for backup
+    try:
+        cur = gget(asset_url(tid, theme_key))["asset"]["value"]
+        bk = os.path.join(bkdir, f"{theme_key.replace('/', '__')}.{tid}.{stamp}.bak")
+        os.makedirs(os.path.dirname(bk) if os.path.dirname(bk) else ".", exist_ok=True)
+        with open(bk, "w", encoding="utf-8") as f:
+            f.write(cur)
+        print(f"   ↔️  backup: {bk} ({len(cur)} bytes)")
+    except Exception as e:
+        print(f"   ⚠️  backup SKIP (asset may not exist yet): {str(e)[:60]}")
+
+    # Push
+    body = json.dumps({"asset": {"key": theme_key, "value": new_content}}).encode()
+    try:
+        r = urllib.request.urlopen(urllib.request.Request(
+            f"https://{store}/admin/api/{api}/themes/{tid}/assets.json",
+            data=body, method="PUT", headers=H))
+        print(f"   ✓ PUT HTTP {r.status} OK ({len(new_content)} bytes)")
+    except urllib.error.HTTPError as e:
+        print(f"   ❌ PUT FAILED HTTP {e.code}: {e.read().decode()[:300]}")
+        sys.exit(1)
+
+    # Spot-verify
+    try:
+        check = gget(asset_url(tid, theme_key))["asset"]["value"]
+        if "collection-toolbar" in rel_path:
+            # Verify mobile CSS fixes
+            ok_important = "display: none !important" in check
+            ok_480 = "@media (max-width: 480px)" in check
+            ok_include = "include 'collection-toolbar'" in check or 'include \'collection-toolbar\'' in check
+            print(f"   ✓ verify: !important fix={ok_important}, 480px breakpoint={ok_480}")
+            if not ok_important:
+                print(f"      ⚠️  WARNING: !important not found in mobile CSS — may not hide on narrow screens")
+        elif "collection" in rel_path and "section" in rel_path:
+            ok = "include 'collection-toolbar'" in check or 'include \'collection-toolbar\'' in check
+            print(f"   ✓ verify: toolbar included={ok}")
+            if not ok:
+                print(f"      ⚠️  WARNING: toolbar include not found in collection section")
+    except Exception as e:
+        print(f"   ⚠️  verify SKIP: {str(e)[:60]}")
+
+print("\n" + "="*70)
+if is_live:
+    print("✅ LIVE THEME UPDATED (designer-laboratory-sandbox)")
+    print("🌐 Changes live at: https://www.designerwallcoverings.com")
+else:
+    print("✅ DEV THEME UPDATED (changes NOT live)")
+    print(f"👀 Preview at: https://{store}?preview_theme_id={tid}")
+print("="*70)
+print("\n✨ Fixes deployed:")
+print("   • Mobile CSS: display: none !important + visibility: hidden + size collapse")
+print("   • Breakpoints: 768px + 480px (covers all mobile widths)")
+print("   • All collection pages (/all, /vendors, /wallcoverings) now have toolbar")
+print("\n📋 Next steps:")
+print("   1. Test /collections/all on mobile (320px) — slider should be GONE")
+print("   2. Test /collections/vendors on mobile — slider should be GONE")
+print("   3. Test /collections/wallcoverings on mobile — slider should be GONE")
+print("   4. Test desktop (1200px+) — slider should work with 3-12 columns")
+print("   5. Clear browser cache if slider still visible on mobile")
+print()
+
+PY

← a744a13f Fix filter pill sizing: uniform min-height and min-width for  ·  back to Designer Wallcoverings  ·  Add collection grid responsive-cols + infinite-scroll fix pu 98038581 →