Loading
Copy-paste a Liquid snippet for product page - Tiered 'buy more, save more' pricing rows. Clicking a tier sets the quantity and highlights the deal - pushes average order value up.
Bundle & save
On the store it also sets the quantity field - and the discount must exist in Shopify too.
{%- comment -%} Shows a per-unit price so shoppers understand the saving. The numbers are only accurate if matching Shopify discounts exist - keep them in sync.{%- endcomment -%}{%- assign p = product.selected_or_first_available_variant.price | default: product.price -%}<div class="gpl-qty" data-gpl-qty style="--gpl-accent: {{ accent | default: 'rgb(var(--color-button, 200 245 66))' }};"> <p class="gpl-qty__head">Bundle & save</p> <button type="button" class="gpl-qty__tier is-active" data-qty="1"> <span class="gpl-qty__label">Buy 1</span> <span class="gpl-qty__price">{{ p | money }}</span> </button> <button type="button" class="gpl-qty__tier" data-qty="2"> <span class="gpl-qty__label">Buy 2 <em class="gpl-qty__save">save 10%</em></span> <span class="gpl-qty__price">{{ p | times: 2 | times: 90 | divided_by: 100 | money }}</span> </button> <button type="button" class="gpl-qty__tier gpl-qty__tier--best" data-qty="3"> <span class="gpl-qty__label">Buy 3 <em class="gpl-qty__save">save 20%</em> <span class="gpl-qty__tag">best value</span></span> <span class="gpl-qty__price">{{ p | times: 3 | times: 80 | divided_by: 100 | money }}</span> </button></div> {% stylesheet %}.gpl-qty { display: grid; gap: 0.5rem; margin: 1rem 0; }.gpl-qty__head { margin: 0; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; opacity: 0.7; }.gpl-qty__tier { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; width: 100%; padding: 0.7rem 0.9rem; border: 1px solid rgb(var(--color-foreground, 148 163 184) / 0.2); border-radius: 0.75rem; background: rgb(var(--color-foreground, 148 163 184) / 0.05); color: inherit; font: inherit; cursor: pointer; text-align: left; transition: border-color 0.15s ease, background 0.15s ease;}.gpl-qty__tier.is-active { border-color: var(--gpl-accent); background: rgb(var(--color-foreground, 148 163 184) / 0.1); box-shadow: 0 0 0 1px var(--gpl-accent);}.gpl-qty__label { font-weight: 600; font-size: 0.9rem; }.gpl-qty__save { font-style: normal; font-size: 0.7rem; font-weight: 700; color: var(--gpl-accent); margin-left: 0.35rem;}.gpl-qty__tag { font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; background: var(--gpl-accent); color: #0b1220; padding: 0.15rem 0.45rem; border-radius: 999px; margin-left: 0.4rem;}.gpl-qty__price { font-weight: 700; font-size: 0.95rem; }{% endstylesheet %} {% javascript %}document.querySelectorAll('[data-gpl-qty]').forEach((wrap) => { const tiers = wrap.querySelectorAll('[data-qty]'); tiers.forEach((btn) => { btn.addEventListener('click', () => { tiers.forEach((t) => t.classList.remove('is-active')); btn.classList.add('is-active'); const qty = btn.getAttribute('data-qty'); const input = wrap.closest('form')?.querySelector('input[name="quantity"]') || document.querySelector('input[name="quantity"]'); if (input) input.value = qty; }); });});{% endjavascript %} Usage
{% render 'quantity-breaks' %}