Gift Wrap Checkbox
A paid gift wrap add-on with an optional gift message, right above add to cart.
"You're £12 away from free shipping" with a bar that fills as the cart grows.
Important: Before making any changes to your theme, duplicate it in Online Store > Themes so you have a backup.
A free shipping bar turns your shipping threshold into a goal. Shoppers who are close add one more item instead of paying for delivery. This section reads the cart, shows how much is left to spend, and updates when items are added or removed.
You're £12.00 away from free shipping
You've unlocked free shipping!
Preview with the default settings. Colours, text and layout can all be changed in the theme editor.
In Shopify admin go to Online Store > Themes. Next to the theme you want to edit, click the three dots and choose Edit code.
In the Sections folder click Add a new section, name it lt-free-shipping-bar, delete the placeholder code, paste in the code below and click Save.
Click Customize, go to the header area of any page (or the Cart template), click Add section and choose Free shipping bar.
Change the settings in the sidebar, drag the section where you want it, and click Save.
{%- comment -%}
Free shipping progress bar by LAUNCHTIP
https://launchtip.com/free-shopify-sections-code
{%- endcomment -%}
{%- liquid
assign threshold = section.settings.threshold | times: 100
assign remaining = threshold | minus: cart.total_price
assign remaining_money = remaining | money
assign percent = 100
if threshold > 0
assign percent = cart.total_price | times: 100 | divided_by: threshold
endif
if percent > 100
assign percent = 100
endif
-%}
{%- style -%}
#lt-{{ section.id }} { background: {{ section.settings.background }}; color: {{ section.settings.text_color }}; padding: {{ section.settings.padding }}px 16px; text-align: center; }
#lt-{{ section.id }}[hidden] { display: none; }
#lt-{{ section.id }} .lt-fsb__message { margin: 0 0 8px; font-size: 0.95em; }
#lt-{{ section.id }} .lt-fsb__track { max-width: 420px; height: 8px; margin: 0 auto; border-radius: 999px; background: {{ section.settings.track_color }}; overflow: hidden; }
#lt-{{ section.id }} .lt-fsb__fill { height: 100%; width: 0; border-radius: 999px; background: {{ section.settings.bar_color }}; transition: width 0.4s ease; }
{%- endstyle -%}
<div
id="lt-{{ section.id }}"
data-threshold="{{ threshold }}"
data-total="{{ cart.total_price }}"
data-count="{{ cart.item_count }}"
data-currency="{{ cart.currency.iso_code }}"
data-cart-url="{{ routes.cart_url }}"
data-hide-empty="{{ section.settings.hide_when_empty }}"
data-message-below="{{ section.settings.message_below | escape }}"
data-message-reached="{{ section.settings.message_reached | escape }}"
{% if section.settings.hide_when_empty and cart.item_count == 0 %}hidden{% endif %}
>
<p class="lt-fsb__message" aria-live="polite">
{%- if remaining > 0 -%}
{{ section.settings.message_below | replace: '[remaining]', remaining_money }}
{%- else -%}
{{ section.settings.message_reached }}
{%- endif -%}
</p>
<div class="lt-fsb__track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="{{ percent }}">
<div class="lt-fsb__fill" style="width: {{ percent }}%;"></div>
</div>
</div>
<script>
(function () {
var root = document.getElementById('lt-{{ section.id }}');
if (!root || root.dataset.ready) return;
root.dataset.ready = '1';
var message = root.querySelector('.lt-fsb__message');
var track = root.querySelector('.lt-fsb__track');
var fill = root.querySelector('.lt-fsb__fill');
function rate() {
var r = window.Shopify && Shopify.currency && Number(Shopify.currency.rate);
return r > 0 ? r : 1;
}
function money(cents) {
var currency = (window.Shopify && Shopify.currency && Shopify.currency.active) || root.dataset.currency;
return new Intl.NumberFormat(document.documentElement.lang || undefined, { style: 'currency', currency: currency }).format(cents / 100);
}
function render(total, count) {
var threshold = Math.round(Number(root.dataset.threshold) * rate());
var remaining = Math.max(threshold - total, 0);
var percent = threshold > 0 ? Math.min(100, Math.round(total / threshold * 100)) : 100;
root.hidden = root.dataset.hideEmpty === 'true' && count === 0;
message.textContent = remaining > 0
? root.dataset.messageBelow.replace('[remaining]', money(remaining))
: root.dataset.messageReached;
fill.style.width = percent + '%';
track.setAttribute('aria-valuenow', percent);
}
function refresh() {
fetch(root.dataset.cartUrl + '.js', { headers: { Accept: 'application/json' } })
.then(function (r) { return r.json(); })
.then(function (cart) { render(cart.total_price, cart.item_count); })
.catch(function () {});
}
// Themes don't share one cart event, so watch the cart endpoints instead.
if (!window.__ltCartWatch) {
window.__ltCartWatch = true;
var originalFetch = window.fetch;
window.fetch = function (resource) {
var request = originalFetch.apply(this, arguments);
var url = typeof resource === 'string' ? resource : (resource && resource.url) || '';
if (/\/cart\/(add|change|update|clear)/.test(url)) {
request.then(function () { document.dispatchEvent(new CustomEvent('lt:cart-changed')); });
}
return request;
};
}
['lt:cart-changed', 'cart:refresh', 'cart:updated', 'cart:change'].forEach(function (name) {
document.addEventListener(name, refresh);
});
render(Number(root.dataset.total), Number(root.dataset.count));
if (rate() !== 1) refresh();
})();
</script>
{% schema %}
{
"name": "Free shipping bar",
"tag": "section",
"class": "lt-free-shipping-section",
"settings": [
{
"type": "number",
"id": "threshold",
"label": "Free shipping threshold",
"default": 50,
"info": "In your store's main currency, with no symbol. Match this to your free shipping rate in Settings > Shipping and delivery."
},
{
"type": "text",
"id": "message_below",
"label": "Message below threshold",
"default": "You're [remaining] away from free shipping",
"info": "[remaining] is replaced with the amount left to spend."
},
{
"type": "text",
"id": "message_reached",
"label": "Message when reached",
"default": "You've unlocked free shipping!"
},
{
"type": "checkbox",
"id": "hide_when_empty",
"label": "Hide when the cart is empty",
"default": false
},
{
"type": "color",
"id": "background",
"label": "Background",
"default": "#f5f5f5"
},
{
"type": "color",
"id": "text_color",
"label": "Text colour",
"default": "#111111"
},
{
"type": "color",
"id": "bar_color",
"label": "Bar colour",
"default": "#16a34a"
},
{
"type": "color",
"id": "track_color",
"label": "Track colour",
"default": "#e5e5e5"
},
{
"type": "range",
"id": "padding",
"label": "Vertical padding",
"min": 4,
"max": 40,
"step": 2,
"unit": "px",
"default": 12
}
],
"presets": [
{
"name": "Free shipping bar"
}
]
}
{% endschema %}
| Setting | Default |
|---|---|
| Free shipping threshold In your store's main currency, with no symbol. Match this to your free shipping rate in Settings > Shipping and delivery. | 50 |
| Message below threshold [remaining] is replaced with the amount left to spend. | You're [remaining] away from free shipping |
| Message when reached | You've unlocked free shipping! |
| Hide when the cart is empty | Off |
| Background | #f5f5f5 |
| Text colour | #111111 |
| Bar colour | #16a34a |
| Track colour | #e5e5e5 |
| Vertical padding | 12px |
Further reading: Shopify AOV calculator
Yes. It watches the cart requests your theme makes and re-reads the cart after each one, so it updates when items are added, removed or changed.
Yes, turn on Hide when the cart is empty.
It shows wherever you place the section. Cart drawers are built into each theme, so to show it inside the drawer you'd need to add the markup to your theme's drawer file.
A progress bar tells shoppers to add more. SellUp shows them what to add, with cart upsells and add-ons matched to what's already in the basket.
Try SellUpA paid gift wrap add-on with an optional gift message, right above add to cart.
Hand-picked product bundles with checkboxes, a running total and one add-all button.
Buy 2 save 10%, buy 3 save 15%: a tiered pricing table with the price each worked out.
Discover more code snippets to enhance your Shopify store.
View All Sections