> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://easybundles-help.skailama.app/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Easy Bundles SDK : Custom Storefront Implementation Guide

## Overview

This guide outlines the workflow for building a custom "Mix & Match" bundle interface using the Easy Bundles SDK on Shopify.

**Architecture Type:** Headless-on-Liquid. The App manages the backend logic, product containers, and cart session. The Developer is responsible for the Frontend UI, HTML structure, state rendering, and validation enforcement.

## Phase 0: Prerequisites & App Configuration

**Crucial:** Before writing any code, the bundle structure must be configured in the Shopify Admin via the Easy Bundles App. The SDK merely reflects the configuration created here.

## 1. Create the Bundle (Merchant/Admin Step)

* Navigate to the **Easy Bundles App** dashboard.
* Select **"Product Page Bundle"**.
* **Automatic Parent Product:** When you create this bundle, the app automatically generates a "Parent" or "Dummy" product in Shopify to house the bundle.

* _Note to Developer:_ You do **not** need to create a product manually in Shopify or configure inventory/pricing for the container. The app handles this.

## 2. Configure Steps & Categories

Understanding the data structure is vital for using the SDK.

* **Steps:** These represent the logical flow (e.g., "Step 1: Choose Base," "Step 2: Choose Toppings").
* **Categories:** These are collections of products.
* **The Relationship:** Steps and Categories are **not** 1:1.

* A single Step can contain _multiple_ Categories.
* _Example:_ Step 1 could display both "T-Shirts" (Category A) and "Hoodies" (Category B).
* **Configuration:** Define your Steps, assign Categories to them, and set selection limits (min/max) inside the App Admin. These settings generate the `stepId`s and validation rules you will access via the SDK.

## 3. Configure Discounts (Optional)

If the bundle includes special pricing (e.g., Percentage Off, Fixed Price, or Tiered Discounts), configure these in the **Discounts** section of the App Admin.

* **Role of SDK:** The SDK allows you to _read_ these settings (via `discountConfiguration`) to display the savings to the user.
* **Role of App:** The App backend automatically applies the actual discount to the cart line items if the rules are met.
* _Note:_ You cannot "create" discounts via JavaScript. They must exist in the Admin first.

## Phase 1: Core Concepts & Mental Model

Once the bundle is configured, use the SDK to build the UI. Keep these three architectural rules in mind:

## 1. The "Manual Render" Loop

The SDK **does not** emit generic "state-changed" events. It does not automatically update your DOM when data changes.

* **The Workflow:** You must manually trigger a UI re-render after every user interaction.
* **Pattern:** `User Click` → `Call SDK Function` → `Await Completion` → `Call Your render() Function`.

## 2. Client-Side Gatekeeping

The SDK is permissive. It will allow an `addToCart` action even if the user hasn't selected the required number of items.

* **Your Responsibility:** You must implement validation logic (using SDK helpers) to disable the "Add to Cart" button until all conditions are met.

## 3. State Management

* **Global Access:** The SDK exposes all bundle data, rules, and current selections via the global `gbbMix` object.

## Phase 2: Setup & Initialization

## 1. Enabling the SDK

Ensure the **Theme App Extension** is enabled in the Shopify Theme Editor.

* **Script Loading:** The SDK injects automatically on product pages that are linked to a Bundle.
* **Initialization:** The SDK auto-detects the Bundle ID based on the current product page. No manual `gbbMix.init()` call is required.

## 2. HTML Structure

The SDK provides **zero** HTML. You must build the container.

* **Recommendation:** Create a wrapper `div` with a specific ID to act as your application root.
* **Liquid:** Do not use Liquid loops to render bundle items. Use Liquid only for the basic page layout, then let JS inject the bundle interface.

```
<!-- Example Root --> <div id="easy-bundle-root">   <!-- Loading Spinner (Default State) -->   <div class="loader">Loading Bundle...</div> </div>
```

## Phase 3: Development & UI Logic

## 1. Accessing Data

All data is available in the global state object. To inspect this during development, append `?dev=true` to the URL and check the console.

* **Global State:** `gbbMix.sdk.state`
* **Offer/Bundle IDs:** Found within the state object.
* **Box Selection:** If your bundle uses box sizes (6-pack vs 12-pack), access `gbbMix.settings.mixAndMatchBundleData.boxSelection`.

## 2. Rendering the UI

Since there is no "State Change" listener, you should structure your code with a central render function.

```
async function handleItemAdd(stepId, variantId, qty) {     // 1. Call SDK Action     await gbbMix.sdk.f.addItem(stepId, variantId, qty);      // 2. Manually Re-render UI to reflect new totals/counts     renderBundleUI(); }
```

## 3. Calculating Prices & Discounts

The SDK provides the **raw subtotal** in `cartData.total_price`. It does **not** automatically calculate the discounted display price for the frontend.

* **To Display Discounts:** You must calculate the math manually using:

