@lime-bundles/react 5.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,48 +39,43 @@ Merchants can set these (and any other CSS) via Lime Bundles admin → Settings
39
39
  | `--lb-text` | `#1A1A1A` | Base body text colour. |
40
40
  | `--lb-border` | `#E5E5E5` | Outer card border colour. |
41
41
  | `--lb-border-width` | `1px` | Outer card border width. |
42
- | `--lb-radius` | `12px` | Outer card corner radius. |
42
+ | `--lb-radius` | `12px` | Global corner radius. The merchant picks a preset (none / subtle / rounded / round) and every element derives its rounding from this value. |
43
43
 
44
44
  ## Header
45
45
 
46
- | Variable | Default | What it styles |
47
- |---|---|---|
48
- | `--lb-header-start` | `#C62828` | Gradient start (ignored when header style is `solid`). |
49
- | `--lb-header-end` | `#AD1457` | Gradient end. |
50
- | `--lb-header-bg` | *derived* | Final header background. Emitted as a solid colour or a gradient depending on the merchant's header-style pick. |
51
- | `--lb-header-text` | `#FFFFFF` | Header text colour. |
52
- | `--lb-countdown-bg` | `#FFF0F3` | Countdown badge background inside the header. |
53
- | `--lb-countdown-text` | `#C62828` | Countdown badge text colour. |
54
-
55
- ## Save badge
46
+ The bundle title renders as a plain section heading in the host theme's own
47
+ type, using `--lb-text`. There is no colored header band and no save-badge pill.
48
+ Only the countdown timer carries its own colours.
56
49
 
57
50
  | Variable | Default | What it styles |
58
51
  |---|---|---|
59
- | `--lb-save-badge-bg` | `#FFFFFF` | Background. |
60
- | `--lb-save-badge-text` | `#AD1457` | Text colour. |
61
- | `--lb-save-badge-border-color` | `#FFFFFF` | Border colour. |
62
- | `--lb-save-badge-border-width` | `0px` | Border width. |
63
- | `--lb-save-badge-radius` | `8px` | Corner radius. |
52
+ | `--lb-countdown-bg` | `#FFF0F3` | Countdown bar background. |
53
+ | `--lb-countdown-text` | `#C62828` | Countdown bar text colour. |
64
54
 
65
55
  ## Product list
66
56
 
57
+ Product thumbnails carry no border; they round to a tighter radius derived from
58
+ `--lb-radius`. The variant selector inherits the global `--lb-border` /
59
+ `--lb-border-width` and the same derived radius, so there are no per-element
60
+ border variables here.
61
+
67
62
  | Variable | Default | What it styles |
68
63
  |---|---|---|
69
- | `--lb-image-border-color` | `#E5E5E5` | Product thumbnail border. |
70
- | `--lb-image-border-width` | `0px` | Product thumbnail border width. |
71
- | `--lb-image-border-radius` | `8px` | Product thumbnail corner radius. |
72
- | `--lb-variant-border-color` | `#E5E5E5` | Variant selector border. |
73
- | `--lb-variant-border-width` | `1px` | Variant selector border width. |
74
- | `--lb-variant-radius` | `8px` | Variant selector corner radius. |
75
- | `--lb-variant-chevron` | *derived SVG data URL* | Chevron icon shown in the variant dropdown. Auto-coloured to match `--lb-variant-border-color`. |
76
-
77
- The variant dropdown is an accessible custom combobox (not a native `<select>`). Its open panel reuses `--lb-variant-border-color`, `--lb-variant-border-width`, and `--lb-variant-radius` for the panel border, `--lb-bg` for the panel background, `--lb-text` for option text and the 4px scrollbar thumb, and `--lb-primary-color` for the focus ring. No additional variables required — adjusting the border or text/background colour automatically restyles the open panel to match.
78
-
79
- | `--lb-qty-badge-bg` | `#1A1A1A` | Quantity badge background. |
80
- | `--lb-qty-badge-color` | `#FFFFFF` | Quantity badge text. |
81
- | `--lb-qty-badge-display` | `flex` (shown) / `none` (hidden) | Toggle; merchant config controls this. |
64
+ | `--lb-variant-chevron` | *derived SVG data URL* | Chevron icon shown in the variant dropdown. Auto-coloured to match `--lb-border`. |
65
+
66
+ The variant dropdown is an accessible custom combobox (not a native `<select>`). Its open panel reuses `--lb-border` and `--lb-border-width` for the panel border, `--lb-bg` for the panel background, `--lb-text` for option text and the scrollbar thumb, and `--lb-primary-color` for the focus ring. No additional variables required: adjusting the global border or text/background colour automatically restyles the open panel to match.
67
+
82
68
  | `--lb-product-price-display` | `block` (shown) / `none` (hidden) | Toggle. |
