case study · liquid · theme · metafields
Tiered cart promotions, three render paths, one contract
Background. Originally built for a US home-goods DTC brand running frequent stacked promotions: free shipping over a threshold, spend-more-save-more tiers, and category bundles. Off-the-shelf promotion apps couldn’t match the design or the exact stacking rules, and each one added script weight to every page. What you can try on the demo store is a clean-room rebuild of that engine on Dawn.
The problem. A cart promotion bar looks trivial until you count the places a cart renders: the cart page, the slide-out drawer, and the fragments Shopify re-renders after every AJAX add or quantity change. Most implementations bolt a JavaScript widget onto one of those and drift out of sync with the others — the bar says “free shipping unlocked” while the drawer disagrees.
The approach. Liquid is the only place promotion math runs. The module is a single snippet rendered inside the containers the theme already re-renders through Shopify’s Section Rendering API, so every cart mutation re-computes the bars server-side — the storefront ships zero promotion JavaScript. Configuration lives in one versioned shop metafield:
{
"v": 1,
"free_shipping": { "enabled": true, "threshold": 80 },
"slots": [
{
"id": "candle-bundle",
"mode": "quantity",
"collection_handle": "candles",
"tiers": [
{ "threshold": 2, "reward_text": "a matching wick trimmer" },
{ "threshold": 4, "reward_text": "a fifth candle free" }
]
}
]
}
The v field is load-bearing: a theme that meets a config version it doesn’t
recognize renders nothing rather than guessing — “hide rather than lie.” The
same rule applies to presentment currencies: thresholds are defined in the
store currency, so any other currency hides the bars instead of mis-converting
them.
Details that came from production. Tier thresholds are judged on
pre-discount line totals (original_line_price). That rule was calibrated
against how Shopify’s own discount engine evaluates minimum spend — getting it
wrong means telling a customer they’ve unlocked a reward the checkout then
refuses.
Try it. Add two candles on the demo store, open the drawer, then change quantities on the cart page. Both bars, both surfaces, always in agreement.