← back to Designer Wallcoverings
Sync 5 _cw* theme.liquid mirrors to v2 variant-button block (matches deployed dev theme) + add mirror-sync script
c33264743c651908cfba5ffc613c30e2542eb5ea · 2026-06-21 19:35:22 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
M shopify/_cwFIX/layout/theme.liquidM shopify/_cwLIVE/layout/theme.liquidM shopify/_cwLIVE2/layout/theme.liquidM shopify/_cwLIVE3/layout/theme.liquidM shopify/_cwVERIFY/layout/theme.liquidA shopify/sync-mirrors-variant-ux.py
Diff
commit c33264743c651908cfba5ffc613c30e2542eb5ea
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jun 21 19:35:22 2026 -0700
Sync 5 _cw* theme.liquid mirrors to v2 variant-button block (matches deployed dev theme) + add mirror-sync script
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
shopify/_cwFIX/layout/theme.liquid | 76 +++++++++++++++++---------
shopify/_cwLIVE/layout/theme.liquid | 76 +++++++++++++++++---------
shopify/_cwLIVE2/layout/theme.liquid | 76 +++++++++++++++++---------
shopify/_cwLIVE3/layout/theme.liquid | 76 +++++++++++++++++---------
shopify/_cwVERIFY/layout/theme.liquid | 76 +++++++++++++++++---------
shopify/sync-mirrors-variant-ux.py | 100 ++++++++++++++++++++++++++++++++++
6 files changed, 345 insertions(+), 135 deletions(-)
diff --git a/shopify/_cwFIX/layout/theme.liquid b/shopify/_cwFIX/layout/theme.liquid
index 24b6cb4a..c9b1ab8d 100644
--- a/shopify/_cwFIX/layout/theme.liquid
+++ b/shopify/_cwFIX/layout/theme.liquid
@@ -515,7 +515,7 @@
<!-- /DW -->
{% if template contains 'product' %}
-<!-- DW: variant option buttons — replace native dropdown with side-by-side name-only buttons (2026-06-21) -->
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
<style>
.dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
.dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
@@ -528,36 +528,58 @@
</style>
<script>
(function(){
- function build(){
- var sels=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
- Array.prototype.forEach.call(sels,function(sel){
- if(sel.dataset.dwBtnDone) return;
- if(!sel.options||sel.options.length<2){ sel.dataset.dwBtnDone='1'; return; }
- sel.dataset.dwBtnDone='1';
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
- Array.prototype.forEach.call(sel.options,function(opt){
- var b=document.createElement('button');
- b.type='button';
- b.textContent=(opt.text||'').trim(); /* NAME ONLY, dynamic per product */
- b.setAttribute('data-value',opt.value);
- b.setAttribute('aria-pressed', opt.selected?'true':'false');
- b.addEventListener('click',function(){
- sel.value=opt.value;
- sel.dispatchEvent(new Event('change',{bubbles:true}));
- Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){
- x.setAttribute('aria-pressed', x===b?'true':'false');
- });
- });
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
wrap.appendChild(b);
});
- var host=sel.closest('.select-wrapper')||sel.closest('.selector-wrapper')||sel.parentElement;
- host.classList.add('dw-hide-native');
- host.parentElement.insertBefore(wrap,host);
- });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
}
- function init(){ build(); setTimeout(build,400); setTimeout(build,1200); }
- if(document.readyState!=='loading') init();
- else document.addEventListener('DOMContentLoaded',init);
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
})();
</script>
<!-- /DW variant buttons -->
diff --git a/shopify/_cwLIVE/layout/theme.liquid b/shopify/_cwLIVE/layout/theme.liquid
index 274f332b..fe72ea3a 100644
--- a/shopify/_cwLIVE/layout/theme.liquid
+++ b/shopify/_cwLIVE/layout/theme.liquid
@@ -515,7 +515,7 @@
<!-- /DW -->
{% if template contains 'product' %}
-<!-- DW: variant option buttons — replace native dropdown with side-by-side name-only buttons (2026-06-21) -->
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
<style>
.dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
.dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
@@ -528,36 +528,58 @@
</style>
<script>
(function(){
- function build(){
- var sels=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
- Array.prototype.forEach.call(sels,function(sel){
- if(sel.dataset.dwBtnDone) return;
- if(!sel.options||sel.options.length<2){ sel.dataset.dwBtnDone='1'; return; }
- sel.dataset.dwBtnDone='1';
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
- Array.prototype.forEach.call(sel.options,function(opt){
- var b=document.createElement('button');
- b.type='button';
- b.textContent=(opt.text||'').trim(); /* NAME ONLY, dynamic per product */
- b.setAttribute('data-value',opt.value);
- b.setAttribute('aria-pressed', opt.selected?'true':'false');
- b.addEventListener('click',function(){
- sel.value=opt.value;
- sel.dispatchEvent(new Event('change',{bubbles:true}));
- Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){
- x.setAttribute('aria-pressed', x===b?'true':'false');
- });
- });
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
wrap.appendChild(b);
});
- var host=sel.closest('.select-wrapper')||sel.closest('.selector-wrapper')||sel.parentElement;
- host.classList.add('dw-hide-native');
- host.parentElement.insertBefore(wrap,host);
- });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
}
- function init(){ build(); setTimeout(build,400); setTimeout(build,1200); }
- if(document.readyState!=='loading') init();
- else document.addEventListener('DOMContentLoaded',init);
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
})();
</script>
<!-- /DW variant buttons -->
diff --git a/shopify/_cwLIVE2/layout/theme.liquid b/shopify/_cwLIVE2/layout/theme.liquid
index 8bd96f57..93f20a49 100644
--- a/shopify/_cwLIVE2/layout/theme.liquid
+++ b/shopify/_cwLIVE2/layout/theme.liquid
@@ -515,7 +515,7 @@
<!-- /DW -->
{% if template contains 'product' %}
-<!-- DW: variant option buttons — replace native dropdown with side-by-side name-only buttons (2026-06-21) -->
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
<style>
.dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
.dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
@@ -528,36 +528,58 @@
</style>
<script>
(function(){
- function build(){
- var sels=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
- Array.prototype.forEach.call(sels,function(sel){
- if(sel.dataset.dwBtnDone) return;
- if(!sel.options||sel.options.length<2){ sel.dataset.dwBtnDone='1'; return; }
- sel.dataset.dwBtnDone='1';
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
- Array.prototype.forEach.call(sel.options,function(opt){
- var b=document.createElement('button');
- b.type='button';
- b.textContent=(opt.text||'').trim(); /* NAME ONLY, dynamic per product */
- b.setAttribute('data-value',opt.value);
- b.setAttribute('aria-pressed', opt.selected?'true':'false');
- b.addEventListener('click',function(){
- sel.value=opt.value;
- sel.dispatchEvent(new Event('change',{bubbles:true}));
- Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){
- x.setAttribute('aria-pressed', x===b?'true':'false');
- });
- });
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
wrap.appendChild(b);
});
- var host=sel.closest('.select-wrapper')||sel.closest('.selector-wrapper')||sel.parentElement;
- host.classList.add('dw-hide-native');
- host.parentElement.insertBefore(wrap,host);
- });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
}
- function init(){ build(); setTimeout(build,400); setTimeout(build,1200); }
- if(document.readyState!=='loading') init();
- else document.addEventListener('DOMContentLoaded',init);
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
})();
</script>
<!-- /DW variant buttons -->
diff --git a/shopify/_cwLIVE3/layout/theme.liquid b/shopify/_cwLIVE3/layout/theme.liquid
index 4f1d09a0..15f2dd94 100644
--- a/shopify/_cwLIVE3/layout/theme.liquid
+++ b/shopify/_cwLIVE3/layout/theme.liquid
@@ -515,7 +515,7 @@
<!-- /DW -->
{% if template contains 'product' %}
-<!-- DW: variant option buttons — replace native dropdown with side-by-side name-only buttons (2026-06-21) -->
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
<style>
.dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
.dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
@@ -528,36 +528,58 @@
</style>
<script>
(function(){
- function build(){
- var sels=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
- Array.prototype.forEach.call(sels,function(sel){
- if(sel.dataset.dwBtnDone) return;
- if(!sel.options||sel.options.length<2){ sel.dataset.dwBtnDone='1'; return; }
- sel.dataset.dwBtnDone='1';
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
- Array.prototype.forEach.call(sel.options,function(opt){
- var b=document.createElement('button');
- b.type='button';
- b.textContent=(opt.text||'').trim(); /* NAME ONLY, dynamic per product */
- b.setAttribute('data-value',opt.value);
- b.setAttribute('aria-pressed', opt.selected?'true':'false');
- b.addEventListener('click',function(){
- sel.value=opt.value;
- sel.dispatchEvent(new Event('change',{bubbles:true}));
- Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){
- x.setAttribute('aria-pressed', x===b?'true':'false');
- });
- });
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
wrap.appendChild(b);
});
- var host=sel.closest('.select-wrapper')||sel.closest('.selector-wrapper')||sel.parentElement;
- host.classList.add('dw-hide-native');
- host.parentElement.insertBefore(wrap,host);
- });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
}
- function init(){ build(); setTimeout(build,400); setTimeout(build,1200); }
- if(document.readyState!=='loading') init();
- else document.addEventListener('DOMContentLoaded',init);
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
})();
</script>
<!-- /DW variant buttons -->
diff --git a/shopify/_cwVERIFY/layout/theme.liquid b/shopify/_cwVERIFY/layout/theme.liquid
index 24b6cb4a..c9b1ab8d 100644
--- a/shopify/_cwVERIFY/layout/theme.liquid
+++ b/shopify/_cwVERIFY/layout/theme.liquid
@@ -515,7 +515,7 @@
<!-- /DW -->
{% if template contains 'product' %}
-<!-- DW: variant option buttons — replace native dropdown with side-by-side name-only buttons (2026-06-21) -->
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
<style>
.dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
.dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
@@ -528,36 +528,58 @@
</style>
<script>
(function(){
- function build(){
- var sels=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
- Array.prototype.forEach.call(sels,function(sel){
- if(sel.dataset.dwBtnDone) return;
- if(!sel.options||sel.options.length<2){ sel.dataset.dwBtnDone='1'; return; }
- sel.dataset.dwBtnDone='1';
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
- Array.prototype.forEach.call(sel.options,function(opt){
- var b=document.createElement('button');
- b.type='button';
- b.textContent=(opt.text||'').trim(); /* NAME ONLY, dynamic per product */
- b.setAttribute('data-value',opt.value);
- b.setAttribute('aria-pressed', opt.selected?'true':'false');
- b.addEventListener('click',function(){
- sel.value=opt.value;
- sel.dispatchEvent(new Event('change',{bubbles:true}));
- Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){
- x.setAttribute('aria-pressed', x===b?'true':'false');
- });
- });
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
wrap.appendChild(b);
});
- var host=sel.closest('.select-wrapper')||sel.closest('.selector-wrapper')||sel.parentElement;
- host.classList.add('dw-hide-native');
- host.parentElement.insertBefore(wrap,host);
- });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
}
- function init(){ build(); setTimeout(build,400); setTimeout(build,1200); }
- if(document.readyState!=='loading') init();
- else document.addEventListener('DOMContentLoaded',init);
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
})();
</script>
<!-- /DW variant buttons -->
diff --git a/shopify/sync-mirrors-variant-ux.py b/shopify/sync-mirrors-variant-ux.py
new file mode 100644
index 00000000..9cd26534
--- /dev/null
+++ b/shopify/sync-mirrors-variant-ux.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# Sync the 5 local _cw* theme.liquid mirrors to the v2 variant-button block
+# (matches what push-variant-ux-to-dev-theme.sh deploys to the dev theme).
+import re, sys, pathlib
+
+MIRRORS = ["_cwLIVE", "_cwLIVE2", "_cwLIVE3", "_cwFIX", "_cwVERIFY"]
+BASE = pathlib.Path(__file__).parent
+
+BLOCK = r"""
+{% if template contains 'product' %}
+<!-- DW: variant option buttons v2 — side-by-side name-only buttons; Sample button on every SKU (2026-06-21) -->
+<style>
+ .dw-variant-btns{display:flex;gap:10px;margin:10px 0 4px;flex-wrap:wrap}
+ .dw-variant-btns button{flex:1 1 0;min-width:120px;padding:12px 14px;border:1px solid #111;
+ background:#fff;color:#111;font:inherit;letter-spacing:.04em;text-transform:uppercase;
+ font-size:12px;cursor:pointer;border-radius:2px;transition:background .12s,color .12s}
+ .dw-variant-btns button:hover{background:#f2f2f2}
+ .dw-variant-btns button[aria-pressed="true"],
+ .dw-variant-btns button[aria-pressed="true"]:hover{background:#111;color:#fff}
+ .dw-hide-native{display:none !important}
+</style>
+<script>
+(function(){
+ function nameOnly(t){ return (t||'').split(/\s+[-–]\s+\$/)[0].replace(/\s+/g,' ').trim(); }
+ function press(wrap,btn){ Array.prototype.forEach.call(wrap.querySelectorAll('button'),function(x){x.setAttribute('aria-pressed', x===btn?'true':'false');}); }
+ function hostOf(el){ return el.closest('.select-wrapper')||el.closest('.selector-wrapper')||el.parentElement; }
+ function mk(sel, onClick){
+ if(sel.dataset.dwBtnDone||!sel.options||sel.options.length<2) return false;
+ sel.dataset.dwBtnDone='1';
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ Array.prototype.forEach.call(sel.options,function(opt){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=nameOnly(opt.text); b.setAttribute('data-value',opt.value);
+ b.setAttribute('aria-pressed', opt.selected?'true':'false');
+ b.addEventListener('click',function(){ onClick(sel,opt); press(wrap,b); });
+ wrap.appendChild(b);
+ });
+ var host=hostOf(sel); host.classList.add('dw-hide-native'); host.parentElement.insertBefore(wrap,host);
+ return true;
+ }
+ function isSampleVar(v){ return /(^|[-_ ])sample\b/i.test(v.sku||'') || /sample/i.test(v.title||''); }
+ function fallbackFromJson(){
+ if(window.__dwVbDone||document.querySelector('.dw-variant-btns')) return; window.__dwVbDone=1;
+ fetch(location.pathname.split('?')[0]+'.js').then(function(r){return r.json();}).then(function(p){
+ if(document.querySelector('.dw-variant-btns')||!p||!p.variants||!p.variants.length) return;
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ var atc=document.querySelector('button[name="add"],[type="submit"][name="add"]')||
+ [].slice.call(document.querySelectorAll('button,input[type=submit]')).filter(function(b){return /add to cart/i.test(b.textContent||b.value||'');})[0];
+ var qty=document.querySelector('input[name="quantity"],input[type="number"]');
+ var anchor=(idSel&&hostOf(idSel))|| (qty&&(qty.closest('.product-form__input,.selector-wrapper,div')||qty)) || atc || document.querySelector('form[action*="/cart"]');
+ if(!anchor) return;
+ var wrap=document.createElement('div'); wrap.className='dw-variant-btns';
+ p.variants.forEach(function(v){
+ var b=document.createElement('button'); b.type='button';
+ b.textContent=isSampleVar(v)?'Sample':nameOnly(v.title||v.name||'');
+ b.setAttribute('aria-pressed', String(p.variants.length===1));
+ b.addEventListener('click',function(){ var u=new URL(location.href); u.searchParams.set('variant',v.id); location.href=u.toString(); });
+ wrap.appendChild(b);
+ });
+ if(idSel) hostOf(idSel).classList.add('dw-hide-native');
+ if(anchor.parentElement) anchor.parentElement.insertBefore(wrap,anchor); else anchor.appendChild(wrap);
+ }).catch(function(){});
+ }
+ function build(){
+ if(location.pathname.indexOf('/products/')===-1) return;
+ var rows=document.querySelectorAll('.single-option-selector,[id^="single-option-"]');
+ if(rows.length){ Array.prototype.forEach.call(rows,function(sel){ mk(sel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true})); }); }); }
+ if(!document.querySelector('.dw-variant-btns')){
+ var idSel=document.querySelector('select[name="id"],[id^="product-select-"]');
+ if(idSel) mk(idSel,function(s,opt){ s.value=opt.value; s.dispatchEvent(new Event('change',{bubbles:true}));
+ try{ var u=new URL(location.href); u.searchParams.set('variant',opt.value); history.replaceState({},'',u); }catch(e){} });
+ }
+ }
+ function init(){ build(); setTimeout(build,400); setTimeout(function(){ build(); fallbackFromJson(); },1200); }
+ if(document.readyState!=='loading') init(); else document.addEventListener('DOMContentLoaded',init);
+})();
+</script>
+<!-- /DW variant buttons -->
+{% endif %}
+"""
+
+STRIP = re.compile(r"\n*\{%-?\s*if template contains 'product'\s*-?%\}.*?<!-- /DW variant buttons -->\s*\{%-?\s*endif\s*-?%\}\n*", re.S)
+
+for m in MIRRORS:
+ f = BASE / m / "layout" / "theme.liquid"
+ if not f.exists():
+ print(f"skip {m} (no theme.liquid)"); continue
+ src = f.read_text()
+ orig = src
+ src = STRIP.sub("\n", src) # remove any prior block (v1/v2)
+ if "<!-- /DW -->" in src:
+ src = src.replace("<!-- /DW -->", "<!-- /DW -->\n" + BLOCK, 1)
+ elif "</head>" in src:
+ src = src.replace("</head>", BLOCK + "\n</head>", 1)
+ else:
+ print(f"WARN {m}: no anchor"); continue
+ if src != orig:
+ f.write_text(src); print(f"synced {m}: v2 block ({src.count('dw-variant-btns')} markers)")
+ else:
+ print(f"{m}: already v2")
← a048effe Variant buttons v2: Sample button guaranteed on every SKU (J
·
back to Designer Wallcoverings
·
auto-save: 2026-06-21T20:01:45 (1 files) — shopify/scripts/d 7f24f962 →