83
69
  | `--lb-product-compare-display` | `inline` (shown) / `none` (hidden) | Toggle. |
70
+ | `--lb-product-unit-price-display` | `block` (shown) / `none` (hidden) | Toggle for the per-unit line (`$4.99/kg`) under a product's price. Separate from the bundle summary's per-item figure. |
71
+ | `--lb-product-qty-chip-display` | `inline-flex` (shown) / `none` (hidden) | Toggle for the fixed bundle's right-slot quantity chip. |
72
+ | `--lb-product-qty-inline-display` | `inline` (shown) / `none` (hidden) | Toggle for the inline `×N` count used by every other bundle type. |
73
+
74
+ Per-product quantity renders as inline `×N` text beside the price (no floating
75
+ count badge), using the widget's own `--lb-text`. The fixed bundle instead puts
76
+ its count in a chip in the row's right slot, which is why one merchant setting
77
+ drives two variables: the chip is a flex box and the inline count is text, so a
78
+ single "shown" value cannot serve both.
84
79
 
85
80
  ## CTA button
86
81
 
@@ -88,29 +83,24 @@ The variant dropdown is an accessible custom combobox (not a native `<select>`).
88
83
  |---|---|---|
89
84
  | `--lb-primary-color` | `#1A1A1A` | Button background. |
90
85
  | `--lb-btn-text` | `#FFFFFF` | Button text. |
91
- | `--lb-cta-border-color` | `#1A1A1A` | Border colour. |
86
+ | `--lb-cta-border-color` | `#1A1A1A` | Border colour. The button keeps its own border (the global border does not touch it). |
92
87
  | `--lb-cta-border-width` | `0px` | Border width. |
93
- | `--lb-cta-radius` | `12px` | Corner radius. |
94
88
 
95
- ## Savings bar
89
+ ## Savings line
96
90
 
97
- | Variable | Default | What it styles |
98
- |---|---|---|
99
- | `--lb-savings-bar-bg` | `#EDFBF1` | Bar background. |
100
- | `--lb-savings-bar-text` | `#2DB554` | Bar text colour. |
101
- | `--lb-savings-bar-border-color` | `#2DB554` | Border colour. |
102
- | `--lb-savings-bar-border-width` | `0px` | Border width. |
103
- | `--lb-savings-bar-radius` | `8px` | Corner radius. |
91
+ The "You save {amount} ({percent}%)" line sits beneath the receipt total. It is
92
+ quiet text with no colours of its own — it inherits the widget's `--lb-text`
93
+ (lime is reserved for the CTA). Toggle it via the `savingsBar.visible` config
94
+ field; there are no savings-line CSS variables.
104
95
 
105
96
  ## Volume tier cards
106
97
 
107
- | Variable | Default | What it styles |
108
- |---|---|---|
109
- | `--lb-tier-border-color` | `#E5E5E5` | Tier card border. |
110
- | `--lb-tier-border-width` | `1px` | Tier card border width. |
111
- | `--lb-tier-radius` | `12px` | Tier card corner radius. |
112
- | `--lb-tier-selected-border-color` | `#1A1A1A` | Border colour for the active tier. |
113
- | `--lb-tier-selected-border-width` | `2px` | Border width for the active tier. |
98
+ Tier cards inherit the global border (`--lb-border` / `--lb-border-width`) and the
99
+ global corner radius (`--lb-radius`), so they have no dedicated border variables.
100
+
101
+ The selected tier is highlighted with a quiet neutral tint and a stronger
102
+ border derived from `--lb-text` (matching the mix & match in-bundle card), so it
103
+ has no dedicated CSS variables.
114
104
 
