[object Object]

← back to Designer Wallcoverings

DW PDP restyle: size-select -> pills + uniform CTA pill layout (theme-5.0-duplicate)

6b98ef31b1df651c5d4ab48a974d693bc6d06736 · 2026-06-22 17:09:36 -0700 · Steve

Progressive enhancement: native single-option-selector kept (visually hidden)
so variant/cart/cowlendar/MOQ JS unchanged; pills set value + dispatch native
change. CTAs relaid into Request Sample + Spec Sheet row / Add to Cart below.
Global include in layout/theme.liquid next to dw-card-hover.js.

Files touched

Diff

commit 6b98ef31b1df651c5d4ab48a974d693bc6d06736
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Jun 22 17:09:36 2026 -0700

    DW PDP restyle: size-select -> pills + uniform CTA pill layout (theme-5.0-duplicate)
    
    Progressive enhancement: native single-option-selector kept (visually hidden)
    so variant/cart/cowlendar/MOQ JS unchanged; pills set value + dispatch native
    change. CTAs relaid into Request Sample + Spec Sheet row / Add to Cart below.
    Global include in layout/theme.liquid next to dw-card-hover.js.
---
 .../theme-5.0-duplicate/assets/dw-pdp-restyle.css  | 160 +++++++++++++++++++++
 .../assets/dw-pdp-size-pills.js                    | 110 ++++++++++++++
 shopify/theme-5.0-duplicate/layout/theme.liquid    |   3 +
 3 files changed, 273 insertions(+)

