What this app does
Bee Bundle Box lets shoppers fill a box with products you choose and buy it as one line in the cart. You set which products they can pick from, how many go in a box, and how the price is worked out.
There are two kinds of bundle. A flexible bundle gives the shopper a grid of products and lets them fill the box themselves — a build-your-own six-pack, a pick-and-mix. A prebuilt bundle is a fixed set you decide, shown on one product’s page as a “buy this, or buy the bundle” choice; the shopper only picks each item’s variant.
Setting up your first bundle
- Make a collection in Shopify holding the products a shopper can pick from. A flexible bundle draws its grid from one collection, so this decides what appears.
- Create the bundle under Bundles → Add bundle. Give it a title, choose the collection, and set the minimum and maximum number of items a box can hold.
- Choose how it’s priced — see Pricing below.
- Turn on the app embed. In Shopify go to Online Store → Themes → Customize → App embeds, and switch on Bundle Box app embed. This keeps bundle lines tidy in the cart and is needed on every store — it’s also what makes a prebuilt bundle’s widget appear on its anchor product’s page automatically.
- Place the block. For a flexible bundle, open the page you want it on in the theme editor, then Add block → Build a Bundle. A prebuilt bundle needs no placement — once its embed is on, it appears on its anchor product’s page by itself.
The app’s Overview page tracks these steps and ticks them off as you go.
Pricing
Every bundle uses one of three pricing modes. Whichever you pick, the storefront shows a live total as the shopper fills the box, and the server works the price out again independently when the bundle is added to the cart — so the price charged is always the one your rules produce, never one edited in the browser.
- Fixed price — the whole box costs one amount, no matter which products go in it. Best for “any 6 for £30”.
- Percentage off — each product is discounted. You set a default percentage for the bundle, and can override it per product, so a premium item can discount less than the rest. You can also cap how many of one product fit in a box.
- Formula — you write the rule yourself. Explained in full below.
Quantity tiers stack an extra discount on top of percentage pricing once the box reaches a size — 5% off at 6 items, 10% at 12. Only the best matching tier applies, never two at once.
Formula pricing
A formula is a single expression that works out what the whole box costs. It’s written in JavaScript-style syntax, and the result is the total in your store’s currency. A negative result is treated as zero — a formula can never make a box cost less than nothing.
It is not JavaScript, though it looks like it. The app reads the formula with its own parser that understands only what’s listed here, so a formula can’t reach the page, the network, or anything on our side. Anything it doesn’t recognise is refused when you save it, with a message saying what went wrong.
Values you can use
| Name | What it holds |
|---|---|
subtotal | What the box would cost at full price, before any discount. |
count | How many items are in the box, counting quantities. |
itemCount, quantity, items | The same number as count. Spellings that mean the same thing, so your first guess at the name works. |
qty("id") | How many of one product are in the box — 0 if it isn't there. Also spelled quantityOf(). Use the product's numeric ID, the number at the end of its admin URL. |
Operators
- Arithmetic:
+ - * / %and parentheses. Dividing by zero is refused rather than producing an error price. - Comparisons:
> < >= <= == !=. These produce 1 when true and 0 when false, so you can multiply by them. - Logic:
&&(and),||(or). - Choice:
condition ? thisValue : otherValue, which reads as “if … then … otherwise …”. - Maths helpers:
min, max, round, floor, ceil, abs— each also spelled with aMath.prefix if that’s what you’re used to typing.Math.powandMath.sqrtare available in their dotted form only.
Examples
| Formula | What it does |
|---|---|
subtotal * 0.9 | Always 10% off. |
count >= 6 ? subtotal * 0.8 : subtotal | 20% off, but only once there are six or more items. |
subtotal - (count * 2) | £2 off per item in the box. |
qty("123") >= 2 && qty("456") >= 1 ? subtotal * 0.9 : subtotal | 10% off when the box holds at least two of product 123 and at least one of product 456. |
max(subtotal * 0.75, 25) | 25% off, but never cheaper than £25. |
subtotal * (1 - 0.05 * min(floor(count / 3), 4)) | A further 5% off for every three items, stopping at 20%. |
When you save a formula the app tests it against a sample box first, so a formula that can’t produce a number is caught in the admin rather than on your storefront.
Fonts and colours
Settings → Appearance changes how the widget looks on every plan, with no code. Each field is optional, and leaving one blank means it inherits from your theme — which is the point: the widget should look like part of your store, not like an app bolted onto it. A shop that sets nothing here renders exactly as it did before.
- Font — “Match my theme” keeps your store’s own typeface. The other choices are system or common faces, so nothing extra has to load.
- Text size — multiplies your theme’s own size rather than replacing it, so the widget stays in proportion. 1 matches the theme; 1.1 is slightly larger.
- Colours — accent (buttons and highlights), button text, text, background, and card. Write them as hex
#1a1a1aor asrgb()/hsl(). - Corner radius —
8pxfor rounded,0for square.
The Build a Bundle block also has colour settings of its own in the theme editor. Those apply to that one block; these apply everywhere the widget appears, including the prebuilt widget.
Custom CSS and JavaScript
Custom CSS and JavaScript are on the Pro plan, under Settings. They’re saved to your shop and loaded by the app embed on your storefront, which means they run in your shoppers’ browsers on your own domain — exactly like a snippet pasted into your theme. Nothing you write here runs on our servers or touches our database.
What custom CSS is for
Styling the widget past what the Appearance settings cover. Target the widget’s own classes:
.bb— the flexible widget’s root, with.bb__grid,.bb__card,.bb__slots,.bb__footer,.bb__add-to-cartand.bb__errorinside it..bbp— the prebuilt widget’s root, with.bbp__option,.bbp__components,.bbp__component,.bbp__savings-badgeand.bbp__add-to-cartinside it.- You can also set the app’s own custom properties —
--bb-accent,--bb-text,--bb-bg,--bb-card-bg,--bb-radius,--bb-font-family,--bb-font-scale— on.bbor.bbp, which is usually tidier than overriding individual rules.
Not allowed: @import, javascript: URLs, expression(), the behavior property, and style or script tags. Each of these is a way of loading or running code from a stylesheet rather than styling something.
What custom JavaScript is for
Reacting to what the shopper does in the widget — analytics events, showing a message, nudging your theme. The widget fires two events you can listen for:
bb:ready— the widget has loaded and rendered.event.detailholdsroot(the widget element) andconfig(its settings and products).bb:added— a bundle was added to the cart.event.detailholdsrootanditems, the products that went in.
Both bubble to the document, so you can listen once:
document.addEventListener('bb:added', function (event) {
console.log(event.detail.items);
});Some things are refused when you save, because they have no honest use in a widget hook and every dishonest one: eval and building functions from strings, importing modules, fetch / XMLHttpRequest / sendBeacon and other ways of sending data elsewhere, WebSockets and workers, cookies and browser storage, innerHTML and insertAdjacentHTML, document.write, postMessage, reaching outside the frame, service workers, and script tags. If you save something that trips one of these, the app tells you which and why.
To build markup, use document.createElement and textContent rather than innerHTML. Both boxes are limited to 20,000 characters.
Cart and checkout
A bundle is added as one cart line at the price your rules produced. Shopify is told which products make it up, so inventory comes off each one and your reports still show what actually sold.
After a bundle is added, the app opens your theme’s own cart drawer by clicking its cart icon — no theme edits needed. If auto-detection picks the wrong element on your theme, set a CSS selector under Settings → Add to cart.
Shoppers can edit a bundle from the cart. That reopens the widget with their picks already in it, and replaces the old line when they save.
What each plan includes
| Feature | Starter | Growth | Pro |
|---|---|---|---|
| Bundles | Up to 3 | Unlimited | Unlimited |
| Fixed and percentage pricing | Yes | Yes | Yes |
| Prebuilt bundles | Yes | Yes | Yes |
| Fonts and colours | Yes | Yes | Yes |
| Quantity tiers | — | Yes | Yes |
| Formula pricing | — | — | Yes |
| Custom CSS and JavaScript | — | — | Yes |
If something isn't working
- The widget doesn’t appear. Check the app embed is on for your live theme, and that the block is placed on the page (flexible bundles) or that the bundle’s first component is the product whose page you’re on (prebuilt).
- It says no bundle is set. The block has no bundle ID and no default bundle is chosen. Set one in Bundles, or put the ID on the block.
- The grid is empty. The bundle’s collection has no products, or they’re unavailable.
- The cart drawer doesn’t open. Set your theme’s cart drawer selector under Settings → Add to cart.