115
105
  ## "Most popular" badge (volume bundle)
116
106
 
@@ -120,7 +110,9 @@ The variant dropdown is an accessible custom combobox (not a native `<select>`).
120
110
  | `--lb-popular-badge-text` | `#FFFFFF` | Badge text. |
121
111
  | `--lb-popular-badge-border-color` | `#1A1A1A` | Badge border colour. |
122
112
  | `--lb-popular-badge-border-width` | `0px` | Badge border width. |
123
- | `--lb-popular-badge-radius` | `12px` | Badge corner radius. |
113
+
114
+ The badge keeps its own border width + colour, but its corner radius follows the
115
+ global radius preset (the derived `--lb-radius-sm`).
124
116
 
125
117
  ## "Only X left" low-stock badge
126
118
 
@@ -135,34 +127,23 @@ Surfaces when a variant's `quantityAvailable` is at or below
135
127
 
136
128
  ## Mix-and-match picker modal
137
129
 
130
+ One "Borders" group drives every border inside the modal (the modal itself, the
131
+ search input, product tiles, the variant select, and the quantity stepper).
132
+ Thumbnails take the radius only (no border). The Add button keeps its own border
133
+ width + colour but shares the radius.
134
+
138
135
  | Variable | Default | What it styles |
139
136
  |---|---|---|
140
137
  | `--lb-picker-bg` | `#FFFFFF` | Modal background. |
141
138
  | `--lb-picker-text` | `#1A1A1A` | Modal text colour. |
142
- | `--lb-picker-border-color` | `#E5E5E5` | Modal outer border. |
143
- | `--lb-picker-border-width` | `0px` | Modal outer border width. |
144
- | `--lb-picker-radius` | `16px` | Modal corner radius. |
145
- | `--lb-picker-search-border-color` | `#E5E5E5` | Search input border. |
146
- | `--lb-picker-search-border-width` | `1px` | Search input border width. |
147
- | `--lb-picker-search-radius` | `8px` | Search input corner radius. |
148
- | `--lb-picker-product-border-color` | `#E5E5E5` | Product thumbnail border. |
149
- | `--lb-picker-product-border-width` | `0px` | Product thumbnail border width. |
150
- | `--lb-picker-product-radius` | `8px` | Product thumbnail corner radius. |
151
- | `--lb-picker-qty-badge-bg` | `#1A1A1A` | Picker quantity-badge background. |
152
- | `--lb-picker-qty-badge-color` | `#FFFFFF` | Picker quantity-badge text. |
153
- | `--lb-picker-qty-badge-display` | `flex` (shown) / `none` (hidden) | Toggle; picker quantity badge visibility. |
154
- | `--lb-picker-variant-border-color` | `#1A1A1A` | Variant select border. |
155
- | `--lb-picker-variant-border-width` | `1px` | Variant select border width. |
156
- | `--lb-picker-variant-radius` | `8px` | Variant select corner radius. |
157
- | `--lb-picker-variant-chevron` | *derived SVG data URL* | Chevron icon on the picker's variant dropdown. Auto-coloured to match `--lb-picker-variant-border-color`. |
158
- | `--lb-picker-qty-stepper-border-color` | `#1A1A1A` | Per-pick quantity stepper border. |
159
- | `--lb-picker-qty-stepper-border-width` | `1px` | Per-pick quantity stepper border width. |
160
- | `--lb-picker-qty-stepper-radius` | `8px` | Per-pick quantity stepper corner radius. |
139
+ | `--lb-picker-border-color` | `#E5E5E5` | Border colour for every element in the modal. |
140
+ | `--lb-picker-border-width` | `1px` | Border width for every element in the modal. |
141
+ | `--lb-picker-radius` | `16px` | Corner radius every element in the modal rounds to. |
142
+ | `--lb-picker-variant-chevron` | *derived SVG data URL* | Chevron icon on the picker's variant dropdown. Auto-coloured to match `--lb-picker-border-color`. |
161
143
  | `--lb-picker-add-bg` | `#1A1A1A` | Add-to-bundle button background. |
162
144
  | `--lb-picker-add-label` | `#FFFFFF` | Add-to-bundle button text. |
