> For the complete documentation index, see [llms.txt](https://orbit-b2b.gitbook.io/orbit-b2b-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://orbit-b2b.gitbook.io/orbit-b2b-docs/b2b-prices-app-embed/variant-qty-reactivity.md).

# Variant & qty reactivity

The embed re-runs whenever the price needs to update — when the customer picks a different variant, when they change the quantity input, or when the theme re-renders the price block via its Section Rendering API.

## Variant change

When a customer picks a new variant (color, size, style):

1. The embed listens for the `variant:changed` event that most themes fire
2. As a fallback, it watches `?variant=` URL changes via MutationObserver
3. The new variant's price is read from the embed's payload (which carries every variant's `priceCents`)
4. The resolver re-runs with the new variant's retail price → new B2B price computed
5. Overlay replaces the old one in place — the old B2B price doesn't flash retail in between

### How we avoid the flash of retail

Theme's Section Rendering API replaces the entire price element asynchronously (fetches HTML, swaps innerHTML). The cart **Add to cart** button gets disabled while the fetch is in flight and re-enabled the moment the new HTML is ready.

The embed uses that button's `disabled` attribute as a precise lifecycle signal:

* **Enabled → disabled** (theme just kicked off a re-render) → re-engage the pre-hide CSS gate, hiding the price element so the retail price doesn't peek through
* **Disabled → enabled** (theme finished re-rendering) → apply the new B2B overlay onto the freshly-rendered price element, release the pre-hide gate

This is more reliable than waiting a fixed number of milliseconds — slow networks render slower, fast networks render faster, and the cart button transition catches both.

## Quantity change

Volume Pricing tiers depend on the quantity the customer plans to buy. The embed listens to:

* `change` events on the qty input (when the customer types and blurs / hits Enter)
* `input` events on the qty input (when the customer clicks + / - stepper buttons)

And re-ticks the resolver with the new qty:

| Qty | Tier matched                       | Price shown                     |
| --- | ---------------------------------- | ------------------------------- |
| 1   | Tier 1 (`1-9`) — no extra discount | $21.25 (wholesale only)         |
| 10  | Tier 2 (`10-49` = 10% off)         | $19.13 (wholesale + 10% volume) |
| 50  | Tier 3 (`50+` = 15% off)           | $18.06 (wholesale + 15% volume) |

The qty input is found via `form[action*="/cart/add"] input[name="quantity"]` (the cross-theme convention). If your theme uses a custom selector, the embed falls back to `input[name="quantity"]` and `[data-quantity-input]` on the page.

## Listing card prices

Collection / search / homepage product cards always render at **qty=1**. That's because the card doesn't know how many the customer will eventually buy. Once the customer clicks through to PDP and picks a qty, the volume tiers come alive.

If you have a Volume Pricing rule that gives a discount starting at qty 1 (i.e. tier 1 already has a discount), cards reflect that discount. Otherwise cards show the wholesale-only price.

## When no rule applies

If no rule matches the current variant + customer + qty combination:

* Resolver returns null
* Embed leaves the theme's native price untouched
* Pre-hide gate releases immediately

So a guest viewing a B2B product on a public store sees the retail price unchanged, with no detectable delay or flicker.

## Next

* [Troubleshoot the embed](https://gitlab.com/thoaingo.vnkingnet/wholesale/-/tree/docs/docs/gitbook/troubleshooting/b2b-prices-embed.md)
* [Discount combination (Wholesale + Volume stacking)](/orbit-b2b-docs/settings/discount-strategy.md)
