← back to Designer Wallcoverings
5.1 dev: horizontal collapsible Boost filter bar (dw-filter-horizontal.{css,js} + boost-overrides neutralize legacy collapse) + theme.liquid includes
8e45fc2129bcbffc1ae0d2889ecb8f57be36c7d3 · 2026-06-22 17:11:40 -0700 · Steve Abrams
Files touched
A shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.cssA shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.jsM shopify/theme-5.0-duplicate/layout/theme.liquidM shopify/theme-5.0-duplicate/snippets/dw-boost-overrides.liquid
Diff
commit 8e45fc2129bcbffc1ae0d2889ecb8f57be36c7d3
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 22 17:11:40 2026 -0700
5.1 dev: horizontal collapsible Boost filter bar (dw-filter-horizontal.{css,js} + boost-overrides neutralize legacy collapse) + theme.liquid includes
---
.../assets/dw-filter-horizontal.css | 188 +++++++++++++++++++++
.../assets/dw-filter-horizontal.js | 169 ++++++++++++++++++
shopify/theme-5.0-duplicate/layout/theme.liquid | 3 +
.../snippets/dw-boost-overrides.liquid | 11 +-
4 files changed, 367 insertions(+), 4 deletions(-)
diff --git a/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.css b/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.css
new file mode 100644
index 00000000..10c97b6f
--- /dev/null
+++ b/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.css
@@ -0,0 +1,188 @@
+/* ============================================================================
+ DW Horizontal Filter Bar — restyles Boost AI Search & Discovery filter groups
+ from a vertical LEFT SIDEBAR into a HORIZONTAL, collapsible bar across the
+ top of the product grid.
+
+ Scope: only takes effect when <body> carries the `dw-filters-horizontal`
+ class (added by dw-filter-horizontal.js once the Boost filter tree exists).
+ Replaces the old `dw-filters-collapsed` hide-entirely behavior.
+
+ Boost DOM (React-rendered):
+ .boost-sd__filter-tree-wrapper > .boost-sd__filter-tree (the sidebar)
+ .boost-sd__filter (one per group: Color, Style, Brand, …)
+ .boost-sd__filter-title (group label / toggle)
+ .boost-sd__filter-options (the option list / swatches)
+ .boost-sd__product-list (the grid)
+ ============================================================================ */
+
+/* ---- 1. Make the filter tree a horizontal row pinned above the grid ------- */
+body.dw-filters-horizontal .boost-sd__filter-tree-wrapper {
+ display: block !important;
+ width: 100% !important;
+ max-width: 100% !important;
+ flex: 0 0 100% !important;
+ padding: 0 !important;
+ margin: 0 0 14px 0 !important;
+ border: none !important;
+ background: transparent !important;
+}
+
+/* The product+filter wrap is normally a side-by-side flex row; stack it so the
+ bar sits ABOVE the grid and the grid gets full width. */
+body.dw-filters-horizontal .boost-sd__filter-products-wrap,
+body.dw-filters-horizontal .boost-sd__filter-products-wrap > div {
+ display: block !important;
+ width: 100% !important;
+ max-width: 100% !important;
+}
+body.dw-filters-horizontal .boost-sd__product-list-wrapper,
+body.dw-filters-horizontal .boost-sd__product-list {
+ width: 100% !important;
+ max-width: 100% !important;
+ flex: 1 1 100% !important;
+ padding-left: 0 !important;
+ padding-right: 0 !important;
+}
+
+/* The filter tree itself: a flex row of group chips, wrap on small screens. */
+body.dw-filters-horizontal .boost-sd__filter-tree {
+ display: flex !important;
+ flex-direction: row !important;
+ flex-wrap: wrap !important;
+ align-items: stretch !important;
+ gap: 8px !important;
+ width: 100% !important;
+ max-width: 100% !important;
+ padding: 10px 12px !important;
+ margin: 0 !important;
+ background: #fff !important;
+ border: 1.5px solid #000 !important;
+ border-radius: 12px !important;
+ box-sizing: border-box !important;
+}
+
+/* ---- 2. Each filter group → a self-contained dropdown chip ----------------- */
+body.dw-filters-horizontal .boost-sd__filter {
+ position: relative !important;
+ flex: 0 0 auto !important;
+ width: auto !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border: none !important;
+ border-bottom: none !important;
+}
+
+/* Group title becomes the chip face */
+body.dw-filters-horizontal .boost-sd__filter-title {
+ display: inline-flex !important;
+ align-items: center !important;
+ gap: 6px !important;
+ margin: 0 !important;
+ padding: 7px 14px !important;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
+ font-size: 11px !important;
+ font-weight: 700 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 1px !important;
+ color: #222 !important;
+ background: #f7f5f2 !important;
+ border: 1px solid #cfc8bd !important;
+ border-radius: 999px !important;
+ cursor: pointer !important;
+ white-space: nowrap !important;
+ user-select: none !important;
+}
+body.dw-filters-horizontal .boost-sd__filter-title:hover {
+ background: #efe9e0 !important;
+}
+
+/* The option list becomes an absolutely-positioned dropdown under the chip.
+ We DO NOT hide it with display:none on the Boost element (that would fight
+ Boost's own collapse state); instead we float it and toggle visibility via
+ the `dw-open` class our JS puts on the parent .boost-sd__filter. */
+body.dw-filters-horizontal .boost-sd__filter-options,
+body.dw-filters-horizontal .boost-sd__filter-collapsible,
+body.dw-filters-horizontal .boost-sd__filter-content {
+ position: absolute !important;
+ top: calc(100% + 6px) !important;
+ left: 0 !important;
+ z-index: 60 !important;
+ min-width: 220px !important;
+ max-width: 340px !important;
+ max-height: 60vh !important;
+ overflow-y: auto !important;
+ padding: 12px 14px !important;
+ background: #fff !important;
+ border: 1.5px solid #000 !important;
+ border-radius: 10px !important;
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.16) !important;
+ /* hidden until the group is opened */
+ display: none !important;
+}
+/* Open state — our JS toggles .dw-open on the .boost-sd__filter */
+body.dw-filters-horizontal .boost-sd__filter.dw-open > .boost-sd__filter-options,
+body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-collapsible,
+body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-content {
+ display: block !important;
+}
+
+/* Keep swatch / option layout sane inside the dropdown */
+body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-option {
+ display: flex !important;
+ align-items: center !important;
+}
+
+/* Price ($) group can render a slider — give it room */
+body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-price,
+body.dw-filters-horizontal .boost-sd__filter.dw-open [class*="price-slider"] {
+ min-width: 240px !important;
+}
+
+/* ---- 3. The collapse/expand toggle for the WHOLE bar ----------------------- */
+.dw-hbar-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 12px;
+ margin: 4px 0 4px 8px;
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
+ font-size: 11px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 1.5px;
+ user-select: none;
+ background: #fff;
+ border: 1.5px solid #000;
+ border-radius: 999px;
+ cursor: pointer;
+ vertical-align: middle;
+}
+.dw-hbar-toggle:hover { background: #f5f5f5; }
+.dw-hbar-toggle svg { width: 14px; height: 14px; }
+
+/* When the bar is collapsed, hide the tree wrapper and free the grid. */
+body.dw-filters-horizontal.dw-hbar-collapsed .boost-sd__filter-tree-wrapper {
+ display: none !important;
+}
+
+/* ---- 4. Responsive: horizontal scroll instead of wrap on phones ----------- */
+@media (max-width: 768px) {
+ body.dw-filters-horizontal .boost-sd__filter-tree {
+ flex-wrap: nowrap !important;
+ overflow-x: auto !important;
+ -webkit-overflow-scrolling: touch !important;
+ padding: 8px 10px !important;
+ }
+ body.dw-filters-horizontal .boost-sd__filter-options,
+ body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-collapsible,
+ body.dw-filters-horizontal .boost-sd__filter.dw-open .boost-sd__filter-content {
+ position: fixed !important;
+ top: auto !important;
+ left: 8px !important;
+ right: 8px !important;
+ bottom: 8px !important;
+ min-width: 0 !important;
+ max-width: none !important;
+ max-height: 55vh !important;
+ }
+}
diff --git a/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.js b/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.js
new file mode 100644
index 00000000..5a22dbe8
--- /dev/null
+++ b/shopify/theme-5.0-duplicate/assets/dw-filter-horizontal.js
@@ -0,0 +1,169 @@
+/* ============================================================================
+ DW Horizontal Filter Bar — JS
+ Turns the Boost AI Search & Discovery vertical filter sidebar into a
+ horizontal, collapsible filter bar across the top of the product grid.
+
+ What it does (theme-side only, no Boost-dashboard change):
+ 1. Waits for Boost's React-rendered .boost-sd__filter-tree to exist.
+ 2. Adds body.dw-filters-horizontal so dw-filter-horizontal.css applies the
+ horizontal layout (each group becomes a chip with a dropdown panel).
+ 3. Each group title (.boost-sd__filter-title) toggles a `.dw-open` class on
+ its .boost-sd__filter so only one dropdown shows at a time. Clicks on the
+ actual option items still bubble to Boost, so filtering still works.
+ 4. Injects a single "Filters" pill into the Boost toolbar that collapses /
+ expands the whole bar; collapsed state persists in localStorage.
+
+ Replaces the old `dw-filters-collapsed` (hide-entirely) behavior on
+ collection/search pages. Only runs where a Boost filter tree is present.
+ ============================================================================ */
+(function () {
+ 'use strict';
+
+ var BODY = document.body;
+ var LS_KEY = 'dw-hbar-collapsed';
+ var wired = false;
+
+ function isGridPage() {
+ return /\/(collections|search)/.test(window.location.pathname);
+ }
+
+ function qs(sel, root) { return (root || document).querySelector(sel); }
+
+ /* Toggle a single filter group open/closed (accordion: one at a time). */
+ function bindGroup(filterEl) {
+ if (filterEl.getAttribute('data-dw-hbar')) return;
+ filterEl.setAttribute('data-dw-hbar', '1');
+
+ var title = qs('.boost-sd__filter-title', filterEl);
+ if (!title) return;
+
+ title.addEventListener('click', function (e) {
+ // Only handle clicks on the title chip itself, not on option rows that
+ // Boost may have nested — let those reach Boost.
+ if (e.target.closest('.boost-sd__filter-option') ||
+ e.target.closest('.boost-sd__filter-options')) {
+ return;
+ }
+ var alreadyOpen = filterEl.classList.contains('dw-open');
+ // Close all siblings first
+ var tree = filterEl.closest('.boost-sd__filter-tree');
+ if (tree) {
+ tree.querySelectorAll('.boost-sd__filter.dw-open').forEach(function (f) {
+ if (f !== filterEl) f.classList.remove('dw-open');
+ });
+ }
+ filterEl.classList.toggle('dw-open', !alreadyOpen);
+ });
+ }
+
+ function bindAllGroups() {
+ var groups = document.querySelectorAll('.boost-sd__filter-tree .boost-sd__filter');
+ for (var i = 0; i < groups.length; i++) bindGroup(groups[i]);
+ }
+
+ /* Close any open dropdown when clicking outside the bar. */
+ function bindOutsideClose() {
+ document.addEventListener('click', function (e) {
+ if (e.target.closest('.boost-sd__filter-tree')) return;
+ document.querySelectorAll('.boost-sd__filter.dw-open').forEach(function (f) {
+ f.classList.remove('dw-open');
+ });
+ });
+ }
+
+ /* Build the collapse/expand toggle pill and drop it in the Boost toolbar. */
+ function buildToggle() {
+ if (qs('#dw-hbar-toggle')) return;
+ var toolbar = qs('.boost-sd__toolbar-item--product-count, .boost-sd__product-count');
+ if (!toolbar || !toolbar.parentElement) return;
+
+ var btn = document.createElement('button');
+ btn.id = 'dw-hbar-toggle';
+ btn.className = 'dw-hbar-toggle';
+ btn.type = 'button';
+ btn.setAttribute('aria-label', 'Toggle filter bar');
+
+ var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ svg.setAttribute('viewBox', '0 0 24 24');
+ svg.setAttribute('fill', 'none');
+ svg.setAttribute('stroke', 'currentColor');
+ svg.setAttribute('stroke-width', '2');
+ var ys = ['6', '12', '18'];
+ for (var i = 0; i < ys.length; i++) {
+ var l = document.createElementNS('http://www.w3.org/2000/svg', 'line');
+ l.setAttribute('x1', '4'); l.setAttribute('y1', ys[i]);
+ l.setAttribute('x2', '20'); l.setAttribute('y2', ys[i]);
+ svg.appendChild(l);
+ }
+ btn.appendChild(svg);
+
+ var label = document.createElement('span');
+ label.id = 'dw-hbar-label';
+ btn.appendChild(label);
+
+ function syncLabel() {
+ var collapsed = BODY.classList.contains('dw-hbar-collapsed');
+ label.textContent = collapsed ? 'Filters' : 'Hide Filters';
+ }
+
+ btn.addEventListener('click', function () {
+ var collapsed = BODY.classList.toggle('dw-hbar-collapsed');
+ try { localStorage.setItem(LS_KEY, collapsed ? '1' : '0'); } catch (e) {}
+ syncLabel();
+ });
+
+ toolbar.parentElement.insertBefore(btn, toolbar);
+ syncLabel();
+ }
+
+ /* Apply the persisted collapsed state. Default: bar EXPANDED (visible). */
+ function applyPersistedState() {
+ var collapsed = false;
+ try { collapsed = localStorage.getItem(LS_KEY) === '1'; } catch (e) {}
+ BODY.classList.toggle('dw-hbar-collapsed', collapsed);
+ }
+
+ function wire() {
+ if (wired) return true;
+ var tree = qs('.boost-sd__filter-tree');
+ if (!tree) return false;
+ wired = true;
+
+ // Remove the legacy hide-entirely behavior so the two don't fight.
+ BODY.classList.remove('dw-filters-collapsed');
+ BODY.classList.add('dw-filters-horizontal');
+
+ applyPersistedState();
+ bindAllGroups();
+ bindOutsideClose();
+ buildToggle();
+
+ // Boost re-renders the tree on filter apply / page change — re-bind groups
+ // and keep our toggle present.
+ var obs = new MutationObserver(function () {
+ clearTimeout(obs._t);
+ obs._t = setTimeout(function () {
+ bindAllGroups();
+ buildToggle();
+ }, 150);
+ });
+ var wrap = qs('.boost-sd__filter-tree-wrapper') || tree.parentElement || document.body;
+ obs.observe(wrap, { childList: true, subtree: true });
+
+ return true;
+ }
+
+ function start() {
+ if (!isGridPage()) return;
+ var tries = 0;
+ var poll = setInterval(function () {
+ if (wire() || tries++ > 80) clearInterval(poll);
+ }, 250);
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', start);
+ } else {
+ start();
+ }
+})();
diff --git a/shopify/theme-5.0-duplicate/layout/theme.liquid b/shopify/theme-5.0-duplicate/layout/theme.liquid
index 7a0347ad..272543e4 100644
--- a/shopify/theme-5.0-duplicate/layout/theme.liquid
+++ b/shopify/theme-5.0-duplicate/layout/theme.liquid
@@ -1023,5 +1023,8 @@ alt="web statistics"></a></div></noscript>
{%- comment -%} DW PDP restyle: size <select> -> pills + uniform CTA pill layout (2026-06-22) {%- endcomment -%}
{{ 'dw-pdp-restyle.css' | asset_url | stylesheet_tag }}
<script src="{{ 'dw-pdp-size-pills.js' | asset_url }}" defer></script>
+{%- comment -%} DW horizontal collapsible filter bar over Boost (2026-06-22) {%- endcomment -%}
+{{ 'dw-filter-horizontal.css' | asset_url | stylesheet_tag }}
+<script src="{{ 'dw-filter-horizontal.js' | asset_url }}" defer></script>
</body>
</html>
diff --git a/shopify/theme-5.0-duplicate/snippets/dw-boost-overrides.liquid b/shopify/theme-5.0-duplicate/snippets/dw-boost-overrides.liquid
index 322bbf9c..e8d5e98e 100644
--- a/shopify/theme-5.0-duplicate/snippets/dw-boost-overrides.liquid
+++ b/shopify/theme-5.0-duplicate/snippets/dw-boost-overrides.liquid
@@ -31,8 +31,10 @@ body.dw-filters-collapsed .boost-sd__product-list[class*="grid--"]{grid-template
.boost-sd__product-item img{max-width:100% !important;height:auto !important;width:100% !important}
.boost-sd__product-list{overflow:hidden !important;display:grid !important}
-/* Filter toggle button */
-.dw-filter-toggle{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;margin:4px 0 4px 8px;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;user-select:none;background:#fff;border:1.5px solid #000;border-radius:999px;cursor:pointer;vertical-align:middle}
+/* Legacy filter toggle button — superseded by the horizontal-bar toggle
+ (#dw-hbar-toggle in dw-filter-horizontal.js). Hidden to avoid two pills. */
+.dw-filter-toggle{display:none !important}
+.dw-filter-toggle--legacy{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;margin:4px 0 4px 8px;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;user-select:none;background:#fff;border:1.5px solid #000;border-radius:999px;cursor:pointer;vertical-align:middle}
.dw-filter-toggle:hover{background:#f5f5f5}
.dw-filter-toggle svg{width:14px;height:14px}
@@ -82,8 +84,9 @@ body.dw-filters-collapsed .boost-sd__product-list[class*="grid--"]{grid-template
<script>
(function(){
- /* Collapse filters immediately */
- document.body.classList.add('dw-filters-collapsed');
+ /* Horizontal filter bar (dw-filter-horizontal.js) now owns filter visibility.
+ Do NOT add the legacy 'dw-filters-collapsed' hide-entirely class — it would
+ hide the sidebar that the horizontal bar restyles in place. */
/* Set CSS var for default 4 cols */
document.documentElement.style.setProperty('--dw-grid-cols', 4);
← 6b98ef31 DW PDP restyle: size-select -> pills + uniform CTA pill layo
·
back to Designer Wallcoverings
·
Port MOQ enforcement to Steves Version 5.1 (Archetype): meta d8930599 →