Bundle builder, plan v3

The rail ladder the client chose, on a receipt that leads with what the shopper saves, riding the theme's own cart path. Mobile first, because 78.5% of sessions are.

Shubham Yadav · 14 Sep 2026 · supersedes the 9 Sep plan for layout and campaign styling; the offer model, discount mechanism and dates carry over unchanged

Decision record

Keep the rail. Keep the money. Borrow the theme's cart. Build the tiles ourselves.

Reuse map: theme vs custom

Verified against the theme files on the Shubham branch on 14 Sep. The stock bundle section is byte-identical to main and has never been published on a page.

Theme partWhat it doesUse it?Why
Cart submit path in ProductBundleBatched /cart/add.js with items[], cart:bundled-sections, cartUpdate pubsub, sold-out relabel, error lineYesAlready patched for the fly-to-cart decision in product-bundle.js. One submit path for the whole store.
quantity-inputStepper element with min, max, step validationYesReceipt row steppers. Free validation, free translations.
sticky-elementSticky sidebar that respects the header offsetYesDesktop receipt. Removes our own max() offset arithmetic.
.button, hover-buttonTheme buttons with the fill animationYesCTA looks native on every market.
theme.Currency.formatMoneyMarket-correct money stringsYesAlready used in draft 1.
NB_GIFT_RULES.tiersSpecThe live gift ladder per marketYesReward copy derives from it, never re-authored. Kills the drift risk from draft 1.
product-card-bundle gridOne card per product, variant picker insideNoWrong shape for one product with 62 variants.
ProductBundle slot model as shippedOne slot per add, prevent_duplicate_productsNot as isOne override merges repeat adds of the same variant into one slot; duplicates setting stays off.
<progress-bar>One fill toward minimum_quantityNoNo rungs, no labels. The rail replaces it.
page.bundle.json + market contextsThe page templateYesAdd nb-bundle-builder as a section; the three stock sections stay in the file untouched.
Native automatic discountsAmount off products, minimum quantity, scheduledYesRan on this store 2022 to 2024; "best discount applies" resolves the tiers; no app to uninstall or pay for.
ProductBundle elementSlots, totals, submit, errors, pubsubExtendSubclass with four overrides (merge same variant, rail fill, slot template, 422 loop); the rest is inherited and updates with the theme.
QuickView subclassModal that fetches a page and injects a fragmentPhase 2The homepage and PDP popup, per the September workflow finding.

Mobile first: 390px, top to bottom

  1. Masthead. Campaign banner only when a campaign is on. Headline. At zero bags the offer sentence; from one bag the hero count with its caption. Countdown line reserved so the shelf never shifts.
  2. The rail. Full width. All rungs as dots with the bag count under each. Six reward labels do not fit 390px, so they collapse to one line beneath the track: 2 more bags for 15% off, plus 3 free bags and 3 carabiners. The shape the client liked survives; the copy stays readable.
  3. Style index. Sticky, text only, jumps and scroll-spies.
  4. Shelf. Two full rows per style, then "Show all 12 colours". Tap a tile to add; tap again to increment; a solid stepper bar sits on the photo.
  5. No tray at zero bags. The rail already shows the whole offer, so a dormant bar eating 68px before the first tap is deleted.
  6. Docked bar from the first bag. 68px: $12.76 saved and the gap on the left, the CTA on the right. Tap the left half to open the sheet: money hero, gifts row, bag rows, pinned CTA. Escape or the scrim closes it.

Desktop, 992px and up

Same order; the rail spans both columns under the masthead, the shelf takes the left column, the receipt is sticky on the right at 380px. Nothing is centred.

Three states, one section

EverydayBlack FridayCyber Monday
Groundpaper #f7f4efblack #0b0b0cmidnight #0f1830
Accentcrimsoncrimsonmint #a8e8e2, the theme's own
Receiptinkwhite paperlifted navy
Rungs1, 3, 4, 6, 8, 101, 2, 3, 4, 6, 8, 10Black Friday plus 15
Extranonebanner, countdownbanner, countdown, stock-up card

Free shipping sits at 3 bags in every state. Two bags is 40g against the 49.9g weight threshold, so a 2-bag promise is one the shipping rate would contradict.

Data and money

Build order, with the gate for each step

  1. Refresh Shubham. Commit the in-flight work in its groups, then git merge origin/main.

    Gate: templates/index.json and templates/product.json match main byte for byte before any bundle edit touches them.

  2. Discount plumbing. Simple Discounts automatic tiers on the eligible collection, one market at a time.

    Gate: a real 4-bag cart in US, UK and EU checks out at the ladder's percentage. Screenshots kept.

  3. Section v3 on the v2 files: rail, tiles, receipt, campaign tokens, theme cart path.

    Gate: shopify theme check clean; probe script passes every state at 390 and 1440; zero em-dashes; AA on every measured pair.

  4. Data. Rewards derived from tiersSpec; nb_bundle_offers metaobject; cart seeding; the market cache guard (one reload, never a loop).

    Gate: ?country=DE, ?country=PL and ?country=SG each render their own prices and translated colour names.

  5. Copy to locale keys, 32 files, plural-safe.

    Gate: no "Translation missing" on da or pl.

  6. Page. nb-bundle-builder added to page.bundle.json plus 8 market contexts, page created unpublished.

    Gate: pre-consent EU pageview renders the shelf and the ladder with the script deferred; consent-guard check passes.

  7. Popup on homepage and PDP via the QuickView subclass.

    Gate: modal fetch is under 20KB and the header never appears inside it.

Dates, working back from Black Friday

WhenWhat
15 to 17 SepShubham refresh. NB09 closes on the 17th; nothing goes near main before that.
18 Sep to 3 OctSteps 2 to 5. Discount plumbing first because it is the part nobody can see and the part that costs money if wrong.
4 to 9 OctPage on the Shubham theme, client review, per-market QA.
12 to 26 OctRollouts test, everyday ladder, 7 markets, EU out.
27 Oct to 12 NovReadout, fixes, popup, campaign dry run with the clock forced.
13 NovCode freeze.
27 Nov, 30 NovBlack Friday, Cyber Monday. The browser flips the state; nobody is awake at midnight for this.

Thinking ahead: what will bite, and the answer for each

Test, do not assume

Decisions needed now

  1. Confirm the read: the design the client liked is draft 1's rail, not Concept's stock section. If it is the stock section, I push it to the Shubham theme on page.bundle so you can both see it rendered before we go further.
  2. Settled 14 Sep: discounts are native automatic tiers, not the Simple Discounts app. The audit is in the build spec; the app stays installed and unused.
  3. Bag-on-white photography: who shoots it, and by when.
  4. The drawer's free-shipping rule: fix it now as its own change, or bundle it into this work.

What the demo is and is not

The v3 page on this hub is a design prototype with the real US catalogue and 62 real product photos. It is not connected to a cart: the count is not seeded from /cart.js, the 422 recovery is not built, the market guard is not built, and the copy is inline English. Everything you can click, toggle and read is the intended design; everything underneath is specified above and unbuilt.