* `gbbMix.sdk.state.discountConfiguration` (contains rules, % off, fixed amounts).
* `cartData.total_price` (current raw total).
* **Note:** The _actual_ transactional discount is applied automatically by the App during the Cart/Checkout phase, provided the rules are met. Your calculation here is purely for the user's visual benefit.

## Phase 4: Validation & Cart Actions

## 1. Validating Selections

**Crucial:** You must validate rules before allowing the user to proceed.

* **Helper Function:** `gbbMix.sdk.f.checkStepLevelCondition(stepId, variantId, productQuantity)`
* **Logic:** Iterate through steps; if any step fails the condition check, keep the "Add to Cart" button disabled.

## 2. Add to Cart Behavior

The `addBundleToCart()` function is **asynchronous** and **AJAX-based**. It does not redirect the user to checkout or the cart page automatically.

**The Implementation Pattern:**

1. **Trigger:** Call the function.
2. **Await:** Use `await` to keep your Loading Spinner active.
3. **Listen:** Use Event Listeners to handle the final outcome (Success/Fail).

**Code Reference:**

```
const handleAddToCart = async () => {     // 1. Start Loading State     showSpinner(true);      // 2. Setup Event Listeners     const onSuccess = () => {         console.log("Bundle added!");         // Redirect to cart or open drawer         window.location.href = '/cart';     };      const onFail = () => {         alert("Failed to add bundle. Please try again.");     };      window.addEventListener('gbb-add-bundle-to-cart-success', onSuccess, { once: true });     window.addEventListener('gbb-add-bundle-to-cart-failed', onFail, { once: true });      // 3. Call SDK (Returns void, but is awaitable)     await gbbMix.sdk.f.addBundleToCart();      // 4. Cleanup Loading State     showSpinner(false); };
```

## Phase 5: Debugging & Tools

* **Developer Mode:** Add `?dev=true` to your URL. This exposes `gbbMix` in the console for inspection.
* **Function Inspection:** Once in dev mode, you can inspect SDK functions via `gbbMix.sdk.f`.

## Phase 6: Technical Limitations

* **Headless Storefronts:** The SDK relies on Shopify's Online Store 2.0 structure. It is **not** compatible with Hydrogen or React-based headless storefronts.
* **Vintage Themes:** Support for legacy Liquid (Vintage) themes is not guaranteed due to legacy technology stacks.
* **Frameworks:** While you may use lightweight frameworks (Alpine.js, Vue, React) to render the widget, the app does not provide framework-specific components. Plain JavaScript is recommended for performance.

## Phase 7: API Reference

This section lists the essential properties and methods available in the `gbbMix` global object.

## Global Objects

| Object Path | Description |
| --- | --- |
| gbbMix.sdk.state | The primary state object containing current selections, bundle configuration, and discount rules. |
| gbbMix.sdk.f | The function library for interacting with the bundle (adding items, checking validation). |
| gbbMix.settings | Static settings loaded from the app admin. |

## Key State Properties (gbbMix.sdk.state)

| Property | Description |
| --- | --- |
| discountConfiguration | Object containing discount rules (Percentage/Fixed) needed for manual frontend price calculation. |
| cartData | Contains the current state of the bundle in the cart, including total_price (raw subtotal). |
| steps | Array containing step configurations (IDs, names, associated categories). |

## SDK Functions (gbbMix.sdk.f)

| Function | Parameters | Description |
| --- | --- | --- |
| addItem | (stepId, variantId, qty) | Adds a specific variant to the bundle selection. Note: Must manually re-render UI after calling. |
| checkStepLevelCondition | (stepId, variantId, qty) | Helper to validate if the current selection meets the Min/Max rules configured in the Admin. |
| addBundleToCart | () | Asynchronously adds the valid bundle to the Shopify cart. Returns void but is awaitable. |

## Settings (gbbMix.settings)

| Property | Description |
| --- | --- |
| mixAndMatchBundleData.boxSelection | Use this to access "Box Size" configurations (e.g., 6-pack vs 12-pack) if enabled for the bundle. |

## Event Listeners (Window)

| Event Name | Description |
| --- | --- |
| gbb-add-bundle-to-cart-success | Fired when the bundle is successfully added to the cart via AJAX. |
| gbb-add-bundle-to-cart-failed | Fired when the add-to-cart operation fails (e.g., network error). |

## Summary Checklist for Developer

* [ ] **Admin:** Bundle structure (Steps/Categories) created in Easy Bundles App.
* [ ] **Frontend:** HTML container present in theme.
* [ ] **Logic:** Manual `render()` function implemented and called after every SDK action.
* [ ] **Math:** Manual price/discount calculation for display.
* [ ] **Validation:** "Add to Cart" button disabled if conditions (`checkStepLevelCondition`) are not met.
* [ ] **Checkout:** `addBundleToCart` handled via `await` (for UI loader) and `EventListeners` (for success/fail logic).