163
- | `--lb-picker-add-border-color` | `#1A1A1A` | Add-to-bundle button border colour. |
164
- | `--lb-picker-add-border-width` | `0px` | Add-to-bundle button border width. |
165
- | `--lb-picker-add-radius` | `8px` | Add-to-bundle button corner radius. |
145
+ | `--lb-picker-add-border-color` | `#1A1A1A` | Add-to-bundle button border colour (its own, not the modal Borders). |
146
+ | `--lb-picker-add-border-width` | `0px` | Add-to-bundle button border width (its own). |
166
147
 
167
148
  ## Internal variables (don't override)
168
149
 
@@ -170,7 +151,6 @@ These exist for internal composition and are not merchant-configurable. The CSS
170
151
 
171
152
  - `--lb-thumbnail-bg`: placeholder background behind images.
172
153
  - `--lb-widget-pad`: internal padding token.
173
- - `--lb-progress-color`: volume progress bar accent.
174
154
  - `--lb-thumbnail-aspect-ratio`: derived from the merchant's `thumbnailRatio` setting (`square`/`tall`/`wide`/`original`). `1 / 1`, `3 / 4`, `4 / 3`, or `auto`. Override only if you need a custom thumb shape that isn't one of the four presets.
175
155
  - `--lb-thumbnail-img-fit`: derived from `thumbnailRatio`. `cover` for the cropping presets, `contain` for `original`.
176
156
  - `--lb-thumbnail-img-height`: derived from `thumbnailRatio`. `100%` for the cropping presets, `auto` for `original` so the image renders at its intrinsic ratio.
package/docs/hydrogen.md CHANGED
@@ -27,7 +27,7 @@ PUBLIC_LIME_BUNDLES_TOKEN=<paste the generated token>
27
27
 
28
28
  The variable is intentionally namespaced so it doesn't collide with the `PUBLIC_STOREFRONT_API_TOKEN` that Shopify's Headless app issues for your primary storefront. Lime Bundles uses its own Storefront Access Token, scoped to bundle metaobjects and product listings only.
29
29
 
30
- Lime Bundles creates the token for you via Shopify's Admin API. No custom-app configuration needed (Shopify deprecated that flow on 2026-01-01). The token carries the read-only scopes `unauthenticated_read_metaobjects` and `unauthenticated_read_product_listings` and nothing else. To rotate, click **Regenerate** on the same page. It revokes the current token and issues a new one atomically.
30
+ Lime Bundles creates the token for you via Shopify's Admin API. No custom-app configuration needed (Shopify deprecated that flow on 2026-01-01). The token carries the read-only scopes `unauthenticated_read_metaobjects`, `unauthenticated_read_product_listings` and `unauthenticated_read_product_inventory` and nothing else. To rotate, click **Regenerate** on the same page. It revokes the current token and issues a new one atomically.
31
31
 
32
32
  ## Getting a bundle GID
33
33
 
@@ -27,7 +27,7 @@ NEXT_PUBLIC_LIME_BUNDLES_TOKEN=<paste the generated token>
27
27
 
28
28
  The variable is intentionally namespaced to `LIME_BUNDLES_` so it doesn't collide with the Storefront API token Shopify's own Headless app issues for your primary storefront. Lime Bundles uses its own Storefront Access Token, scoped to bundle metaobjects and product listings only.
29
29
 
30
- The `NEXT_PUBLIC_` prefix (or `VITE_` for Vite) exposes the value to client bundles. Lime Bundles creates the token for you via Shopify's Admin API. No custom-app setup needed (Shopify deprecated that flow on 2026-01-01). The token is a read-only public Storefront Access Token with `unauthenticated_read_metaobjects` and `unauthenticated_read_product_listings` scopes, safe to ship in client code. To rotate, click **Regenerate** in the admin. It atomically revokes the old token and issues a new one.
30
+ The `NEXT_PUBLIC_` prefix (or `VITE_` for Vite) exposes the value to client bundles. Lime Bundles creates the token for you via Shopify's Admin API. No custom-app setup needed (Shopify deprecated that flow on 2026-01-01). The token is a read-only public Storefront Access Token with `unauthenticated_read_metaobjects`, `unauthenticated_read_product_listings` and `unauthenticated_read_product_inventory` scopes, safe to ship in client code. To rotate, click **Regenerate** in the admin. It atomically revokes the old token and issues a new one.
31
31
 
