Advanced Collection Filters
Sidebar filters with active chips and mobile drawer. Replaces Boost AI.
Live preview
See it in action.
Fully interactive, drag, click, scroll inside the frame, toggle to mobile.
About this section
A complete filterable collection-page layout that uses Shopify's native storefront filtering (no third-party search index). Auto-renders filter groups for any facet you've set up in Online Store, Navigation, Filters. Active selections appear as removable chips above the grid. Mobile gets a slide-in drawer. Pure Liquid + native URL filtering, no SDK required.
Install in 90 seconds
- 01
Create /sections/modblo-advanced-collection-filters.liquid.
- 02
Paste the section code and save.
- 03
Add the section to collection.json (it will replace the default product grid).
- 04
In Shopify Admin, go to Online Store, Navigation, Filters and configure which facets you want exposed (color, size, price, vendor, etc.).
- 05
(Optional) Wrap the section in a {% paginate collection.products by 24 %}, {% endpaginate %} block in collection.liquid for built-in pagination.
The Liquid
{%- comment -%}
modblo. Advanced Collection Filters
Replaces Boost AI Search & Discovery / Searchanise / Smart Product Filter.
Sidebar filter UI for collection pages with:
- Auto-detected filters from collection.filters (price, vendor, type, tags)
- Active-filter chips with one-click remove
- Mobile drawer (slides in from left)
- Real-time count per facet
- Pure Liquid + Shopify's native /collections/all/filter+url query strings
Works with Shopify's storefront filtering, no third-party search index needed.
{%- endcomment -%}
{%- if collection -%}
<section class="modblo-acf" data-modblo-acf
data-section-id="{{ section.id }}"
style="--modblo-acf-bg: {{ section.settings.bg }};
--modblo-acf-fg: {{ section.settings.fg }};
--modblo-acf-accent: {{ section.settings.accent }};">
<div class="modblo-acf__inner page-width">
<header class="modblo-acf__head">
<div>
<h1 class="modblo-acf__h">{{ collection.title }}</h1>
<p class="modblo-acf__count"><span data-modblo-acf-count>{{ collection.products_count }}</span> products</p>
</div>
<button type="button" class="modblo-acf__mobile-btn" data-modblo-acf-toggle aria-label="Open filters">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<line x1="4" y1="6" x2="20" y2="6"/>
<line x1="6" y1="12" x2="18" y2="12"/>
<line x1="9" y1="18" x2="15" y2="18"/>
</svg>
Filter
</button>
</header>
{%- comment -%} Active filter chips {%- endcomment -%}
{%- assign has_active = false -%}
{%- for f in collection.filters -%}
{%- for v in f.active_values -%}{%- assign has_active = true -%}{%- endfor -%}
{%- endfor -%}
{%- if has_active -%}
<div class="modblo-acf__active">
{%- for f in collection.filters -%}
{%- for v in f.active_values -%}
<a href="{{ v.url_to_remove }}" class="modblo-acf__chip">
{{ f.label }}: {{ v.label }}
<span class="modblo-acf__chip-x" aria-hidden="true">×</span>
</a>
{%- endfor -%}
{%- endfor -%}
<a href="{{ collection.url }}" class="modblo-acf__clear">Clear all</a>
</div>
{%- endif -%}
<div class="modblo-acf__layout">
{%- comment -%} Sidebar filters {%- endcomment -%}
<aside class="modblo-acf__sidebar" data-modblo-acf-sidebar>
<div class="modblo-acf__sidebar-head">
<p class="modblo-acf__sidebar-title">Filters</p>
<button type="button" class="modblo-acf__sidebar-close" data-modblo-acf-close aria-label="Close filters">×</button>
</div>
{%- for f in collection.filters -%}
<details class="modblo-acf__group" open>
<summary class="modblo-acf__group-summary">
{{ f.label }}
<svg class="modblo-acf__chevron" viewBox="0 0 12 12" width="10" height="10" aria-hidden="true">
<path d="M3 4.5l3 3 3-3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</summary>
{%- case f.type -%}
{%- when 'list' -%}
<ul class="modblo-acf__opts">
{%- for v in f.values -%}
<li>
<a href="{% if v.active %}{{ v.url_to_remove }}{% else %}{{ v.url_to_add }}{% endif %}"
class="modblo-acf__opt {% if v.active %}is-active{% endif %} {% if v.count == 0 and v.active == false %}is-empty{% endif %}">
<span class="modblo-acf__opt-check" aria-hidden="true">
<svg viewBox="0 0 12 12" width="10" height="10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 6l3 3 5-6"/></svg>
</span>
<span class="modblo-acf__opt-label">{{ v.label }}</span>
<span class="modblo-acf__opt-count">({{ v.count }})</span>
</a>
</li>
{%- endfor -%}
</ul>
{%- when 'price_range' -%}
<form class="modblo-acf__price" method="get" action="{{ collection.url }}">
{%- for active in collection.filters -%}
{%- unless active.param_name == f.min_value.param_name or active.param_name == f.max_value.param_name -%}
{%- for v in active.active_values -%}
<input type="hidden" name="{{ active.param_name }}" value="{{ v.value }}">
{%- endfor -%}
{%- endunless -%}
{%- endfor -%}
<div class="modblo-acf__price-inputs">
<label>
<span>From</span>
<input type="number" name="{{ f.min_value.param_name }}"
value="{{ f.min_value.value }}"
placeholder="{{ f.range_min | money_without_currency | strip_html }}"
min="0">
</label>
<label>
<span>To</span>
<input type="number" name="{{ f.max_value.param_name }}"
value="{{ f.max_value.value }}"
placeholder="{{ f.range_max | money_without_currency | strip_html }}"
min="0">
</label>
</div>
<button type="submit" class="modblo-acf__price-apply">Apply</button>
</form>
{%- endcase -%}
</details>
{%- endfor -%}
</aside>
{%- comment -%} Product grid {%- endcomment -%}
<div class="modblo-acf__grid">
{%- if collection.products.size == 0 -%}
<div class="modblo-acf__empty">
<p class="modblo-acf__empty-title">No products match.</p>
<p>Try removing a filter or two.</p>
<a href="{{ collection.url }}" class="modblo-acf__empty-cta">Reset filters</a>
</div>
{%- else -%}
{%- for product in collection.products -%}
<a class="modblo-acf__product" href="{{ product.url }}">
{%- if product.featured_image -%}
{{ product.featured_image | image_url: width: 480 | image_tag:
loading: 'lazy', widths: '240,480,720', sizes: '(max-width:768px) 50vw, 240px',
class: 'modblo-acf__product-img' }}
{%- endif -%}
<p class="modblo-acf__product-vendor">{{ product.vendor }}</p>
<p class="modblo-acf__product-title">{{ product.title }}</p>
<p class="modblo-acf__product-price">
{%- if product.compare_at_price > product.price -%}
<s>{{ product.compare_at_price | money }}</s>
{%- endif -%}
<strong>{{ product.price | money }}</strong>
</p>
</a>
{%- endfor -%}
{%- endif -%}
</div>
</div>
{%- if paginate.pages > 1 -%}
<nav class="modblo-acf__pages" aria-label="Pagination">
{{ paginate | default_pagination }}
</nav>
{%- endif -%}
</div>
<div class="modblo-acf__overlay" data-modblo-acf-close></div>
</section>
<style>
.modblo-acf { background: var(--modblo-acf-bg, #fff); color: var(--modblo-acf-fg, #0b0b0c); padding: clamp(32px, 5vw, 64px) 0; }
.modblo-acf__inner { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
.modblo-acf__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.modblo-acf__h { font-size: clamp(24px, 3vw, 32px); letter-spacing: -.02em; margin: 0 0 4px; }
.modblo-acf__count { font-size: 13px; opacity: .6; margin: 0; }
.modblo-acf__mobile-btn {
display: none; align-items: center; gap: 8px;
background: transparent; color: inherit; cursor: pointer;
border: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 15%, transparent);
padding: 9px 14px; border-radius: 10px;
font-size: 13px; font-weight: 500;
}
.modblo-acf__active {
display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
margin-bottom: 24px; padding-bottom: 24px;
border-bottom: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 8%, transparent);
}
.modblo-acf__chip {
display: inline-flex; align-items: center; gap: 6px;
background: color-mix(in oklab, var(--modblo-acf-fg) 5%, transparent);
color: inherit; text-decoration: none;
padding: 5px 10px; border-radius: 999px;
font-size: 12px; font-weight: 500;
transition: background .2s;
}
.modblo-acf__chip:hover { background: color-mix(in oklab, var(--modblo-acf-fg) 10%, transparent); }
.modblo-acf__chip-x { font-size: 14px; line-height: 1; opacity: .6; }
.modblo-acf__clear {
color: var(--modblo-acf-accent, #6366f1); font-size: 12px; font-weight: 600;
text-decoration: underline; text-underline-offset: 3px; padding: 5px 8px;
}
.modblo-acf__layout { display: grid; grid-template-columns: 240px 1fr; gap: 32px; align-items: start; }
.modblo-acf__sidebar {
background: var(--modblo-acf-bg, #fff);
position: sticky; top: 24px;
}
.modblo-acf__sidebar-head { display: none; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 8%, transparent); }
.modblo-acf__sidebar-title { font-size: 15px; font-weight: 600; margin: 0; }
.modblo-acf__sidebar-close { background: transparent; border: 0; color: inherit; cursor: pointer; font-size: 22px; line-height: 1; padding: 4px 8px; }
.modblo-acf__group {
border-bottom: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 8%, transparent);
padding: 14px 0;
}
.modblo-acf__group:last-child { border-bottom: 0; }
.modblo-acf__group-summary {
display: flex; align-items: center; justify-content: space-between;
cursor: pointer; font-size: 13px; font-weight: 600;
text-transform: uppercase; letter-spacing: .08em;
list-style: none;
}
.modblo-acf__group-summary::-webkit-details-marker { display: none; }
.modblo-acf__chevron { opacity: .55; transition: transform .2s; }
.modblo-acf__group[open] .modblo-acf__chevron { transform: rotate(180deg); }
.modblo-acf__opts { list-style: none; padding: 12px 0 0; margin: 0; display: flex; flex-direction: column; gap: 4px; }
.modblo-acf__opt {
display: flex; align-items: center; gap: 8px;
color: inherit; text-decoration: none;
font-size: 13px; padding: 6px 4px; border-radius: 6px;
transition: background .15s;
}
.modblo-acf__opt:hover { background: color-mix(in oklab, var(--modblo-acf-fg) 4%, transparent); }
.modblo-acf__opt-check {
width: 16px; height: 16px; border-radius: 4px;
border: 1.5px solid color-mix(in oklab, var(--modblo-acf-fg) 25%, transparent);
display: grid; place-items: center; flex-shrink: 0;
color: transparent;
}
.modblo-acf__opt.is-active .modblo-acf__opt-check {
background: var(--modblo-acf-accent, #6366f1);
border-color: var(--modblo-acf-accent, #6366f1);
color: #fff;
}
.modblo-acf__opt-label { flex: 1; }
.modblo-acf__opt-count { opacity: .5; font-variant-numeric: tabular-nums; }
.modblo-acf__opt.is-empty { opacity: .35; pointer-events: none; }
.modblo-acf__price { padding-top: 12px; }
.modblo-acf__price-inputs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 10px; }
.modblo-acf__price label { display: flex; flex-direction: column; gap: 4px; font-size: 11px; opacity: .7; text-transform: uppercase; letter-spacing: .08em; }
.modblo-acf__price input {
background: transparent; color: inherit;
border: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 15%, transparent);
border-radius: 8px; padding: 8px 10px; font-size: 13px;
}
.modblo-acf__price-apply {
width: 100%; padding: 9px;
background: var(--modblo-acf-fg, #0b0b0c); color: var(--modblo-acf-bg, #fff);
border: 0; border-radius: 8px;
font-size: 12px; font-weight: 600; cursor: pointer;
}
.modblo-acf__grid {
display: grid; gap: 18px;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.modblo-acf__product { color: inherit; text-decoration: none; transition: transform .2s; }
.modblo-acf__product:hover { transform: translateY(-2px); }
.modblo-acf__product-img {
width: 100%; aspect-ratio: 4/5; object-fit: cover;
border-radius: 12px; margin-bottom: 10px;
background: color-mix(in oklab, var(--modblo-acf-fg) 4%, transparent);
}
.modblo-acf__product-vendor { font-size: 11px; opacity: .6; text-transform: uppercase; letter-spacing: .08em; margin: 0 0 4px; }
.modblo-acf__product-title { font-size: 14px; font-weight: 600; margin: 0 0 4px; line-height: 1.3; }
.modblo-acf__product-price { font-size: 13px; margin: 0; display: flex; gap: 6px; align-items: baseline; }
.modblo-acf__product-price s { opacity: .5; }
.modblo-acf__empty { grid-column: 1 / -1; text-align: center; padding: 80px 24px; }
.modblo-acf__empty-title { font-size: 18px; font-weight: 600; margin: 0 0 6px; }
.modblo-acf__empty-cta {
display: inline-block; margin-top: 16px; padding: 10px 18px;
background: var(--modblo-acf-fg, #0b0b0c); color: var(--modblo-acf-bg, #fff);
border-radius: 10px; text-decoration: none; font-size: 13px; font-weight: 600;
}
.modblo-acf__pages { margin-top: 32px; padding-top: 24px; border-top: 1px solid color-mix(in oklab, var(--modblo-acf-fg) 8%, transparent); text-align: center; }
.modblo-acf__overlay {
display: none; position: fixed; inset: 0;
background: rgba(0,0,0,.5); z-index: 49;
}
.modblo-acf.is-mobile-open .modblo-acf__overlay { display: block; }
@media (max-width: 768px) {
.modblo-acf__layout { grid-template-columns: 1fr; }
.modblo-acf__mobile-btn { display: inline-flex; }
.modblo-acf__sidebar {
position: fixed; top: 0; left: 0; bottom: 0;
width: min(320px, 85vw); z-index: 50;
transform: translateX(-100%); transition: transform .3s cubic-bezier(.16,1,.3,1);
overflow-y: auto; padding: 0 20px 24px;
box-shadow: 8px 0 32px -12px rgba(0,0,0,.3);
}
.modblo-acf.is-mobile-open .modblo-acf__sidebar { transform: translateX(0); }
.modblo-acf__sidebar-head { display: flex; }
}
</style>
<script>
(function () {
var root = document.querySelector('[data-modblo-acf][data-section-id="{{ section.id }}"]');
if (!root) return;
var openBtn = root.querySelector('[data-modblo-acf-toggle]');
var closeEls = root.querySelectorAll('[data-modblo-acf-close]');
if (openBtn) {
openBtn.addEventListener('click', function () {
root.classList.add('is-mobile-open');
document.body.style.overflow = 'hidden';
});
}
closeEls.forEach(function (el) {
el.addEventListener('click', function () {
root.classList.remove('is-mobile-open');
document.body.style.overflow = '';
});
});
})();
</script>
{%- endif -%}
{% schema %}
{
"name": "Collection Filters",
"tag": "section",
"settings": [
{ "type": "header", "content": "Colors" },
{ "type": "color", "id": "bg", "label": "Background", "default": "#ffffff" },
{ "type": "color", "id": "fg", "label": "Foreground", "default": "#0b0b0c" },
{ "type": "color", "id": "accent", "label": "Accent", "default": "#6366f1" }
],
"presets": [{ "name": "Collection Filters" }]
}
{% endschema %}Unlock the section code
Advanced Collection Filters is a premium section. Get the full Liquid + scoped CSS paste-ready.
One-time purchase · Lifetime updates · You own the code
Theme editor settings
| Setting | Type | Default |
|---|---|---|
Background bg | color | #ffffff |
Foreground fg | color | #0b0b0c |
Accent accent | color | #6366f1 |
SEO & accessibility notes
- Uses Shopify's native /collections/all/filter+url querystrings, every filter state is a fully crawlable URL.
- Real <a> elements per filter option, no JavaScript required to filter (the section is fully functional with JS disabled).
- Mobile drawer uses a single is-mobile-open class toggle, no animation library.
- Native <details> elements for each filter group, accordion behavior comes free.
Related
