Template
Cart drawer notice
A free-shipping progress notice for the cart drawer, driven by cart totals and updated on every cart change.
Renders the amount remaining before free shipping, plus a progress bar. Values come from the cart object, so the notice is correct on first paint and does not flash a placeholder.
Set your threshold in the snippet call — `{% render 'cart-notice', threshold: 7500 %}` — using the amount in cents, matching Shopify money fields.
Code
Copy a single file, or download all 3 as an archive. Duplicate your theme before pasting into a live store.
template.liquidLiquid {% comment %} Cart drawer notice — free shipping progress. Usage: {% render 'cart-notice', threshold: 7500 %} {% endcomment %} {%- liquid assign goal = threshold | default: 7500 assign remaining = goal | minus: cart.total_price assign progress = cart.total_price | times: 100.0 | divided_by: goal if progress > 100 assign progress = 100 endif -%} <div class="cart-notice" data-cart-notice data-threshold="{{ goal }}"> {%- if remaining > 0 -%} <p class="cart-notice__text"> You are {{ remaining | money }} away from free shipping. </p> {%- else -%} <p class="cart-notice__text cart-notice__text--met"> Your order qualifies for free shipping. </p> {%- endif -%} <div class="cart-notice__track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="{{ progress | round }}" aria-label="Progress toward free shipping" > <div class="cart-notice__bar" style="--cart-notice-progress: {{ progress }}%"></div> </div> </div>template.cssCSS .cart-notice { display: grid; gap: 0.5rem; padding: 0.875rem 1rem; background: rgb(0 0 0 / 0.04); } .cart-notice__text { margin: 0; font-size: 0.875rem; } .cart-notice__text--met { font-weight: 600; } .cart-notice__track { height: 0.375rem; background: rgb(0 0 0 / 0.1); overflow: hidden; } .cart-notice__bar { height: 100%; width: var(--cart-notice-progress, 0%); background: currentColor; transition: width 200ms ease-out; } @media (prefers-reduced-motion: reduce) { .cart-notice__bar { transition: none; } }template.jsJavaScript (() => { const formatter = new Intl.NumberFormat(document.documentElement.lang || 'en', { style: 'currency', currency: window.Shopify?.currency?.active || 'USD', }); async function refresh() { const notice = document.querySelector('[data-cart-notice]'); if (!notice) return; const threshold = Number(notice.dataset.threshold || 0); if (!threshold) return; const response = await fetch(window.Shopify.routes.root + 'cart.js'); if (!response.ok) return; const cart = await response.json(); const remaining = threshold - cart.total_price; const progress = Math.min((cart.total_price / threshold) * 100, 100); const text = notice.querySelector('.cart-notice__text'); const bar = notice.querySelector('.cart-notice__bar'); const track = notice.querySelector('.cart-notice__track'); if (text) { text.textContent = remaining > 0 ? `You are ${formatter.format(remaining / 100)} away from free shipping.` : 'Your order qualifies for free shipping.'; text.classList.toggle('cart-notice__text--met', remaining <= 0); } if (bar) bar.style.setProperty('--cart-notice-progress', `${progress}%`); if (track) track.setAttribute('aria-valuenow', String(Math.round(progress))); } document.addEventListener('cart:updated', refresh); document.addEventListener('shopify:section:load', refresh); })();