← back to Designer Wallcoverings
HORIZONTAL-FILTERS-B-DEPLOYMENT.md
182 lines
# Horizontal Filter Bar — Approach B Deployment
## Overview
**Approach B: Custom Horizontal Filter UI** that hides Boost's vertical sidebar and builds a separate chip-based filter bar that proxies clicks into Boost's hidden native controls.
### Why This Works
- ✅ **Decoupled from Boost's React DOM** — Boost can re-render internally without breaking our UI
- ✅ **Only uses stable Boost APIs** — hidden `<input>` elements and `change` events (rarely change)
- ✅ **No CSS override fragility** — we don't fight Boost's CSS, we hide it and replace it
- ✅ **Maintainable long-term** — our UI is self-contained and testable
### What Breaks in Approach A
1. CSS `.boost-sd__*` selectors are volatile (Boost changes them on updates)
2. MutationObserver to re-apply body classes is timing-fragile
3. Prior attempt broke header nav + SORT BY rendering vertically
4. **→ Revert proved unfixable (commit f8cc8cf5)**
---
## Implementation Steps
### Phase 1: Wire Section into Collection Template
**File:** `shopify/_cwGRID/sections/collection.liquid`
Locate the existing `collection-filters` div (around line 82). Replace or supplement with:
```liquid
{%- if show_filters and collection.filters.size > 0 -%}
{%- section 'collection-filters-horizontal' -%}
{%- endif -%}
```
OR (if you want both for A/B testing):
```liquid
{%- if show_filters and collection.filters.size > 0 -%}
<!-- Approach B: Horizontal filter bar (NEW) -->
{%- section 'collection-filters-horizontal' -%}
<!-- Boost native filters (hidden by B's CSS) -->
<div class="faceted-filters" data-faceted-filter>
{%- render "faceted-filters", filters: collection.filters, class_prefix: 'collection' -%}
</div>
{%- endif -%}
```
### Phase 2: Update Boost Integration
**File:** Wherever Boost is initialized (likely in theme.liquid or a Boost snippet)
Ensure Boost's filter inputs have these data attributes for our proxy to find them:
```html
<input
type="checkbox"
data-filter-type="color"
data-filter-label="Gold"
value="color:Gold"
class="boost-filter-input"
/>
```
If Boost doesn't expose these attributes, add them via JavaScript in the section:
```javascript
// Auto-populate Boost inputs with data attributes if missing
document.querySelectorAll('.boost-sd input[type="checkbox"]').forEach(input => {
if (!input.dataset.filterType) {
// Extract filter type and label from Boost's DOM structure
const label = input.closest('.boost-sd__filter-item')?.querySelector('label')?.textContent || input.value;
const type = input.closest('[data-boost-filter-type]')?.dataset.boostFilterType || 'unknown';
input.dataset.filterType = type;
input.dataset.filterLabel = label;
}
});
```
### Phase 3: Test in Sandbox
**URL:** `https://designer-laboratory-sandbox.myshopify.com/admin`
1. **Go to a collection** (e.g., wallpaper by color)
2. **Verify Boost's vertical sidebar hides** — check that `.boost-sd__filter-tree` is gone (display: none)
3. **Click filter chips** — each click should:
- Toggle the chip's active state (background → black)
- Proxy to a hidden Boost input's change event
- Update the grid in real-time (Boost does the filtering)
4. **Check page layout**:
- ✅ Header nav stays horizontal
- ✅ SORT BY dropdown renders correctly
- ✅ Grid density slider (if present) works
5. **Mobile test** (< 720px):
- Filter chips stack
- Clear button goes full-width
- No label text visible
### Phase 4: Deploy to Live
1. **Backup current collection template** (auto-save available)
2. **Push code to live theme** (use Shopify theme push)
3. **Verify on production**:
```
https://www.designerwallcoverings.com/collections/wallpaper-by-color
```
4. **Monitor for 24 hours**:
- Check Sentry/logs for JS errors
- Test random 5–10 collections
- Verify filter interactions work end-to-end
### Phase 5: Monitor & Iterate
#### If filter options don't show up:
- **Problem:** Boost's filter inputs don't have `data-filter-type` attributes
- **Fix:** Enhance the section's JS to auto-detect filter structure:
```javascript
// If data attributes missing, parse from Boost's class names
const filterType = input.classList
.find(c => c.startsWith('boost-filter-'))
?.replace('boost-filter-', '');
```
#### If click doesn't trigger filter:
- **Problem:** Boost uses a different event system (not `change`)
- **Fix:** Also dispatch `input`, `click`, or custom events:
```javascript
input.dispatchEvent(new Event('change', { bubbles: true }));
input.dispatchEvent(new Event('input', { bubbles: true }));
if (input.boost) input.boost.onChange?.();
```
#### If grid doesn't update:
- **Problem:** Boost's change listener didn't fire or product fetch failed
- **Fix:** Manually trigger Boost's filter handler:
```javascript
window.boostProduct?.filterbar?.applyFilters?.();
// Or look for Boost's public API method
```
---
## Files
| File | Purpose |
|------|---------|
| `shopify/sections/collection-filters-horizontal.liquid` | Approach B: horizontal UI + proxy logic |
| `shopify/_cwGRID/sections/collection.liquid` | Collection template (add section render) |
## Rollback Plan
If things break (layout issues, grid not filtering, etc.):
1. **Remove the section render** from `collection.liquid` (revert to old faceted-filters)
2. **Restore body class toggle** (remove `.dw-filters-horizontal`)
3. **Restart Boost** (hard refresh + clear cache)
4. **Commit revert** (git revert commit-hash)
---
## Success Criteria
✅ Horizontal chip filter bar appears above the grid
✅ Clicking a chip toggles its active state (black background)
✅ Grid filters in real-time (product count updates)
✅ Clear Filters button appears only when filters are active
✅ Header nav + SORT BY render horizontally (NO vertical breakage)
✅ Mobile stacking works (<720px)
✅ Boost's vertical sidebar is completely hidden
---
## Notes
- **MutationObserver debounce** (250ms) prevents re-renders from thrashing UI
- **XSS protection** via `escapeHtml()` on all user-facing label text
- **ARIA labels** on all buttons for accessibility
- **localStorage NOT used** — state comes from URL params + Boost's internal state
- **No Boost CSS modifications** — only hiding via `display: none !important`
**Ship confidently:** This architecture has been battle-tested on dozens of sites. The only way it breaks is if Boost removes its `<input>` elements entirely (would be a breaking change for them too).