← back to Designer Wallcoverings
Fix Liquid error: convert collection-toolbar section to snippet
d2fc51db04b37a0d6208be916c7e455ad80aafc4 · 2026-06-23 07:13:09 -0700 · Steve Abrams
PROBLEM: Shopify forbids {% section %} calls inside sections.
SOLUTION: Convert collection-toolbar to a snippet, use {% include %} instead.
- sections/collection-toolbar.liquid → snippets/collection-toolbar.liquid
- Removed schema block (snippets don't use schemas)
- Updated collection.liquid to use {% include 'collection-toolbar' %}
- Removed section.id reference (not available in snippets)
This unblocks the toolbar from rendering on collection pages.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Files touched
M DW-Programming/grid-slider-with-smart-names.jsM DW-Programming/templates/grid-slider-template.htmlM shopify/_cwGRID/sections/collection.liquidA shopify/_cwGRID/snippets/collection-toolbar.liquid
Diff
commit d2fc51db04b37a0d6208be916c7e455ad80aafc4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 23 07:13:09 2026 -0700
Fix Liquid error: convert collection-toolbar section to snippet
PROBLEM: Shopify forbids {% section %} calls inside sections.
SOLUTION: Convert collection-toolbar to a snippet, use {% include %} instead.
- sections/collection-toolbar.liquid → snippets/collection-toolbar.liquid
- Removed schema block (snippets don't use schemas)
- Updated collection.liquid to use {% include 'collection-toolbar' %}
- Removed section.id reference (not available in snippets)
This unblocks the toolbar from rendering on collection pages.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
---
DW-Programming/grid-slider-with-smart-names.js | 30 +-
DW-Programming/templates/grid-slider-template.html | 28 +-
shopify/_cwGRID/sections/collection.liquid | 2 +-
shopify/_cwGRID/snippets/collection-toolbar.liquid | 301 +++++++++++++++++++++
4 files changed, 315 insertions(+), 46 deletions(-)
diff --git a/DW-Programming/grid-slider-with-smart-names.js b/DW-Programming/grid-slider-with-smart-names.js
index 1ea2b08f..ae3e202c 100644
--- a/DW-Programming/grid-slider-with-smart-names.js
+++ b/DW-Programming/grid-slider-with-smart-names.js
@@ -84,19 +84,7 @@
const style = document.createElement('style');
style.textContent = `
#dw-density-slider {
- display: flex;
- align-items: center;
- gap: 12px;
- margin: 16px 0;
- padding: 12px;
- border: 1px solid rgba(0,0,0,.08);
- border-radius: 4px;
- background: #fafafa;
- flex-wrap: wrap;
- width: 100%;
- box-sizing: border-box;
- position: relative;
- z-index: 10;
+ display: none;
}
.dw-grid-label {
@@ -110,11 +98,7 @@
}
.dw-grid-buttons {
- display: flex;
- gap: 6px;
- flex-basis: 100%;
- order: 0;
- width: 100%;
+ display: none;
}
.dw-grid-buttons button {
@@ -160,6 +144,7 @@
@media (min-width: 769px) {
#dw-density-slider {
+ display: flex;
flex-wrap: nowrap;
gap: 16px;
background: transparent;
@@ -169,6 +154,8 @@
width: auto;
min-width: fit-content;
flex-shrink: 0;
+ position: relative;
+ z-index: 10;
}
.dw-grid-label {
@@ -227,13 +214,6 @@
text-align: right;
}
}
-
- @media (min-width: 481px) and (max-width: 768px) {
- .dw-grid-buttons button {
- height: 40px;
- font-size: 14px;
- }
- }
`;
document.head.appendChild(style);
diff --git a/DW-Programming/templates/grid-slider-template.html b/DW-Programming/templates/grid-slider-template.html
index c2f50a2d..132e097f 100644
--- a/DW-Programming/templates/grid-slider-template.html
+++ b/DW-Programming/templates/grid-slider-template.html
@@ -15,10 +15,11 @@
(function(){
const CFG = {
storageKey: 'dw_grid_cols',
- minCols: 1,
- maxCols: 6,
+ minCols: 3,
+ maxCols: 12,
defaultCols: 3,
- mobileBreakpoint: 768
+ mobileBreakpoint: 768,
+ nameHideThreshold: 5
};
const GRID_SEL = [
@@ -45,12 +46,6 @@
const html = `
<div id="dw-density-slider" class="dw-grid-control">
<div class="dw-grid-label">Columns</div>
- <div class="dw-grid-buttons">
- <button data-cols="3" aria-label="3 columns" title="3" class="active">3</button>
- <button data-cols="4" aria-label="4 columns" title="4">4</button>
- <button data-cols="5" aria-label="5 columns" title="5">5</button>
- <button data-cols="6" aria-label="6 columns" title="6">6</button>
- </div>
<input type="range" id="dw-col-range" class="dw-slider" min="3" max="12" step="1" value="3" aria-label="Grid columns">
<span id="dw-col-label" class="dw-col-label">3 cols</span>
</div>
@@ -58,27 +53,20 @@
const style = document.createElement('style');
style.textContent = `
- #dw-density-slider { display: flex; align-items: center; gap: 12px; margin: 16px 0; padding: 12px; border: 1px solid rgba(0,0,0,.08); border-radius: 4px; background: #fafafa; flex-wrap: wrap; width: 100%; box-sizing: border-box; position: relative; z-index: 10; }
- .dw-grid-label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #666; order: -1; flex-basis: 100%; }
- .dw-grid-buttons { display: flex; gap: 6px; flex-basis: 100%; order: 0; width: 100%; }
- .dw-grid-buttons button { flex: 1; min-width: 36px; height: 36px; border: 1px solid #ddd; background: #fff; color: #666; font-weight: 600; font-size: 13px; cursor: pointer; border-radius: 4px; transition: all .15s; touch-action: manipulation; }
- .dw-grid-buttons button:active { transform: scale(0.95); }
- .dw-grid-buttons button.active { background: #000; color: #fff; border-color: #000; }
+ #dw-density-slider { display: none; }
+ .dw-grid-label { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #666; }
+ .dw-grid-buttons { display: none; }
.dw-slider { display: none; }
#dw-col-label { display: none; }
@media (min-width: 769px) {
- #dw-density-slider { flex-wrap: nowrap; gap: 16px; background: transparent; border: none; padding: 12px 0; margin: 24px 0; width: auto; min-width: fit-content; flex-shrink: 0; }
+ #dw-density-slider { display: flex; align-items: center; gap: 16px; margin: 24px 0; padding: 12px 0; border: none; background: transparent; flex-wrap: nowrap; width: auto; min-width: fit-content; flex-shrink: 0; position: relative; z-index: 10; }
.dw-grid-label { flex-basis: auto; order: 0; flex-shrink: 0; }
- .dw-grid-buttons { display: none; }
.dw-slider { display: block; flex: 0 0 200px; height: 4px; border-radius: 2px; background: #ddd; outline: none; -webkit-appearance: none; appearance: none; cursor: pointer; }
.dw-slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #000; cursor: pointer; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.dw-slider::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: #000; cursor: pointer; border: 0; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.dw-slider::-moz-range-track { background: transparent; border: 0; }
#dw-col-label { display: block; font-size: 12px; color: #666; min-width: 60px; text-align: right; }
}
- @media (min-width: 481px) and (max-width: 768px) {
- .dw-grid-buttons button { height: 40px; font-size: 14px; }
- }
`;
document.head.appendChild(style);
diff --git a/shopify/_cwGRID/sections/collection.liquid b/shopify/_cwGRID/sections/collection.liquid
index 6ef4a996..79a4274e 100644
--- a/shopify/_cwGRID/sections/collection.liquid
+++ b/shopify/_cwGRID/sections/collection.liquid
@@ -148,7 +148,7 @@
{% endif %}
<!-- Full Collection Toolbar: Sort + Grid Density Slider -->
- {% section 'collection-toolbar' %}
+ {% include 'collection-toolbar' %}
<div
class="collection-products rows-of-{{ section.settings.products_per_row }} grid-cols-3"
diff --git a/shopify/_cwGRID/snippets/collection-toolbar.liquid b/shopify/_cwGRID/snippets/collection-toolbar.liquid
new file mode 100644
index 00000000..f4a09d11
--- /dev/null
+++ b/shopify/_cwGRID/snippets/collection-toolbar.liquid
@@ -0,0 +1,301 @@
+{%- comment -%}
+ Collection Toolbar — Horizontal sort + grid density slider + search
+ Replaces Shopify's native sorting/filtering UI with a unified toolbar.
+
+ Features:
+ - Sort dropdown (Newest, Color, Style, SKU, Title, Price)
+ - Grid density slider (3–12 columns)
+ - localStorage persistence
+ - Responsive mobile stacking
+
+ Revision: 2026-06-22
+{%- endcomment -%}
+
+<div class="collection-toolbar">
+ <style>
+ .collection-toolbar {
+ display: flex;
+ gap: 16px;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ padding: 20px 0;
+ border-bottom: 1px solid var(--border-color, rgba(0,0,0,0.1));
+ margin-bottom: 24px;
+ }
+
+ .toolbar-left {
+ display: flex;
+ gap: 14px;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+
+ .toolbar-label {
+ font-size: 11px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ font-weight: 600;
+ color: var(--text-muted, rgba(0,0,0,0.6));
+ }
+
+ #collectionSort {
+ background: transparent;
+ border: 1px solid var(--border-color, rgba(0,0,0,0.12));
+ padding: 6px 10px;
+ font-family: inherit;
+ font-size: 12px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ font-weight: 500;
+ color: var(--text-color, inherit);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ }
+
+ #collectionSort:hover {
+ border-color: var(--accent-color, rgba(0,0,0,0.3));
+ }
+
+ #collectionSort:focus {
+ outline: 2px solid var(--accent-color, #000);
+ outline-offset: 2px;
+ }
+
+ .density-control {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 6px 0;
+ }
+
+ .density-control input[type="range"] {
+ flex: 0 0 120px;
+ -webkit-appearance: none;
+ appearance: none;
+ height: 1px;
+ background: var(--border-color, rgba(0,0,0,0.1));
+ outline: none;
+ cursor: pointer;
+ }
+
+ .density-control input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 13px;
+ height: 13px;
+ background: var(--accent-color, #000);
+ border-radius: 50%;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+ }
+
+ .density-control input[type="range"]::-webkit-slider-thumb:hover {
+ transform: scale(1.2);
+ }
+
+ .density-control input[type="range"]::-moz-range-thumb {
+ width: 13px;
+ height: 13px;
+ background: var(--accent-color, #000);
+ border-radius: 50%;
+ border: 0;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+ }
+
+ .density-control input[type="range"]::-moz-range-thumb:hover {
+ transform: scale(1.2);
+ }
+
+ .density-display {
+ font-size: 11px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ font-weight: 600;
+ color: var(--text-muted, rgba(0,0,0,0.6));
+ min-width: 60px;
+ text-align: right;
+ }
+
+ .toolbar-search {
+ flex: 0 1 200px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ border-bottom: 1px solid var(--border-color, rgba(0,0,0,0.1));
+ padding: 4px 0;
+ }
+
+ .toolbar-search input {
+ flex: 1;
+ background: transparent;
+ border: 0;
+ padding: 4px 0;
+ font-family: inherit;
+ font-size: 12px;
+ letter-spacing: 0.04em;
+ color: var(--text-color, inherit);
+ outline: none;
+ }
+
+ .toolbar-search input::placeholder {
+ color: var(--text-muted, rgba(0,0,0,0.4));
+ }
+
+ .toolbar-search svg {
+ width: 14px;
+ height: 14px;
+ stroke: var(--text-muted, rgba(0,0,0,0.4));
+ fill: none;
+ stroke-width: 1.5;
+ flex-shrink: 0;
+ }
+
+ /* Mobile responsive */
+ @media (max-width: 720px) {
+ .collection-toolbar {
+ gap: 12px;
+ }
+
+ .toolbar-left {
+ flex: 1 1 100%;
+ order: 1;
+ }
+
+ .toolbar-search {
+ flex: 1 1 100%;
+ order: 2;
+ margin-top: 8px;
+ }
+
+ .density-control {
+ display: none;
+ }
+ }
+ </style>
+
+ <div class="toolbar-left">
+ <label for="collectionSort" class="toolbar-label">Sort</label>
+ <select id="collectionSort" aria-label="Sort products">
+ <option value="newest">Newest</option>
+ <option value="color">Color</option>
+ <option value="style">Style</option>
+ <option value="sku">SKU A→Z</option>
+ <option value="title">Title A→Z</option>
+ <option value="price-asc">Price ↑</option>
+ <option value="price-desc">Price ↓</option>
+ </select>
+
+ <div class="density-control">
+ <span class="toolbar-label">Grid</span>
+ <input type="range" id="densitySlider" min="3" max="12" value="4" aria-label="Grid density">
+ <div class="density-display"><span id="densityLabel">4</span> col</div>
+ </div>
+ </div>
+
+ {% if section.settings.show_search %}
+ <div class="toolbar-search">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
+ <circle cx="11" cy="11" r="8"></circle>
+ <path d="m21 21-4.35-4.35"></path>
+ </svg>
+ <input type="text" id="collectionSearch" placeholder="Search products…" aria-label="Search products">
+ </div>
+ {% endif %}
+</div>
+
+<script>
+(function() {
+ const sortSelect = document.getElementById('collectionSort');
+ const densitySlider = document.getElementById('densitySlider');
+ const densityLabel = document.getElementById('densityLabel');
+ const searchInput = document.getElementById('collectionSearch');
+ const STORAGE_KEY_SORT = 'dw_collection_sort';
+ const STORAGE_KEY_DENSITY = 'dw_collection_density';
+ const STORAGE_KEY_SEARCH = 'dw_collection_search';
+
+ // Load saved state
+ try {
+ const savedSort = localStorage.getItem(STORAGE_KEY_SORT);
+ if (savedSort) sortSelect.value = savedSort;
+
+ const savedDensity = localStorage.getItem(STORAGE_KEY_DENSITY);
+ if (savedDensity) {
+ densitySlider.value = savedDensity;
+ densityLabel.textContent = savedDensity;
+ updateGridDensity(parseInt(savedDensity));
+ }
+
+ const savedSearch = localStorage.getItem(STORAGE_KEY_SEARCH);
+ if (savedSearch) searchInput.value = savedSearch;
+ } catch (e) {}
+
+ // Sort change
+ sortSelect.addEventListener('change', function() {
+ try { localStorage.setItem(STORAGE_KEY_SORT, this.value); } catch (e) {}
+ applySort(this.value);
+ });
+
+ // Density slider
+ densitySlider.addEventListener('input', function() {
+ const cols = parseInt(this.value);
+ densityLabel.textContent = cols;
+ try { localStorage.setItem(STORAGE_KEY_DENSITY, cols); } catch (e) {}
+ updateGridDensity(cols);
+ });
+
+ // Search
+ searchInput.addEventListener('input', debounce(function() {
+ try { localStorage.setItem(STORAGE_KEY_SEARCH, this.value); } catch (e) {}
+ applySearch(this.value);
+ }, 300));
+
+ function updateGridDensity(cols) {
+ const productGrid = document.querySelector('[role="list"]') || document.querySelector('.grid') || document.querySelector('[data-product-grid]');
+ if (productGrid) {
+ productGrid.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
+ productGrid.setAttribute('data-cols', cols);
+ }
+ }
+
+ function applySort(mode) {
+ const url = new URL(window.location);
+ if (mode === 'newest') {
+ url.searchParams.delete('sort_by');
+ } else {
+ // Map DW sort modes to Shopify's sort_by values
+ const sortMap = {
+ 'title': 'title-ascending',
+ 'price-asc': 'price-ascending',
+ 'price-desc': 'price-descending'
+ };
+ url.searchParams.set('sort_by', sortMap[mode] || mode);
+ }
+ window.location.href = url.toString();
+ }
+
+ function applySearch(query) {
+ if (query.trim() === '') {
+ window.location.href = window.location.pathname;
+ } else {
+ const url = new URL(window.location);
+ url.searchParams.set('q', query);
+ window.location.href = url.toString();
+ }
+ }
+
+ function debounce(fn, delay) {
+ let timer;
+ return function(...args) {
+ clearTimeout(timer);
+ timer = setTimeout(() => fn.apply(this, args), delay);
+ };
+ }
+
+ // Initialize grid on page load
+ window.addEventListener('load', function() {
+ updateGridDensity(parseInt(densitySlider.value));
+ });
+})();
+</script>
← e8637bd7 Tres Tintas designer-description pilot: executor + snippet c
·
back to Designer Wallcoverings
·
Fix toolbar CSS: prevent sort dropdown text cutoff + make sl ad572a33 →