@code-collective/booking-widget 1.0.2 → 1.0.4

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.
package/README.md CHANGED
@@ -1,282 +1,318 @@
1
- # Booking Widget
2
-
3
- A Svelte 5 widget library for embedding ticket sales on tourism websites. Three custom HTML elements handle product selection, cart management, and payment via Peach Payments.
4
-
5
- ## Option 1: HTML Custom Elements (CDN)
6
-
7
- Drop in a script tag and use the custom elements directly. No framework or npm install required.
8
-
9
- ```html
10
- <head>
11
- <link rel="stylesheet"
12
- href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
13
- integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
14
- crossorigin="anonymous" />
15
- </head>
16
- <body>
17
- <bw-configurator product-id="your-product-id" checkout-key="your-checkout-key"></bw-configurator>
18
- <bw-cart display="button"></bw-cart>
19
- <bw-cart display="bar"></bw-cart>
20
- <bw-checkout></bw-checkout>
21
-
22
- <script
23
- src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
24
- integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
25
- crossorigin="anonymous"></script>
26
- </body>
27
- ```
28
-
29
- > **Note:** Pin to a specific version (e.g. `@1.0.0`) when using SRI. The hashes change with every release. Run `npm run build:elements` to see the current hashes.
30
-
31
- The script auto-wires the elements together:
32
- - Adding an item shows the cart and opens the checkout overlay
33
- - Clicking checkout in the cart opens the overlay
34
- - Closing or completing checkout closes the overlay
35
-
36
- ### Element attributes
37
-
38
- #### `<bw-configurator>`
39
-
40
- | Attribute | Required | Description |
41
- |---|---|---|
42
- | `product-id` | Yes | OCTO product ID |
43
- | `checkout-key` | Yes | Public key identifying the supplier |
44
- | `wizard-pages` | No | JSON string for wizard step ordering |
45
- | `auto-select-single-time-slot` | No | Boolean. Skips time picker if only one slot |
46
- | `cancelable` | No | Boolean. Shows a cancel button |
47
- | `no-auto-checkout` | No | Boolean. Prevents auto-opening checkout on add |
48
- | `on-cart-change` | No | Name of a global JS function to call on add |
49
-
50
- #### `<bw-cart>`
51
-
52
- | Attribute | Required | Description |
53
- |---|---|---|
54
- | `checkout-key` | Yes | Public key identifying the supplier |
55
- | `display` | No | `bar` (default) or `button` |
56
-
57
- #### `<bw-checkout>`
58
-
59
- | Attribute | Required | Description |
60
- |---|---|---|
61
- | `checkout-key` | Yes | Public key identifying the supplier |
62
- | `wizard-pages` | No | JSON string for wizard step ordering (edit flow) |
63
-
64
- ### Global options
65
-
66
- Set `window.bwOptions` before the widget script loads:
67
-
68
- ```html
69
- <script>
70
- window.bwOptions = {
71
- shouldBottomCloseOnModal: true,
72
- autoSelectSingleTimeSlot: false,
73
- wizardPages: '',
74
- };
75
- </script>
76
- <script src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
77
- integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
78
- crossorigin="anonymous"></script>
79
- ```
80
-
81
- | Option | Default | Description |
82
- |---|---|---|
83
- | `shouldBottomCloseOnModal` | `true` | Hide bar carts when checkout modal opens |
84
- | `autoSelectSingleTimeSlot` | `false` | Auto-select when only one time slot |
85
- | `wizardPages` | `''` | JSON wizard step ordering for all elements |
86
-
87
- ### Window events
88
-
89
- All `bw:*` events are re-dispatched on `window`:
90
-
91
- ```js
92
- window.addEventListener('bw:order-confirmed', (e) => {
93
- console.log(e.detail); // { cartToken, value, currency }
94
- });
95
- ```
96
-
97
- | Event | Detail | When |
98
- |---|---|---|
99
- | `bw:cart-change` | `{ itemCount, cartItemId, totalFormatted }` | Item added to cart |
100
- | `bw:order-confirmed` | `{ cartToken, value, currency }` | Payment completed |
101
- | `bw:modal-open` | -- | Checkout modal opens |
102
- | `bw:modal-close` | -- | Checkout modal closes |
103
- | `bw:checkout` | -- | Cart checkout button clicked |
104
- | `bw:close` | -- | Checkout dismissed |
105
- | `bw:cancel` | -- | Configurator cancelled |
106
-
107
- ## Option 2: JavaScript mount functions
108
-
109
- For programmatic control in an Astro, Vite, or bundled project, install the package and import the ES module:
110
-
111
- ```bash
112
- npm install @code-collective/booking-widget
113
- ```
114
-
115
- ```js
116
- import { mountConfigurator, mountCheckout, mountCartOverview } from '@code-collective/booking-widget';
117
- import '@code-collective/booking-widget/style.css';
118
-
119
- const widget = await mountConfigurator(document.getElementById('configurator'), {
120
- productId: 'your-product-id',
121
- checkoutKey: 'your-checkout-key',
122
- apiBaseUrl: 'https://checkout.yourdomain.com',
123
- onCartChange({ itemCount, totalFormatted }) {
124
- console.log(`${itemCount} items, ${totalFormatted}`);
125
- },
126
- });
127
-
128
- // Later: widget.destroy();
129
- ```
130
-
131
- ### Mount functions
132
-
133
- **`mountConfigurator(target, config)`** — Product selection wizard
134
-
135
- | Config | Required | Description |
136
- |---|---|---|
137
- | `productId` | Yes | OCTO product ID |
138
- | `checkoutKey` | No | Supplier checkout key |
139
- | `apiBaseUrl` | No | Checkout API URL |
140
- | `wizardPages` | No | Wizard step ordering |
141
- | `autoSelectSingleTimeSlot` | No | Skip time picker if single slot |
142
- | `onCartChange` | No | Callback: `({ itemCount, cartItemId, totalFormatted })` |
143
- | `onCancel` | No | Callback when cancelled |
144
-
145
- **`mountCheckout(target, config)`** — Cart review, contact form, payment
146
-
147
- | Config | Required | Description |
148
- |---|---|---|
149
- | `checkoutKey` | No | Supplier checkout key |
150
- | `apiBaseUrl` | No | Checkout API URL |
151
- | `onClose` | No | Callback when closed |
152
- | `onOrderConfirmed` | No | Callback: `({ cartToken, value, currency })` |
153
-
154
- **`mountCartOverview(target, config)`** — Cart summary
155
-
156
- | Config | Required | Description |
157
- |---|---|---|
158
- | `checkoutKey` | No | Supplier checkout key |
159
- | `apiBaseUrl` | No | Checkout API URL |
160
- | `display` | No | `bar` or `button` |
161
- | `onCheckout` | No | Callback when checkout clicked |
162
-
163
- All mount functions return `Promise<{ destroy(): void }>`.
164
-
165
- ## Option 3: Svelte components
166
-
167
- Install the package and import Svelte components directly for full reactivity:
168
-
169
- ```bash
170
- npm install @code-collective/booking-widget
171
- ```
172
-
173
- ```svelte
174
- <script lang="ts">
175
- import { TicketConfigurator, Checkout, CartBar, CartOverviewButton } from '@code-collective/booking-widget';
176
- import { ApiClient, SessionManager, CartManager } from '@code-collective/booking-widget';
177
- import '@code-collective/booking-widget/style.css';
178
-
179
- const api = new ApiClient('https://checkout.yourdomain.com');
180
- const sessionManager = new SessionManager(api);
181
- const cartManager = new CartManager(api);
182
- sessionManager.startBackgroundRefresh();
183
-
184
- let ready = $state(false);
185
- sessionManager.ensureSession('your-checkout-key').then(() => { ready = true; });
186
- </script>
187
-
188
- {#if ready}
189
- <TicketConfigurator {api} {cartManager} productId="your-product-id" />
190
- <CartOverviewButton {api} {cartManager} />
191
- <Checkout {api} />
192
- {/if}
193
- ```
194
-
195
- ## Astro integration
196
-
197
- ### Script tag approach
198
-
199
- ```astro
200
- ---
201
- const product = await getProduct(Astro.params.slug);
202
- ---
203
-
204
- <bw-configurator product-id={product.id} checkout-key={product.checkoutKey}></bw-configurator>
205
- <bw-cart display="bar" checkout-key={product.checkoutKey}></bw-cart>
206
- <bw-checkout checkout-key={product.checkoutKey}></bw-checkout>
207
-
208
- <link rel="stylesheet"
209
- href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
210
- integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
211
- crossorigin="anonymous" />
212
- <script is:inline
213
- src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
214
- integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
215
- crossorigin="anonymous"></script>
216
- ```
217
-
218
- ### Svelte island approach
219
-
220
- ```bash
221
- npx astro add svelte
222
- npm install @code-collective/booking-widget
223
- ```
224
-
225
- ```astro
226
- ---
227
- import BookingWidget from '../components/BookingWidget.svelte';
228
- const product = await getProduct(Astro.params.slug);
229
- ---
230
-
231
- <BookingWidget
232
- client:load
233
- productId={product.id}
234
- checkoutKey={product.checkoutKey}
235
- apiBaseUrl="https://checkout.yourdomain.com"
236
- />
237
- ```
238
-
239
- ## Wizard page ordering
240
-
241
- Control the step sequence with `wizardPages`:
242
-
243
- **Option-first (default):**
244
- ```json
245
- [["option", "age-category"], ["date", "time"], ["pickup"]]
246
- ```
247
-
248
- **Age-first:**
249
- ```json
250
- [["age-category"], ["date", "time"], ["option"], ["pickup"]]
251
- ```
252
-
253
- ## Theming
254
-
255
- The widget reads CSS custom properties. It automatically picks up site variables (`--header-background`, `--default-font-family`, `--radius`) or can be themed directly:
256
-
257
- ```css
258
- :root {
259
- --bw-color-primary: #0066CC;
260
- --bw-color-primary-dark: #004C99;
261
- --bw-font-family: 'Inter', sans-serif;
262
- --bw-radius-md: 4px;
263
- }
264
- ```
265
-
266
- | Variable | Fallback | Default |
267
- |---|---|---|
268
- | `--bw-color-primary` | `--header-background` | `#E30613` |
269
- | `--bw-color-primary-dark` | `--footer-background` | `#C00510` |
270
- | `--bw-color-primary-light` | -- | `rgba(227,6,19,0.08)` |
271
- | `--bw-color-text` | -- | `#212121` |
272
- | `--bw-color-text-secondary` | -- | `#757575` |
273
- | `--bw-color-border` | -- | `#E0E0E0` |
274
- | `--bw-color-bg` | -- | `#FFFFFF` |
275
- | `--bw-color-surface` | -- | `#F5F5F5` |
276
- | `--bw-color-success` | -- | `#4CAF50` |
277
- | `--bw-color-error` | `--header-background` | `#E30613` |
278
- | `--bw-font-family` | `--default-font-family` | `Roboto, system` |
279
- | `--bw-radius-sm` | -- | `4px` |
280
- | `--bw-radius-md` | `--radius` | `8px` |
281
- | `--bw-radius-lg` | -- | `12px` |
282
- | `--bw-transition` | -- | `0.15s ease` |
1
+ # Booking Widget
2
+
3
+ A Svelte 5 widget library for embedding ticket sales on tourism websites. Three custom HTML elements handle product selection, cart management, and payment via Peach Payments.
4
+
5
+ ## Option 1: HTML Custom Elements (CDN)
6
+
7
+ Drop in a script tag and use the custom elements directly. No framework or npm install required.
8
+
9
+ ```html
10
+ <head>
11
+ <link rel="stylesheet"
12
+ href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
13
+ integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
14
+ crossorigin="anonymous" />
15
+ </head>
16
+ <body>
17
+ <bw-configurator product-id="your-product-id" checkout-key="your-checkout-key"></bw-configurator>
18
+ <bw-cart display="button"></bw-cart>
19
+ <bw-cart display="bar"></bw-cart>
20
+ <bw-checkout></bw-checkout>
21
+
22
+ <script
23
+ src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
24
+ integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
25
+ crossorigin="anonymous"></script>
26
+ </body>
27
+ ```
28
+
29
+ > **Note:** Pin to a specific version (e.g. `@1.0.0`) when using SRI. The hashes change with every release. Run `npm run build:elements` to see the current hashes.
30
+
31
+ The script auto-wires the elements together:
32
+ - Adding an item shows the cart and opens the checkout overlay
33
+ - Clicking checkout in the cart opens the overlay
34
+ - Closing or completing checkout closes the overlay
35
+
36
+ ### Element attributes
37
+
38
+ #### `<bw-configurator>`
39
+
40
+ | Attribute | Required | Description |
41
+ |---|---|---|
42
+ | `product-id` | Yes | OCTO product ID |
43
+ | `checkout-key` | Yes | Public key identifying the supplier |
44
+ | `wizard-pages` | No | JSON string for wizard step ordering |
45
+ | `auto-select-single-time-slot` | No | Boolean. Skips time picker if only one slot |
46
+ | `cancelable` | No | Boolean. Shows a cancel button |
47
+ | `no-auto-checkout` | No | Boolean. Prevents auto-opening checkout on add |
48
+ | `on-cart-change` | No | Name of a global JS function to call on add |
49
+
50
+ #### `<bw-cart>`
51
+
52
+ | Attribute | Required | Description |
53
+ |---|---|---|
54
+ | `checkout-key` | Yes | Public key identifying the supplier |
55
+ | `display` | No | `bar` (default) or `button` |
56
+
57
+ #### `<bw-checkout>`
58
+
59
+ | Attribute | Required | Description |
60
+ |---|---|---|
61
+ | `checkout-key` | Yes | Public key identifying the supplier |
62
+ | `wizard-pages` | No | JSON string for wizard step ordering (edit flow) |
63
+
64
+ ### Global options
65
+
66
+ Set `window.bwOptions` before the widget script loads:
67
+
68
+ ```html
69
+ <script>
70
+ window.bwOptions = {
71
+ shouldBottomCloseOnModal: true,
72
+ autoSelectSingleTimeSlot: false,
73
+ wizardPages: '',
74
+ };
75
+ </script>
76
+ <script src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
77
+ integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
78
+ crossorigin="anonymous"></script>
79
+ ```
80
+
81
+ | Option | Default | Description |
82
+ |---|---|---|
83
+ | `shouldBottomCloseOnModal` | `true` | Hide bar carts when checkout modal opens |
84
+ | `autoSelectSingleTimeSlot` | `false` | Auto-select when only one time slot |
85
+ | `wizardPages` | `''` | JSON wizard step ordering for all elements |
86
+
87
+ ### Window events
88
+
89
+ All `bw:*` events are re-dispatched on `window`:
90
+
91
+ ```js
92
+ window.addEventListener('bw:order-confirmed', (e) => {
93
+ console.log(e.detail); // { cartToken, value, currency }
94
+ });
95
+ ```
96
+
97
+ | Event | Detail | When |
98
+ |---|---|---|
99
+ | `bw:cart-change` | `{ itemCount, cartItemId, totalFormatted }` | Item added to cart |
100
+ | `bw:order-confirmed` | `{ cartToken, value, currency }` | Payment completed |
101
+ | `bw:modal-open` | -- | Checkout modal opens |
102
+ | `bw:modal-close` | -- | Checkout modal closes |
103
+ | `bw:checkout` | -- | Cart checkout button clicked |
104
+ | `bw:close` | -- | Checkout dismissed |
105
+ | `bw:cancel` | -- | Configurator cancelled |
106
+
107
+ ## Option 2: JavaScript mount functions
108
+
109
+ For programmatic control in an Astro, Vite, or bundled project, install the package and import the ES module:
110
+
111
+ ```bash
112
+ npm install @code-collective/booking-widget
113
+ ```
114
+
115
+ ```js
116
+ import { mountConfigurator, mountCheckout, mountCartOverview } from '@code-collective/booking-widget';
117
+ import '@code-collective/booking-widget/style.css';
118
+
119
+ const widget = await mountConfigurator(document.getElementById('configurator'), {
120
+ productId: 'your-product-id',
121
+ checkoutKey: 'your-checkout-key',
122
+ apiBaseUrl: 'https://checkout.yourdomain.com',
123
+ onCartChange({ itemCount, totalFormatted }) {
124
+ console.log(`${itemCount} items, ${totalFormatted}`);
125
+ },
126
+ });
127
+
128
+ // Later: widget.destroy();
129
+ ```
130
+
131
+ ### Mount functions
132
+
133
+ **`mountConfigurator(target, config)`** — Product selection wizard
134
+
135
+ | Config | Required | Description |
136
+ |---|---|---|
137
+ | `productId` | Yes | OCTO product ID |
138
+ | `checkoutKey` | No | Supplier checkout key |
139
+ | `apiBaseUrl` | No | Checkout API URL |
140
+ | `wizardPages` | No | Wizard step ordering |
141
+ | `autoSelectSingleTimeSlot` | No | Skip time picker if single slot |
142
+ | `onCartChange` | No | Callback: `({ itemCount, cartItemId, totalFormatted })` |
143
+ | `onCancel` | No | Callback when cancelled |
144
+
145
+ **`mountCheckout(target, config)`** — Cart review, contact form, payment
146
+
147
+ | Config | Required | Description |
148
+ |---|---|---|
149
+ | `checkoutKey` | No | Supplier checkout key |
150
+ | `apiBaseUrl` | No | Checkout API URL |
151
+ | `onClose` | No | Callback when closed |
152
+ | `onOrderConfirmed` | No | Callback: `({ cartToken, value, currency })` |
153
+
154
+ **`mountCartOverview(target, config)`** — Cart summary
155
+
156
+ | Config | Required | Description |
157
+ |---|---|---|
158
+ | `checkoutKey` | No | Supplier checkout key |
159
+ | `apiBaseUrl` | No | Checkout API URL |
160
+ | `display` | No | `bar` or `button` |
161
+ | `onCheckout` | No | Callback when checkout clicked |
162
+
163
+ All mount functions return `Promise<{ destroy(): void }>`.
164
+
165
+ ## Option 3: Svelte components
166
+
167
+ Install the package and import Svelte components directly for full reactivity:
168
+
169
+ ```bash
170
+ npm install @code-collective/booking-widget
171
+ ```
172
+
173
+ ```svelte
174
+ <script lang="ts">
175
+ import { TicketConfigurator, Checkout, CartBar, CartOverviewButton } from '@code-collective/booking-widget';
176
+ import { ApiClient, SessionManager, CartManager } from '@code-collective/booking-widget';
177
+ import '@code-collective/booking-widget/style.css';
178
+
179
+ const api = new ApiClient('https://checkout.yourdomain.com');
180
+ const sessionManager = new SessionManager(api);
181
+ const cartManager = new CartManager(api);
182
+ sessionManager.startBackgroundRefresh();
183
+
184
+ let ready = $state(false);
185
+ sessionManager.ensureSession('your-checkout-key').then(() => { ready = true; });
186
+ </script>
187
+
188
+ {#if ready}
189
+ <TicketConfigurator {api} {cartManager} productId="your-product-id" />
190
+ <CartOverviewButton {api} {cartManager} />
191
+ <Checkout {api} />
192
+ {/if}
193
+ ```
194
+
195
+ ## Astro integration
196
+
197
+ ### Script tag approach
198
+
199
+ ```astro
200
+ ---
201
+ const product = await getProduct(Astro.params.slug);
202
+ ---
203
+
204
+ <bw-configurator product-id={product.id} checkout-key={product.checkoutKey}></bw-configurator>
205
+ <bw-cart display="bar" checkout-key={product.checkoutKey}></bw-cart>
206
+ <bw-checkout checkout-key={product.checkoutKey}></bw-checkout>
207
+
208
+ <link rel="stylesheet"
209
+ href="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.css"
210
+ integrity="sha384-Chz7xKNGRdJXbNwkaJzy45I9DiwPStkzNQV9VgWMNKgnlHCpgnhbiukEJEtbHt+v"
211
+ crossorigin="anonymous" />
212
+ <script is:inline
213
+ src="https://cdn.jsdelivr.net/npm/@code-collective/booking-widget@1.0.2/dist/booking-widget.min.js"
214
+ integrity="sha384-y2Ncw7PWI9NxiwKR0nhDk+ddkYmUyl+kxFyU+B4eDXWt3bfcMk1CRVTXXUwhTgJd"
215
+ crossorigin="anonymous"></script>
216
+ ```
217
+
218
+ ### Svelte island approach
219
+
220
+ ```bash
221
+ npx astro add svelte
222
+ npm install @code-collective/booking-widget
223
+ ```
224
+
225
+ ```astro
226
+ ---
227
+ import BookingWidget from '../components/BookingWidget.svelte';
228
+ const product = await getProduct(Astro.params.slug);
229
+ ---
230
+
231
+ <BookingWidget
232
+ client:load
233
+ productId={product.id}
234
+ checkoutKey={product.checkoutKey}
235
+ apiBaseUrl="https://checkout.yourdomain.com"
236
+ />
237
+ ```
238
+
239
+ ## Wizard pages
240
+
241
+ Each wizard page is an object with a `title` (shown as the page heading and accordion label) and a `widgets` array that controls which sections appear on that page.
242
+
243
+ **Option-first (default):**
244
+ ```json
245
+ [
246
+ { "title": "Option", "widgets": ["option", "age-category"] },
247
+ { "title": "Schedule", "widgets": ["date", "time"] },
248
+ { "title": "Pickup", "widgets": ["pickup"] }
249
+ ]
250
+ ```
251
+
252
+ **Date-first:**
253
+ ```json
254
+ [
255
+ { "title": "Age & capacity", "widgets": ["age-category"] },
256
+ { "title": "Schedule", "widgets": ["date", "time"] },
257
+ { "title": "Option", "widgets": ["option"] },
258
+ { "title": "Pickup", "widgets": ["pickup"] }
259
+ ]
260
+ ```
261
+
262
+ Available widget types: `option`, `age-category`, `date`, `time`, `pickup`.
263
+
264
+ ### Setting wizard pages
265
+
266
+ **Using a preset** (simplest):
267
+ ```html
268
+ <bw-configurator product-id="..." checkout-key="..." wizard-pages="option-first"></bw-configurator>
269
+
270
+ <bw-configurator product-id="..." checkout-key="..." wizard-pages="date-first"></bw-configurator>
271
+ ```
272
+
273
+ **Using a custom JSON array** (full control):
274
+ ```html
275
+ <bw-configurator product-id="..." checkout-key="..."
276
+ wizard-pages='[
277
+ {"title":"Age & capacity","widgets":["age-category"]},
278
+ {"title":"Schedule","widgets":["date","time"]},
279
+ {"title":"Option","widgets":["option"]},
280
+ {"title":"Pickup","widgets":["pickup"]}
281
+ ]'>
282
+ </bw-configurator>
283
+ ```
284
+
285
+ If `wizard-pages` is omitted, the default is `option-first`.
286
+
287
+ In the configurator, pages are shown as a step-by-step wizard with dot indicators. In the checkout edit view, pages are shown as collapsible accordion sections with a summary of the selected values when collapsed.
288
+
289
+ ## Theming
290
+
291
+ The widget reads CSS custom properties. It automatically picks up site variables (`--header-background`, `--default-font-family`, `--radius`) or can be themed directly:
292
+
293
+ ```css
294
+ :root {
295
+ --bw-color-primary: #0066CC;
296
+ --bw-color-primary-dark: #004C99;
297
+ --bw-font-family: 'Inter', sans-serif;
298
+ --bw-radius-md: 4px;
299
+ }
300
+ ```
301
+
302
+ | Variable | Fallback | Default |
303
+ |---|---|---|
304
+ | `--bw-color-primary` | `--header-background` | `#E30613` |
305
+ | `--bw-color-primary-dark` | `--footer-background` | `#C00510` |
306
+ | `--bw-color-primary-light` | -- | `rgba(227,6,19,0.08)` |
307
+ | `--bw-color-text` | -- | `#212121` |
308
+ | `--bw-color-text-secondary` | -- | `#757575` |
309
+ | `--bw-color-border` | -- | `#E0E0E0` |
310
+ | `--bw-color-bg` | -- | `#FFFFFF` |
311
+ | `--bw-color-surface` | -- | `#F5F5F5` |
312
+ | `--bw-color-success` | -- | `#4CAF50` |
313
+ | `--bw-color-error` | `--header-background` | `#E30613` |
314
+ | `--bw-font-family` | `--default-font-family` | `Roboto, system` |
315
+ | `--bw-radius-sm` | -- | `4px` |
316
+ | `--bw-radius-md` | `--radius` | `8px` |
317
+ | `--bw-radius-lg` | -- | `12px` |
318
+ | `--bw-transition` | -- | `0.15s ease` |