32
32
  ## Pick a rendering mode
33
33
 
@@ -357,7 +357,9 @@ import { injectCustomCss, sanitizeCustomCss } from "@lime-bundles/react";
357
357
  ```tsx
358
358
  import { validateQuantity } from "@lime-bundles/react";
359
359
 
360
- const { valid, message } = validateQuantity(selectedCount, bundle.minQuantity, bundle.maxQuantity);
360
+ // Pass the total units selected (sum of per-product quantities) — completion
361
+ // counts units, so one product at quantity 3 satisfies minQuantity: 3.
362
+ const { valid, message } = validateQuantity(totalUnits, bundle.minQuantity, bundle.maxQuantity);
361
363
  ```
362
364
 
363
365
  Anything the web component does, a developer importing from `@lime-bundles/react` can do too. The built-in components are one way to arrange these primitives; ship your own the moment you need a different one.
@@ -295,7 +295,7 @@ Every part of the widget a merchant configures in the admin is wired up for you:
295
295
  - **Countdown timer.** When a bundle has `endsAt`, the widget ticks a live countdown every second and hides the bar once the offer expires.
296
296
  - **Variant dropdowns on fixed bundles.** Products with multiple eligible variants (filtered by the merchant's `selectedVariantIds`) render a `<select>`; switching variants live-updates the row price and the bundle total.
297
297
  - **Mix-match picker modal.** Click any empty slot to open the modal; search, quantity stepper, progress bar, and pricing update as selections change. Keyboard: Escape closes, Tab traps inside the modal.
298
- - **Out-of-stock behaviour.** Honours `widgetConfig.outOfStockBehavior` (`"hide"` removes OOS products from the list; `"show_greyed_out"` renders them disabled). Fixed bundles hide the whole widget when required products are OOS; mix-match hides when the available count can't satisfy `minQuantity`.
298
+ - **Out-of-stock behaviour.** Honours `widgetConfig.outOfStockBehavior` (`"hide"` removes OOS products from the list; `"show_greyed_out"` renders them disabled). Fixed bundles hide the whole widget when required products are OOS; mix-match hides when the count of in-stock products falls below `minQuantity` (completion itself counts total units selected, not distinct products).
299
299
  - **A/B test assignment (link-group model).** When a bundle is the primary of an A/B test, the widget reads the link group's variant weights from `data-link-group-variants`, walks the cumulative FNV-1a weight distribution via `getLinkGroupAssignment`, and renders the assigned variant (which is a real Bundle with its own products, discount, and widget config — not an overlay). Cookie-persisted, consent-gated; honours Shopify's `customerPrivacy` framework or the SDK's `setConsent(true)` helper. Without consent the primary renders and no cookie is written.
300
300
  - **Impression + add-to-cart analytics.** Fire on visibility + CTA click regardless of whether the merchant takes over the cart via `preventDefault`. Disable by setting `analytics="false"` or omitting `app-url`.
301
301
  - **Purchase attribution.** The `orders/create` webhook ingests `bundle_purchased` events server-side from the `_lime_bundle_gid` cart attribute the widget adds automatically. No `checkout_completed` handler needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lime-bundles/react",
3
- "version": "5.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "React components and hooks for the Lime Bundles Shopify app. Use on Hydrogen, Next.js, Vite, or any React-based headless storefront.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -32,7 +32,7 @@
32
32
  "url": "https://github.com/lime-app-dev/lime-bundles-app/issues"
33
33
  },
34
34
  "license": "MIT",
35
- "author": "lime-app-dev",
35
+ "author": "Lime App Dev (Pty) Ltd",
36
36
  "keywords": [
37
37
  "shopify",
38
38
  "bundles",
@@ -52,7 +52,7 @@
52
52
  "react-dom": ">=18.0.0"
53
53
  },
54
54
  "dependencies": {
55
- "@lime-bundles/core": "^5.0.0"
55
+ "@lime-bundles/core": "^6.2.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@shopify/hydrogen-react": "^2026.4.1",