← back to Designer Wallcoverings
Add native-Liquid infinite scroll for theme 142250278963 (collection-list, blog, blog-masonry) + gated push script
e2ae1b4deeee49ecac59565d18553f80341fc7bf · 2026-06-22 15:18:46 -0700 · Steve
Files touched
A shopify/theme-infinite-scroll-142250278963/README.mdA shopify/theme-infinite-scroll-142250278963/assets/dw-native-infinite.jsA shopify/theme-infinite-scroll-142250278963/push-native-infinite-to-steves-v5.shA shopify/theme-infinite-scroll-142250278963/sections/blog-masonry.liquidA shopify/theme-infinite-scroll-142250278963/sections/blog.liquidA shopify/theme-infinite-scroll-142250278963/sections/collection-list.liquidA shopify/theme-infinite-scroll-142250278963/snippets/pagination.liquid
Diff
commit e2ae1b4deeee49ecac59565d18553f80341fc7bf
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jun 22 15:18:46 2026 -0700
Add native-Liquid infinite scroll for theme 142250278963 (collection-list, blog, blog-masonry) + gated push script
---
.../theme-infinite-scroll-142250278963/README.md | 59 ++++++++
.../assets/dw-native-infinite.js | 137 +++++++++++++++++
.../push-native-infinite-to-steves-v5.sh | 69 +++++++++
.../sections/blog-masonry.liquid | 96 ++++++++++++
.../sections/blog.liquid | 111 ++++++++++++++
.../sections/collection-list.liquid | 168 +++++++++++++++++++++
.../snippets/pagination.liquid | 26 ++++
7 files changed, 666 insertions(+)
diff --git a/shopify/theme-infinite-scroll-142250278963/README.md b/shopify/theme-infinite-scroll-142250278963/README.md
new file mode 100644
index 00000000..3a41c05d
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/README.md
@@ -0,0 +1,59 @@
+# Infinite scroll — theme "Steves Version 5.0" (142250278963)
+
+Goal: every grid/list surface on the dev theme infinite-scrolls.
+
+## Renderer split (audited 2026-06-22, read-only Admin Assets API)
+
+| Surface | Route/template | Renderer | Was | Now |
+|---|---|---|---|---|
+| Collection product grid | `templates/collection.json` (Boost `apps` block; native `collection` section renders only the description) | **Boost** | infinite (Boost global) | unchanged ✓ |
+| Search results | `templates/search.json` (native `template-search` `disabled:true`; Boost `apps` block) | **Boost** | infinite (Boost global) | unchanged ✓ |
+| Vendor / tag pages | render through `collection.json` | **Boost** | infinite (Boost global) | unchanged ✓ |
+| Boost belt-and-suspenders | `assets/boost-sd-custom.js` (hides `.boost-sd__pagination`, auto-clicks next on scroll) | Boost+JS | infinite | unchanged ✓ |
+| **/collections (list of collections)** | `templates/list-collections.json` → `sections/collection-list.liquid` | **native Liquid** `{% paginate %}` | numbered pager | **infinite (this change)** |
+| **Blog index** | `sections/blog.liquid` | native Liquid `{% paginate %}` | numbered pager | **infinite (this change)** |
+| **Blog (masonry)** | `sections/blog-masonry.liquid` | native Liquid `{% paginate %}` | numbered pager | **infinite (this change)** |
+| Related / "You may also like" | `sections/static-product-recommendations.liquid` (`limit:4`) | native, bounded | fixed 4 | n/a — bounded recommendation row |
+| Complementary products | `snippets/complementary-products.liquid` | native slider, `recommendations.products` | bounded slider | n/a — bounded cross-sell |
+| Recently viewed | `sections/recently-viewed.liquid` | native, bounded | bounded | n/a — bounded |
+| Home "Featured collection" ×4 | `sections/home-products.liquid` (`limit: cols×rows`) | native, bounded | fixed module | n/a — merchandised module |
+| Home collections / masonry / slideshow | `index.json` modules | native, bounded | fixed | n/a — merchandised modules |
+| Article comments | `sections/article.liquid` (`paginate article.comments by 10`) | native | numbered | n/a — comment thread, not a content grid |
+
+Boost's per-template embedded `paginationType` reads `"default"`, but the live
+effective behavior is `infinite_scroll` because Boost's **global dashboard
+setting** overrides the theme-snapshot value (confirmed live on /collections/new-arrivals
+by the main session). No theme template disables/overrides Boost back to numbered,
+so all Boost surfaces (collection/search/vendor/tag) are consistently infinite. ✓
+
+## What changed (native Liquid → infinite scroll)
+
+New reusable helper `assets/dw-native-infinite.js` — progressive enhancement.
+For any `[data-dw-infinite]` wrapper it reads `data-dw-infinite-next`
+(= `paginate.next.url`), fetches the next page, appends the items-container's
+children, then reads the next-next URL from the fetched page. Hides the numbered
+`<ul.pagination>` (kept in DOM as no-JS / SEO fallback; restored on fetch error).
+Uses `IntersectionObserver` with a scroll fallback, skips masonry sizer nodes.
+
+Sections wrapped with the markup contract + `<script>` include:
+- `sections/collection-list.liquid` — items `.dw-collections-grid` (linklist mode left unpaginated)
+- `sections/blog.liquid` — items `.blog-posts`
+- `sections/blog-masonry.liquid` — items `.blog-posts-masonry` (sizer skipped)
+
+`snippets/pagination.liquid` is unchanged (now emitted inside `[data-dw-infinite-pager]`).
+
+## Push (GATED — do not run autonomously; theme writes need Steve's go)
+
+```bash
+cd /Users/stevestudio2/Projects/Designer-Wallcoverings/shopify/theme-infinite-scroll-142250278963
+# theme-scoped token lives in DW-Agents/dw-agents/update-theme-quantity.js
+# (regex shpat_[a-f0-9]{32}) — export it, don't paste the literal here.
+THEME_TOKEN="$(grep -oE 'shpat_[a-f0-9]{32}' \
+ ../../DW-Agents/dw-agents/update-theme-quantity.js | head -1)" \
+ bash push-native-infinite-to-steves-v5.sh
+```
+
+Pushes 4 assets to theme **142250278963** only (hard-guarded against live
+143739584563). Backs up each asset (GET before PUT) to `backups/`, verifies
+(GET after PUT). Idempotent. Preview URLs + revert instructions printed on
+completion. NEVER publishes the theme.
diff --git a/shopify/theme-infinite-scroll-142250278963/assets/dw-native-infinite.js b/shopify/theme-infinite-scroll-142250278963/assets/dw-native-infinite.js
new file mode 100644
index 00000000..6a2f73b4
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/assets/dw-native-infinite.js
@@ -0,0 +1,137 @@
+/* DW Native Infinite Scroll — progressive enhancement for theme {% paginate %} grids.
+ *
+ * Converts the theme's native numbered-pager Liquid grids (collection-list /
+ * blog / blog-masonry) to append-on-scroll, WITHOUT touching the Boost AI
+ * Search & Discovery surfaces (collection/search product grids — those are
+ * handled by Boost's own global infinite_scroll + assets/boost-sd-custom.js).
+ *
+ * Markup contract (added by the section .liquid files):
+ * <div data-dw-infinite ← scroll container wrapper
+ * data-dw-infinite-items=".blog-posts" ← selector of the element whose
+ * CHILDREN are the cards
+ * data-dw-infinite-next="/blogs/news?page=2" ← next page URL (paginate.next.url)
+ * data-dw-infinite-page="1">
+ * <div class="blog-posts"> … cards … </div>
+ * <ul class="pagination" data-dw-infinite-pager> … numbered links … </ul> ← noscript fallback
+ * </div>
+ *
+ * Behavior: when the viewport nears the bottom, fetch the next URL, extract the
+ * same items-container from the returned HTML, append its children to the live
+ * items-container, then read the NEXT next-url from the fetched page. Stops when
+ * there is no further next URL. The numbered <ul.pagination> is hidden once JS
+ * takes over (kept in the DOM for no-JS users + SEO crawl).
+ */
+(function () {
+ 'use strict';
+
+ function initContainer(root) {
+ if (root.__dwInfiniteInit) return;
+ root.__dwInfiniteInit = true;
+
+ var itemsSel = root.getAttribute('data-dw-infinite-items');
+ var itemsEl = itemsSel ? root.querySelector(itemsSel) : root;
+ if (!itemsEl) return;
+
+ var nextUrl = root.getAttribute('data-dw-infinite-next') || '';
+ var loading = false;
+ var done = !nextUrl;
+
+ // Hide the numbered pager — JS is now driving. Kept in DOM as no-JS fallback.
+ var pager = root.querySelector('[data-dw-infinite-pager]');
+ if (pager) pager.setAttribute('hidden', 'hidden');
+
+ // Sentinel + loading indicator appended after the items container.
+ var sentinel = document.createElement('div');
+ sentinel.className = 'dw-infinite-sentinel';
+ sentinel.setAttribute('aria-hidden', 'true');
+ sentinel.style.cssText = 'min-height:1px;width:100%';
+ var spinner = document.createElement('div');
+ spinner.className = 'dw-infinite-loading';
+ spinner.style.cssText = 'display:none;text-align:center;padding:24px 0;font:600 11px/1 -apple-system,system-ui,sans-serif;letter-spacing:1.5px;text-transform:uppercase;color:#999';
+ spinner.textContent = 'Loading more…';
+ itemsEl.parentNode.insertBefore(spinner, itemsEl.nextSibling);
+ itemsEl.parentNode.insertBefore(sentinel, spinner.nextSibling);
+
+ function appendFrom(html, url) {
+ var doc = new DOMParser().parseFromString(html, 'text/html');
+ // Find the matching items container on the fetched page.
+ var src = null;
+ var fetchedRoot = doc.querySelector('[data-dw-infinite][data-dw-infinite-items="' + itemsSel + '"]') ||
+ doc.querySelector('[data-dw-infinite]');
+ if (fetchedRoot) src = itemsSel ? fetchedRoot.querySelector(itemsSel) : fetchedRoot;
+ if (!src) src = doc.querySelector(itemsSel || '*');
+ if (src) {
+ var frag = document.createDocumentFragment();
+ // Skip masonry sizer elements (they must stay single).
+ Array.prototype.slice.call(src.children).forEach(function (child) {
+ if (child.hasAttribute && (child.hasAttribute('data-masonry-sizer') ||
+ (child.classList && child.classList.contains('masonry-grid-sizer')))) return;
+ frag.appendChild(child.cloneNode(true));
+ });
+ itemsEl.appendChild(frag);
+ // Re-layout masonry if the theme's masonry lib is present.
+ if (window.dwRelayoutMasonry) { try { window.dwRelayoutMasonry(itemsEl); } catch (e) {} }
+ document.dispatchEvent(new CustomEvent('dw:infinite:appended', { detail: { container: itemsEl, url: url } }));
+ }
+ // Read the next-next url from the fetched root (or its pager).
+ var newNext = '';
+ if (fetchedRoot) newNext = fetchedRoot.getAttribute('data-dw-infinite-next') || '';
+ if (!newNext) {
+ var np = doc.querySelector('[data-dw-infinite-pager] .next a, [data-dw-infinite-pager] a[rel="next"]');
+ if (np) newNext = np.getAttribute('href') || '';
+ }
+ return newNext;
+ }
+
+ function loadMore() {
+ if (loading || done) return;
+ loading = true;
+ spinner.style.display = 'block';
+ fetch(nextUrl, { headers: { 'X-Requested-With': 'XMLHttpRequest' }, credentials: 'same-origin' })
+ .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })
+ .then(function (html) {
+ nextUrl = appendFrom(html, nextUrl);
+ if (!nextUrl) done = true;
+ })
+ .catch(function () {
+ // On any error, fall back to showing the numbered pager again.
+ done = true;
+ if (pager) pager.removeAttribute('hidden');
+ })
+ .then(function () {
+ loading = false;
+ spinner.style.display = 'none';
+ // If the new content didn't fill the viewport, keep loading.
+ if (!done) maybeLoad();
+ });
+ }
+
+ function maybeLoad() {
+ if (done || loading) return;
+ var rect = sentinel.getBoundingClientRect();
+ if (rect.top - window.innerHeight < 700) loadMore();
+ }
+
+ if ('IntersectionObserver' in window) {
+ var io = new IntersectionObserver(function (entries) {
+ if (entries.some(function (e) { return e.isIntersecting; })) loadMore();
+ }, { rootMargin: '700px 0px' });
+ io.observe(sentinel);
+ } else {
+ window.addEventListener('scroll', maybeLoad, { passive: true });
+ }
+ // Initial check in case the first page is short.
+ maybeLoad();
+ }
+
+ function initAll() {
+ var roots = document.querySelectorAll('[data-dw-infinite]');
+ for (var i = 0; i < roots.length; i++) initContainer(roots[i]);
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initAll);
+ } else {
+ initAll();
+ }
+})();
diff --git a/shopify/theme-infinite-scroll-142250278963/push-native-infinite-to-steves-v5.sh b/shopify/theme-infinite-scroll-142250278963/push-native-infinite-to-steves-v5.sh
new file mode 100755
index 00000000..a2d3ce6e
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/push-native-infinite-to-steves-v5.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+# Stage native-Liquid infinite scroll onto the UNPUBLISHED dev theme
+# "Steves Version 5.0" (142250278963) on designer-laboratory-sandbox.
+#
+# Converts the theme's three native {% paginate %} numbered-pager grids
+# (collection-list / blog / blog-masonry) to append-on-scroll infinite scroll.
+# Leaves the Boost AI Search & Discovery surfaces (collection/search PRODUCT
+# grids) untouched — those already infinite-scroll via Boost's global setting
+# + assets/boost-sd-custom.js. NEVER touches the live theme 143739584563.
+#
+# Each asset is backed up (GET before PUT) and verified (GET after PUT).
+# Idempotent: re-running only re-PUTs assets whose live value differs.
+#
+# THEME_TOKEN=shpat_xxx bash push-native-infinite-to-steves-v5.sh
+set -euo pipefail
+cd "$(dirname "$0")"
+STORE="designer-laboratory-sandbox.myshopify.com"; API="2024-10"
+TGT_THEME=142250278963 # Steves Version 5.0 (unpublished dev)
+LIVE_THEME=143739584563 # NewWall Template — NEVER write to this
+TOK="${THEME_TOKEN:?set THEME_TOKEN}"
+
+# Hard guard: refuse to ever target the live theme id.
+if [ "$TGT_THEME" = "$LIVE_THEME" ]; then echo "REFUSING: target == live theme"; exit 2; fi
+
+python3 - "$STORE" "$API" "$TOK" "$TGT_THEME" <<'PY'
+import sys,json,os,urllib.request,urllib.parse,datetime,time
+store,api,tok,tid=sys.argv[1:5]
+H={"X-Shopify-Access-Token":tok,"Content-Type":"application/json"}
+base=f"https://{store}/admin/api/{api}/themes/{tid}/assets.json"
+def get(key):
+ u=base+"?"+urllib.parse.urlencode({"asset[key]":key})
+ return json.load(urllib.request.urlopen(urllib.request.Request(u,headers=H)))["asset"]["value"]
+def put(key,val):
+ body=json.dumps({"asset":{"key":key,"value":val}}).encode()
+ r=urllib.request.urlopen(urllib.request.Request(base,data=body,method="PUT",headers=H))
+ return r.status
+
+# local file -> theme asset key
+FILES={
+ "assets/dw-native-infinite.js": "assets/dw-native-infinite.js",
+ "sections/collection-list.liquid": "sections/collection-list.liquid",
+ "sections/blog.liquid": "sections/blog.liquid",
+ "sections/blog-masonry.liquid": "sections/blog-masonry.liquid",
+}
+os.makedirs("backups", exist_ok=True)
+stamp=datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
+for lf,key in FILES.items():
+ want=open(lf,encoding="utf-8").read()
+ try: live=get(key)
+ except Exception: live=None
+ if live is not None:
+ bk=f"backups/{key.replace('/','_')}.{stamp}.bak"
+ open(bk,"w",encoding="utf-8").write(live)
+ print(f"backup: {bk} ({len(live)} bytes)")
+ if live==want:
+ print(f"skip : {key} (already current)"); continue
+ st=put(key,want)
+ time.sleep(0.6) # gentle rate
+ ver=get(key)
+ ok = ver==want
+ print(f"PUT : {key} HTTP {st} verify={'OK' if ok else 'MISMATCH'} ({len(want)} bytes)")
+ if not ok: print(f" *** verify mismatch on {key} — investigate"); sys.exit(1)
+
+print()
+print("DONE. Preview the native grids on the dev theme:")
+print(f" /collections -> https://www.designerwallcoverings.com/collections?preview_theme_id={tid}")
+print(f" /blogs/<blog-handle> -> https://www.designerwallcoverings.com/blogs/news?preview_theme_id={tid}")
+print(f"revert: PUT each backups/*.{stamp}.bak back to its key on theme {tid}")
+PY
diff --git a/shopify/theme-infinite-scroll-142250278963/sections/blog-masonry.liquid b/shopify/theme-infinite-scroll-142250278963/sections/blog-masonry.liquid
new file mode 100644
index 00000000..3ebc9d65
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/sections/blog-masonry.liquid
@@ -0,0 +1,96 @@
+<script
+ type="application/json"
+ data-section-type="static-blog-masonry"
+ data-section-id="{{ section.id }}"
+>
+</script>
+
+{% assign emphasis_first_post = false %}
+{% if section.settings.emphasis_first_post %}
+ {% assign emphasis_first_post = true %}
+{% endif %}
+
+<section>
+ <h1 class="page-title">{{ blog.title }}</h1>
+
+ {% render 'breadcrumbs' %}
+
+ <div class="blog-wrapper-masonry">
+ {% paginate blog.articles by section.settings.articles_per_page %}
+
+ {%- comment -%} DW infinite scroll: wrapper carries the next-page URL; dw-native-infinite.js appends on scroll (masonry sizer is skipped to avoid duplication). {%- endcomment -%}
+ <div
+ data-dw-infinite
+ data-dw-infinite-items=".blog-posts-masonry"
+ data-dw-infinite-next="{% if paginate.next %}{{ paginate.next.url }}{% endif %}"
+ data-dw-infinite-page="{{ paginate.current_page }}"
+ >
+ <div class="blog-posts-masonry masonry-grid" data-masonry-grid>
+ <div class="masonry-grid-sizer" data-masonry-sizer></div>
+
+ {% for article in blog.articles %}
+ {% assign is_emphasized = false %}
+
+ {%- if emphasis_first_post -%}
+ {% comment %}
+ Don't emphasize post if:
+ - is not first page
+ - if tags are active
+ - is not first in loop
+ {% endcomment %}
+ {%- if paginate.current_page == 1 and current_tags == blank and forloop.first -%}
+ {% assign is_emphasized = true %}
+ {%- endif -%}
+ {%- endif -%}
+
+ {%
+ render 'section-blog-post-masonry',
+ article: article,
+ is_emphasized: is_emphasized
+ %}
+ {% else %}
+ <div class="blog-post-masonry masonry-grid-item" data-masonry-item>
+ <span class="empty">
+ {{ 'blogs.blog.empty' | t }}
+ </span>
+ </div>
+
+ {% endfor %}
+ </div>
+
+ {% if paginate.previous or paginate.next %}
+ <div data-dw-infinite-pager>
+ {% render 'pagination', paginate: paginate %}
+ </div>
+ {% endif %}
+ </div>
+
+ {% endpaginate %}
+ </div>
+</section>
+
+<script src="{{ 'dw-native-infinite.js' | asset_url }}" defer></script>
+
+{% schema %}
+
+{
+ "name": "Blog",
+ "settings": [
+ {
+ "type": "range",
+ "id": "articles_per_page",
+ "label": "Posts per page",
+ "min": 3,
+ "max": 12,
+ "step": 3,
+ "default": 9
+ },
+ {
+ "type": "checkbox",
+ "id": "emphasis_first_post",
+ "label": "Emphasize first post"
+ }
+ ]
+}
+
+{% endschema %}
diff --git a/shopify/theme-infinite-scroll-142250278963/sections/blog.liquid b/shopify/theme-infinite-scroll-142250278963/sections/blog.liquid
new file mode 100644
index 00000000..b7cedbfb
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/sections/blog.liquid
@@ -0,0 +1,111 @@
+<script
+ type="application/json"
+ data-section-type="static-blog"
+ data-section-id="{{ section.id }}"
+>
+</script>
+
+<section
+ {% if section.settings.tag-filter %}data-tag-filter{% endif %}
+>
+
+ <h1 class="page-title">{{ blog.title }}</h1>
+
+ {% render 'breadcrumbs' %}
+
+ {% render 'section-blog-sidebar' %}
+
+ <div class="blog-wrapper">
+ {% paginate blog.articles by section.settings.articles_per_page %}
+
+ {%- comment -%} DW infinite scroll: wrapper carries the next-page URL; dw-native-infinite.js appends on scroll. {%- endcomment -%}
+ <div
+ data-dw-infinite
+ data-dw-infinite-items=".blog-posts"
+ data-dw-infinite-next="{% if paginate.next %}{{ paginate.next.url }}{% endif %}"
+ data-dw-infinite-page="{{ paginate.current_page }}"
+ >
+ <div class="blog-posts">
+ {% for article in blog.articles %}
+ {% render 'section-blog-post', article: article %}
+ {% else %}
+ <span class="empty">{{ 'blogs.blog.empty' | t }}</span>
+ {% endfor %}
+ </div>
+
+ {% if paginate.previous or paginate.next %}
+ <div data-dw-infinite-pager>
+ {% render 'pagination', paginate: paginate %}
+ </div>
+ {% endif %}
+ </div>
+
+ {% endpaginate %}
+
+ {%- for block in section.blocks -%}
+ {%- case block.type -%}
+ {%- when 'promotional-image' -%}
+ {% render 'block-blog-promo', block: block %}
+ {%- endcase -%}
+ {%- endfor -%}
+ </div>
+</section>
+
+<script src="{{ 'dw-native-infinite.js' | asset_url }}" defer></script>
+
+{% schema %}
+{
+ "name": "Blog",
+ "settings": [
+ {
+ "type": "range",
+ "id": "articles_per_page",
+ "label": "Posts per page",
+ "min": 2,
+ "max": 10,
+ "step": 1,
+ "default": 4
+ },
+ {
+ "type": "checkbox",
+ "id": "tag-filter",
+ "label": "Show tag filter"
+ },
+ {
+ "type": "checkbox",
+ "id": "rss-icon",
+ "label": "Show subscribe link"
+ }
+ ],
+ "blocks": [
+ {
+ "type": "promotional-image",
+ "name": "Promotional image",
+ "settings": [
+ {
+ "type": "image_picker",
+ "id": "image",
+ "label": "Image"
+ },
+ {
+ "type": "text",
+ "id": "heading",
+ "label": "Heading"
+ },
+ {
+ "type": "text",
+ "id": "subtitle",
+ "label": "Subheading"
+ },
+ {
+ "type": "url",
+ "id": "url",
+ "label": "Link"
+ }
+ ],
+ "limit": 1
+ }
+ ]
+}
+
+{% endschema %}
diff --git a/shopify/theme-infinite-scroll-142250278963/sections/collection-list.liquid b/shopify/theme-infinite-scroll-142250278963/sections/collection-list.liquid
new file mode 100644
index 00000000..10fe83e8
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/sections/collection-list.liquid
@@ -0,0 +1,168 @@
+<script
+ type="application/json"
+ data-section-id="{{ section.id }}"
+ data-section-type="static-collections-list"
+>
+</script>
+
+
+{% assign use_masonry = false %}
+
+{% if section.settings.layout == 'masonry' %}
+ {% assign use_masonry = true %}
+{% endif %}
+
+<section
+ class="collections-list-section {% unless use_masonry %}collections-list-grid{% endunless %}"
+ {% if use_masonry %}data-collection-masonry{% endif %}>
+ <h1 class="page-title">{{ 'collections.listing.header' | t }}</h1>
+
+ {% render 'breadcrumbs' %}
+
+ <!-- STICKY SHOP BY COLOR BAR -->
+ <div id="dw-color-bar" style="position:sticky;top:0;z-index:100;background:#FAF9F6;border-bottom:1px solid #e5e5e5;padding:12px 0;margin:0 -20px 24px;transition:box-shadow 0.3s;">
+ <div style="max-width:1200px;margin:0 auto;padding:0 16px;">
+ <p style="font-family:-apple-system,system-ui,sans-serif;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#999;margin:0 0 10px;text-align:center;">Shop by Color</p>
+ <div style="display:flex;flex-wrap:nowrap;overflow-x:auto;justify-content:center;gap:16px;scrollbar-width:none;-ms-overflow-style:none;padding:2px 0;">
+ <a href="/collections/blue-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#3B5998;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Blue</span></a>
+ <a href="/collections/green-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#4A7C59;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Green</span></a>
+ <a href="/collections/gold-wallpaper" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#C4995C;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Gold</span></a>
+ <a href="/collections/red-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#9B2335;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Red</span></a>
+ <a href="/collections/pink-wallcovering-2" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#D4768B;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Pink</span></a>
+ <a href="/collections/purple" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#6B3FA0;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Purple</span></a>
+ <a href="/collections/orange" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#D2691E;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Orange</span></a>
+ <a href="/collections/yellow-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#DAA520;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Yellow</span></a>
+ <a href="/collections/black" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#1a1a1a;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Black</span></a>
+ <a href="/collections/white-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#F5F5F0;margin:0 auto 4px;border:2px solid #ddd;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">White</span></a>
+ <a href="/collections/grey-wallcovering" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#808080;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Gray</span></a>
+ <a href="/collections/brown-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#6B4226;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Brown</span></a>
+ <a href="/collections/beige-cream-wallpaper-collection" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#C8B99A;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Beige</span></a>
+ <a href="/collections/grey-silver" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#C0C0C0,#E8E8E8);margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Silver</span></a>
+ <a href="/collections/metallic-wallpaper-collections" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#B8860B,#DAA520,#F0E68C);margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Metallic</span></a>
+ <a href="/collections/coral" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#FF6F61;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Coral</span></a>
+ <a href="/collections/turquoise" style="text-decoration:none;text-align:center;flex-shrink:0;"><div style="width:36px;height:36px;border-radius:50%;background:#40B5AD;margin:0 auto 4px;border:2px solid #eee;box-shadow:0 1px 4px rgba(0,0,0,0.08);"></div><span style="font-family:-apple-system,system-ui,sans-serif;font-size:10px;color:#666;">Teal</span></a>
+ </div>
+ </div>
+ </div>
+ <style>
+ #dw-color-bar::-webkit-scrollbar{display:none}
+ #dw-color-bar div::-webkit-scrollbar{display:none}
+ #dw-color-bar a:hover div{transform:scale(1.15);transition:transform 0.2s}
+ @media(max-width:768px){#dw-color-bar>div>div{justify-content:flex-start;padding:0 8px}}
+ </style>
+ <!-- END STICKY COLOR BAR -->
+
+ {% assign using_linklist = false %}
+ {% if linklists['all-collections'] and linklists['all-collections'].links.size > 0 %}
+ {% assign using_linklist = true %}
+ {% endif %}
+
+ {% assign collections_per_page = section.settings.collections_per_row | times: section.settings.collections_number_of_rows %}
+
+ <div class="collections-list-content">
+ {%- if using_linklist -%}
+ {%- comment -%} Linklist mode is unpaginated (all links on one page) — no infinite scroll needed. {%- endcomment -%}
+ <div
+ class="rows-of-{{ section.settings.collections_per_row }} {% if use_masonry %}masonry-grid{% endif %}"
+ {% if use_masonry %}data-masonry-grid{% endif %}
+ >
+ {%- if use_masonry -%}
+ <div class="masonry-grid-sizer" data-masonry-sizer></div>
+ {%- endif -%}
+ {% for link in linklists['all-collections'].links %}
+ {%
+ render 'collection-list-item',
+ collection_list_item: 'linklist',
+ link: link,
+ use_masonry: use_masonry,
+ description: section.settings.show_description
+ %}
+ {% endfor %}
+ </div>
+ {%- else -%}
+ {%- paginate collections by collections_per_page -%}
+ {%- comment -%} DW infinite scroll: wrapper carries the next-page URL; dw-native-infinite.js appends on scroll. {%- endcomment -%}
+ <div
+ data-dw-infinite
+ data-dw-infinite-items=".dw-collections-grid"
+ data-dw-infinite-next="{% if paginate.next %}{{ paginate.next.url }}{% endif %}"
+ data-dw-infinite-page="{{ paginate.current_page }}"
+ >
+ <div
+ class="dw-collections-grid rows-of-{{ section.settings.collections_per_row }} {% if use_masonry %}masonry-grid{% endif %}"
+ {% if use_masonry %}data-masonry-grid{% endif %}
+ >
+ {%- if use_masonry -%}
+ <div class="masonry-grid-sizer" data-masonry-sizer></div>
+ {%- endif -%}
+ {%- for collection in collections -%}
+ {%-
+ render 'collection-list-item',
+ collection_list_item: 'collection',
+ collection: collection,
+ use_masonry: use_masonry,
+ description: section.settings.show_description
+ -%}
+ {%- endfor -%}
+ </div>
+
+ <div data-dw-infinite-pager>
+ {% render 'pagination', paginate: paginate %}
+ </div>
+ </div>
+ {%- endpaginate -%}
+ {% endif %}
+ </div>
+</section>
+
+<script src="{{ 'dw-native-infinite.js' | asset_url }}" defer></script>
+
+{% schema %}
+{
+ "name": "Collection list page",
+ "settings": [
+ {
+ "type": "select",
+ "id": "layout",
+ "label": "Layout",
+ "options": [
+ {
+ "value": "default",
+ "label": "Default"
+ },
+ {
+ "value": "masonry",
+ "label": "Masonry"
+ }
+ ],
+ "default": "default"
+ },
+ {
+ "type": "checkbox",
+ "id": "show_description",
+ "label": "Show description",
+ "default": false
+ },
+ {
+ "type": "range",
+ "id": "collections_per_row",
+ "label": "Collections per row",
+ "min": 2,
+ "max": 4,
+ "step": 1,
+ "default": 3
+ },
+ {
+ "type": "range",
+ "id": "collections_number_of_rows",
+ "label": "Rows",
+ "min": 2,
+ "max": 6,
+ "step": 1,
+ "default": 6
+ }
+ ]
+}
+
+{% endschema %}
+
diff --git a/shopify/theme-infinite-scroll-142250278963/snippets/pagination.liquid b/shopify/theme-infinite-scroll-142250278963/snippets/pagination.liquid
new file mode 100644
index 00000000..d8edf52e
--- /dev/null
+++ b/shopify/theme-infinite-scroll-142250278963/snippets/pagination.liquid
@@ -0,0 +1,26 @@
+
+{% if paginate.previous or paginate.next %}
+ <ul class="pagination">
+
+ {% if paginate.previous %}
+ <li class="previous"><a href="{{ paginate.previous.url }}"></a></li>
+ {% endif %}
+
+ {% for part in paginate.parts %}
+ {% if part.is_link %}
+ <li>{{ part.title | link_to: part.url }}</li>
+ {% else %}
+ {% if part.title == '…' %}
+ <li class="ellipsis">{{ part.title }}</li>
+ {% else %}
+ <li class="active">{{ part.title | link_to: part.url }}</li>
+ {% endif %}
+ {% endif %}
+ {% endfor %}
+
+ {% if paginate.next %}
+ <li class="next"><a href="{{ paginate.next.url }}"></a></li>
+ {% endif %}
+ </ul>
+{% endif %}
+
← c05eddc1 Bucket B Scalamandre: roll-variant recovery script + run rep
·
back to Designer Wallcoverings
·
Generalize Bucket-B lander: bucketB-land-all.sh drains ALL s e8b5ae15 →