diff --git a/shopify/theme-5.0-duplicate/assets/dw-pdp-restyle.css b/shopify/theme-5.0-duplicate/assets/dw-pdp-restyle.css
new file mode 100644
index 00000000..dd09d136
--- /dev/null
+++ b/shopify/theme-5.0-duplicate/assets/dw-pdp-restyle.css
@@ -0,0 +1,160 @@
+/* ==========================================================================
+   dw-pdp-restyle.css  (Designer Wallcoverings PDP restyle, 2026-06-22)
+   Reference-design pass on the product page:
+     - SIZE selector -> two pill buttons (progressive enhancement; the native
+       <select.single-option-selector> stays in the DOM, visually hidden, so
+       all variant / cart / cowlendar / MOQ JS keeps reading it).
+     - REQUEST SAMPLE / SPEC SHEET / ADD TO CART -> three uniform rounded
+       pills, laid out: [Request Sample] [Spec Sheet] on one row, Add to Cart
+       below. Existing hrefs/handlers/classes untouched; restyle/relayout only.
+   Scoped to .template-product so nothing leaks to other templates.
+   ========================================================================== */
+
+/* --- Token: keep pill radius/font consistent across size pills + CTAs ------ */
+:root {
+  --dw-pill-radius: 99px;
+  --dw-pill-font: Lora, serif;
+}
+
+/* ==========================================================================
+   1. SIZE PILLS
+   ========================================================================== */
+
+/* Hide the native size <select> but KEEP it operable for JS (.value/.dispatch).
+   We only hide it once our pills have been built (data-dw-pilled="1"), so on a
+   no-JS render the original dropdown is still usable. */
+.template-product select.single-option-selector[data-dw-pilled="1"] {
+  position: absolute !important;
+  width: 1px !important;
+  height: 1px !important;
+  padding: 0 !important;
+  margin: -1px !important;
+  overflow: hidden !important;
+  clip: rect(0 0 0 0) !important;
+  white-space: nowrap !important;
+  border: 0 !important;
+}
+
+.template-product .dw-size-pills {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin: 6px 0 18px;
+}
+
+.template-product .dw-size-pill {
+  font-family: var(--dw-pill-font);
+  font-weight: 700;
+  font-size: 12px;
+  letter-spacing: 0.02em;
+  text-transform: uppercase;
+  line-height: 1.1;
+  cursor: pointer;
+  padding: 14px 22px;
+  min-height: 46px;
+  border-radius: var(--dw-pill-radius);
+  border: 1.5px solid #111;
+  background: #fff;
+  color: #111;
+  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
+  -webkit-appearance: none;
+  appearance: none;
+}
+
+.template-product .dw-size-pill:hover {
+  background: #f2f2f2;
+}
+
+.template-product .dw-size-pill.is-active {
+  background: #111;
+  color: #fff;
+  border-color: #111;
+}
+
+.template-product .dw-size-pill.is-active:hover {
+  background: #000;
+}
+
+.template-product .dw-size-pill.is-unavailable,
+.template-product .dw-size-pill:disabled {
+  opacity: 0.45;
+  cursor: not-allowed;
+  text-decoration: line-through;
+}
+
+/* When the size pills are present we don't need the legacy plain-text
+   ".last_variant" echo of the option value cluttering the column. The
+   theme JS still toggles it for the display_variant=="no" single-variant
+   case; we only hide it once pills exist as a sibling. */
+.template-product .dw-size-pills ~ .selector-wrapper .last_variant {
+  /* leave theme behavior intact; no override needed */
+}
+
+/* ==========================================================================
+   2. CTA PILL ROW  (Request Sample + Spec Sheet on a row, Add to Cart below)
+   The existing buttons already share border-radius:99px + black fill from
+   custom.css. Here we only normalize sizing and arrange them in the
+   reference 2-up + full-width layout.
+   ========================================================================== */
+
+.template-product .product-add-to-cart {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+  align-items: stretch;
+  max-width: 432px; /* ~ two 206px pills + gap */
+}
+
+/* Every primary CTA pill: uniform height / font / weight / radius / color */
+.template-product .product-add-to-cart .dl-sample-btn,
+.template-product .product-add-to-cart .dl-second-sample-btn,
+.template-product .product-add-to-cart .add-to-cart,
+.template-product .product-add-to-cart .shopify-payment-button__button {
+  flex: 1 1 calc(50% - 6px);
+  width: auto !important;          /* override fixed 206px so they share the row */
+  min-width: 0;
+  min-height: 48px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  margin: 0 !important;
+  border-radius: var(--dw-pill-radius) !important;
+  font-family: var(--dw-pill-font);
+  font-weight: 700;
+  font-size: 12px !important;
+  letter-spacing: 0.02em;
+  text-transform: uppercase;
+  box-sizing: border-box;
+}
+
+/* Add to Cart spans the full width on its own row, beneath the 2-up pills. */
+.template-product .product-add-to-cart .add-to-cart {
+  flex: 1 1 100%;
+}
+
+/* Spec-sheet button has its own inline-block style block in its snippet; make
+   sure it participates in the flex row like the other pills. */
+.template-product .product-add-to-cart .spec-sheet-btn {
+  display: inline-flex !important;
+  vertical-align: top;
+}
+
+/* Keep the hidden/unavailable helpers behaving (they were display utilities). */
+.template-product .product-add-to-cart .hidden {
+  display: none !important;
+}
+
+/* Mobile: stack everything full-width but keep the pill look. */
+@media screen and (max-width: 1024px) {
+  .template-product .product-add-to-cart {
+    max-width: none;
+  }
+  .template-product .product-add-to-cart .dl-sample-btn,
+  .template-product .product-add-to-cart .dl-second-sample-btn,
+  .template-product .product-add-to-cart .add-to-cart,
+  .template-product .product-add-to-cart .shopify-payment-button__button {
+    flex: 1 1 100%;
+    width: 100% !important;
+    max-width: none !important;
+  }
+}
diff --git a/shopify/theme-5.0-duplicate/assets/dw-pdp-size-pills.js b/shopify/theme-5.0-duplicate/assets/dw-pdp-size-pills.js
new file mode 100644
index 00000000..0a6db012
--- /dev/null
+++ b/shopify/theme-5.0-duplicate/assets/dw-pdp-size-pills.js
@@ -0,0 +1,110 @@
+/*
+ * dw-pdp-size-pills.js  (Designer Wallcoverings PDP restyle, 2026-06-22)
+ *
+ * Progressive enhancement ONLY. We do NOT remove or replace the native
+ * <select class="single-option-selector"> that the theme's variant /
+ * cart / cowlendar / MOQ JS reads. We:
+ *   1. keep the <select> in the DOM (visually hidden via dw-pdp-restyle.css),
+ *   2. render one clickable pill <button> per <option>,
+ *   3. on click -> set select.value + dispatch a native 'change' event so
+ *      the existing jQuery `.on('change', '.single-option-selector', ...)'
+ *      handler in custom.js.liquid (and everything downstream) fires exactly
+ *      as if the user had used the dropdown.
+ *
+ * Safe to load globally; it no-ops on any page without a size selector.
+ */
+(function () {
+  'use strict';
+  if (window.__dwPdpSizePillsLoaded) return;
+  window.__dwPdpSizePillsLoaded = true;
+
+  var SELECTOR = 'select.single-option-selector';
+
+  function buildPills(select) {
+    if (!select || select.dataset.dwPilled === '1') return;
+    // Only enhance real size/option selectors with 2+ choices.
+    var opts = Array.prototype.slice.call(select.options);
+    if (opts.length < 1) return;
+
+    select.dataset.dwPilled = '1';
+
+    var wrap = document.createElement('div');
+    wrap.className = 'dw-size-pills';
+    wrap.setAttribute('role', 'radiogroup');
+    var lbl = select.getAttribute('data-option-name') ||
+              (select.options[0] && select.options[0].closest ? '' : '');
+    wrap.setAttribute('aria-label', lbl || 'Size');
+
+    opts.forEach(function (opt) {
+      var btn = document.createElement('button');
+      btn.type = 'button';
+      btn.className = 'dw-size-pill';
+      btn.setAttribute('role', 'radio');
+      btn.textContent = (opt.textContent || opt.value || '').trim();
+      btn.dataset.value = opt.value;
+      if (opt.disabled) {
+        btn.disabled = true;
+        btn.classList.add('is-unavailable');
+      }
+      if (opt.selected) {
+        btn.classList.add('is-active');
+        btn.setAttribute('aria-checked', 'true');
+      } else {
+        btn.setAttribute('aria-checked', 'false');
+      }
+      btn.addEventListener('click', function () {
+        if (btn.disabled) return;
+        if (select.value === opt.value && opt.selected) {
+          // already selected — still re-sync visual state
+          syncActive(wrap, select);
+          return;
+        }
+        select.value = opt.value;
+        // Native change so jQuery delegated handlers + theme JS all fire.
+        var evt;
+        try {
+          evt = new Event('change', { bubbles: true });
+        } catch (e) {
+          evt = document.createEvent('HTMLEvents');
+          evt.initEvent('change', true, false);
+        }
+        select.dispatchEvent(evt);
+        syncActive(wrap, select);
+      });
+      wrap.appendChild(btn);
+    });
+
+    // Insert the pills right before the (now hidden) select wrapper.
+    var anchor = select.closest('.selector-wrapper') || select.parentNode;
+    if (anchor && anchor.parentNode) {
+      anchor.parentNode.insertBefore(wrap, anchor);
+    }
+
+    // Keep pills in sync if the select changes from any other source.
+    select.addEventListener('change', function () {
+      syncActive(wrap, select);
+    });
+  }
+
+  function syncActive(wrap, select) {
+    var val = select.value;
+    Array.prototype.forEach.call(wrap.querySelectorAll('.dw-size-pill'), function (b) {
+      var on = b.dataset.value === val;
+      b.classList.toggle('is-active', on);
+      b.setAttribute('aria-checked', on ? 'true' : 'false');
+    });
+  }
+
+  function init() {
+    var selects = document.querySelectorAll(SELECTOR);
+    Array.prototype.forEach.call(selects, buildPills);
+  }
+
+  if (document.readyState === 'loading') {
+    document.addEventListener('DOMContentLoaded', init);
+  } else {
+    init();
+  }
+  // Re-run shortly after load in case theme JS injects/reorders selectors.
+  window.addEventListener('load', function () { setTimeout(init, 600); });
+})();
diff --git a/shopify/theme-5.0-duplicate/layout/theme.liquid b/shopify/theme-5.0-duplicate/layout/theme.liquid
index 6727e422..7a0347ad 100644
--- a/shopify/theme-5.0-duplicate/layout/theme.liquid
+++ b/shopify/theme-5.0-duplicate/layout/theme.liquid
@@ -1020,5 +1020,8 @@ alt="web statistics"></a></div></noscript>
 <script src="{{ 'dw-pj-hide.js' | asset_url }}" defer></script>
 {%- comment -%} DW card hover label: Pattern, Color + Vendor on hover (2026-06-22) {%- endcomment -%}
 <script src="{{ 'dw-card-hover.js' | asset_url }}" defer></script>
+{%- 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>
 </body>
 </html>

← 19ae0d34 auto-save: 2026-06-22T17:07:33 (6 files) — shopify/scripts/b  ·  back to Designer Wallcoverings  ·  5.1 dev: horizontal collapsible Boost filter bar (dw-filter- 8e45fc21 →