@wix/agent-skills 1.8.0 → 1.10.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.
Files changed (66) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +2 -1
  3. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +14 -0
  4. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +11 -6
  5. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +3 -17
  6. package/skills/wix-app/references/editor-react-component/SSR.md +119 -0
  7. package/skills/wix-design-system/SKILL.md +1 -1
  8. package/skills/wix-headless/references/BUILD-astro.md +5 -7
  9. package/skills/wix-headless/references/DESIGN_SYSTEM.md +6 -16
  10. package/skills/wix-headless/references/SETUP.md +1 -1
  11. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +41 -333
  12. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +77 -428
  13. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +2 -2
  14. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +7 -10
  15. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +2 -2
  16. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +3 -3
  17. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +2 -2
  18. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +7 -7
  19. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +4 -4
  20. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +2 -2
  21. package/skills/wix-headless/references/astro/templates/Footer.astro +4 -4
  22. package/skills/wix-headless/references/astro/templates/Navigation.astro +4 -4
  23. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +298 -142
  24. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +104 -162
  25. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +35 -14
  26. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +11 -2
  27. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +23 -85
  28. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +245 -0
  29. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +209 -198
  30. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +114 -19
  31. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +131 -15
  32. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +3 -3
  33. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +3 -3
  34. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +17 -17
  35. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +3 -3
  36. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +4 -4
  37. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +19 -18
  38. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +3 -3
  39. package/skills/wix-headless/references/astro/templates/global.css +17 -15
  40. package/skills/wix-headless/references/astro/templates/index.astro +4 -4
  41. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +31 -31
  42. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +4 -4
  43. package/skills/wix-headless/references/bookings/FLOW.md +246 -0
  44. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +65 -76
  45. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +60 -0
  46. package/skills/wix-headless/references/custom/bookings/WIRING.md +169 -60
  47. package/skills/wix-headless/references/shared/DESIGN_MD.md +2 -3
  48. package/skills/wix-headless/references/shared/IMPLEMENTER.md +4 -4
  49. package/skills/wix-headless/references/shared/STYLING.md +15 -18
  50. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +2 -2
  51. package/skills/wix-headless/references/verticals/bookings.md +9 -9
  52. package/skills/wix-headless/scripts/compose.mjs +4 -19
  53. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -3
  54. package/skills/wix-manage/SKILL.md +12 -0
  55. package/skills/wix-manage/references/app-installation/install-wix-apps.md +3 -27
  56. package/skills/wix-manage/references/bookings/create-appointment-service.md +254 -0
  57. package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +37 -0
  58. package/skills/wix-manage/references/bookings/create-class-service.md +233 -0
  59. package/skills/wix-manage/references/bookings/create-course-service.md +237 -0
  60. package/skills/wix-manage/references/cms/cms-data-items-crud.md +2 -1
  61. package/skills/wix-manage/references/cms/cms-schema-management.md +1 -1
  62. package/skills/wix-manage/references/stores/create-product-from-image.md +154 -2
  63. package/skills/wix-headless/references/astro/templates/bookings/ManageBooking.tsx +0 -84
  64. package/skills/wix-headless/references/astro/templates/bookings/api/confirm-booking.ts +0 -34
  65. package/skills/wix-headless/references/astro/templates/bookings/api/waitlist.ts +0 -74
  66. package/skills/wix-headless/references/astro/templates/bookings/manage-booking.astro +0 -76
@@ -1,88 +1,197 @@
1
1
  ---
2
2
  name: custom-bookings-wiring
3
- description: "Integration-mode wiring subagent for the bookings capability. Wires a services list + availability + a book action into a brought-in site — @wix/bookings reads client-side via @wix/sdk, and a redirect to Wix-hosted bookings checkout for the booking itself (mirrors the ecom checkout-redirect pattern). No server runtime required."
3
+ description: "Integration-mode wiring subagent for the bookings capability. Wires a services list + availability + a schema-driven booking form into a brought-in site, then books via the ecom Cart V2 sequence (createBooking → cart → placeOrder / hosted-checkout redirect). Client-side @wix/sdk; no server runtime, no confirmBooking."
4
4
  ---
5
5
 
6
- # Bookings — integration wiring
6
+ # Bookings — integration wiring (own / static)
7
7
 
8
- You wire the **bookings capability** (services list + availability + book) into a brought-in site (`frontend = "custom"`). Client-side `@wix/sdk` — CDN imports for `none`-build, bundled imports for `own`-build (same calls). Read `INSTRUCTIONS.md` § "The technical spine" + § "Wiring discipline" first.
8
+ You wire the **bookings capability** into a brought-in site (`frontend = "custom"`).
9
+ Client-side `@wix/sdk` — CDN imports for `none`-build, bundled for `own`-build
10
+ (same calls). Read `INSTRUCTIONS.md` § "The technical spine" + § "Wiring discipline".
9
11
 
10
- > **Scope.** Beta does **services display + availability + redirect-to-Wix-hosted bookings checkout**. The hosted checkout owns payment, the seat-holding confirm, and the customer email — exactly the pieces a client-only site cannot do itself: `confirmBooking` requires the Manage Bookings scope (server-side elevation), and a bare client `createBooking` leaves the booking **`CREATED`, which holds no seat** (the class/slot stays bookable → overbooking). Do NOT ship a client-side `createBooking` as a complete flow. An on-site booking flow (calendar + form + elevated confirm, as in the astro vertical) needs the server runtime — **deferred**.
12
+ > **The logic is shared.** The step model, the booking SDK sequence, the
13
+ > schema-driven form, and the gotchas are in `../../bookings/FLOW.md` — read it
14
+ > first. The astro vertical's React examples
15
+ > (`../../astro/templates/bookings/*` incl. `bookingDriver.ts`) are the reference
16
+ > implementation; here you run the **same SDK calls** through an `OAuthStrategy`
17
+ > visitor client (adapt the React idiom to whatever framework the site uses).
11
18
 
12
- ## Inputs (inlined in your prompt)
19
+ > **Scope (same as the astro vertical — the astro/own distinction collapses).**
20
+ > Services display + a week-calendar availability picker + a schema-driven booking
21
+ > form + the **ecom Cart V2 booking sequence**, all client-side under the visitor
22
+ > identity (no server elevation): `createBooking` (→ `CREATED`) → `createCart` →
23
+ > `calculateCart` → `isCheckoutRequired ? ecomCheckout redirect : placeOrder`.
24
+ > **No `confirmBooking`** — the cart holds the seat, so a client-only site
25
+ > completes the whole flow.
13
26
 
27
+ ## Inputs (inlined in your prompt)
14
28
  - **`appId`** — `OAuthStrategy` `clientId`.
15
- - **Seeded services** — read your `bookings` slice from `.wix/seeded.json` (`services[{id, slug, name, type, durationMinutes, price, currency}]`).
29
+ - **Seeded services** — read your `bookings` slice from `.wix/seeded.json`.
16
30
  - The site's CSS token names (style additively from them).
17
31
 
18
- ## Render services (wire an existing region, or inject a section)
19
-
20
- ```html
21
- <script type="module">
22
- import { createClient, OAuthStrategy } from "https://esm.sh/@wix/sdk@1";
23
- import { services } from "https://esm.sh/@wix/bookings@1";
24
-
25
- const wix = createClient({ modules: { services }, auth: OAuthStrategy({ clientId: "REPLACE_WITH_APP_ID" }) });
26
-
27
- // Query BUILDER + .find() — the { query: {...} } object form returns 0 items silently.
28
- const { items } = await wix.services.queryServices().limit(100).find();
29
- const visible = items.filter((s) => !s.hidden);
30
- // Bind into the existing markup: name s.name, tagline s.tagLine, slug s.mainSlug?.name,
31
- // duration s.schedule?.availabilityConstraints?.sessionDurations?.[0] (APPOINTMENT),
32
- // price s.payment?.fixed?.price ({ value, currency } — value is a string; format from
33
- // the returned currency, the site business locale wins over what was seeded).
34
- </script>
32
+ ## The client (acquire once)
33
+ ```js
34
+ import { createClient, OAuthStrategy } from "https://esm.sh/@wix/sdk@1"; // bundled for own-build
35
+ import { services, availabilityTimeSlots, eventTimeSlots, bookings, categoriesV2 } from "https://esm.sh/@wix/bookings@1";
36
+ import { forms } from "https://esm.sh/@wix/forms@1";
37
+ import * as cartV2 from "https://esm.sh/@wix/auto_sdk_ecom_cart-v-2@1";
38
+ import { redirects } from "https://esm.sh/@wix/redirects@1";
39
+
40
+ const wix = createClient({
41
+ modules: { services, availabilityTimeSlots, eventTimeSlots, bookings, categoriesV2, forms, redirects, ...cartV2 },
42
+ auth: OAuthStrategy({ clientId: "REPLACE_WITH_APP_ID" }),
43
+ });
35
44
  ```
45
+ (`categoriesV2` + `services.queryLocations` power the optional catalog filters; drop them if you don't surface filters.)
36
46
 
37
- ## Availability (per selected service)
47
+ ## Render services
48
+ ```js
49
+ // The filter MUST include appId; pass conditionalFields, then .find().
50
+ const BOOKING_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
51
+ const { items } = await wix.services.queryServices({
52
+ query: { filter: { appId: BOOKING_APP_ID }, paging: { limit: 100 } },
53
+ conditionalFields: ["STAFF_MEMBER_DETAILS"],
54
+ }).find();
55
+ const visible = items.filter((s) => !s.hidden);
56
+ // name s.name · tagline s.tagLine · slug s.mainSlug?.name ·
57
+ // duration s.schedule?.availabilityConstraints?.sessionDurations?.[0] ·
58
+ // price s.payment?.fixed?.price ({ value, currency } — value is a string) ·
59
+ // staff s.staffMemberDetails?.staffMembers ([{ staffMemberId, name }] — staffMemberId IS the resource id).
60
+ ```
38
61
 
62
+ ## Optional catalog filters — location & category
63
+ Both auto-skip when there's nothing to choose (see `../../bookings/FLOW.md` §7).
64
+ Merge the chosen filter into the **same** `queryServices` filter and re-query.
39
65
  ```js
40
- import { availabilityTimeSlots, eventTimeSlots } from "https://esm.sh/@wix/bookings@1";
41
- // register the module(s) in createClient({ modules: { ... } })
66
+ // Locations — show the selector only when count > 1.
67
+ const loc = await wix.services.queryLocations();
68
+ const businessLocations = loc.businessLocations?.locations ?? []; // [{ _id, type, business: { name } }]
69
+ const count = businessLocations.length + (loc.customLocations?.exists ? 1 : 0) + (loc.customerLocations?.exists ? 1 : 0);
70
+ // Categories — non-fatal; show the bar only when > 1.
71
+ const cats = (await wix.categoriesV2.queryCategories().find()).items ?? []; // [{ _id, name }]
72
+
73
+ // Merge into the services filter:
74
+ const filter = { appId: BOOKING_APP_ID };
75
+ if (categoryId) filter["category.id"] = { $eq: categoryId };
76
+ if (locationId === "custom") filter["locations.type"] = { $hasSome: ["CUSTOM"] };
77
+ else if (locationId === "customer") filter["locations.type"] = { $hasSome: ["CUSTOMER"] };
78
+ else if (locationId) filter["locations.business.id"] = { $hasSome: [locationId] };
79
+ // → wix.services.queryServices({ query: { filter, paging: { limit: 100 } }, conditionalFields: ["STAFF_MEMBER_DETAILS"] }).find()
80
+ // Carry locationId/locationType into the availability call so slots are scoped to it.
81
+ ```
42
82
 
43
- // APPOINTMENT — serviceId is a single GUID STRING (NOT an array):
83
+ ## Availability (week calendar)
84
+ Render a **week calendar** (day strip → the day's times), not a flat grid — see
85
+ `../../bookings/FLOW.md` § 5. `fromLocalDate`/`toLocalDate` are **local** strings
86
+ (`YYYY-MM-DDThh:mm:ss`, no `Z`) + a `timeZone`.
87
+
88
+ > **Multi-location: scope to ONE business location.** Unscoped,
89
+ > `listAvailabilityTimeSlots` returns **one slot per location** per time → duplicate
90
+ > rows. Source the location list from **`queryLocations()`** (the site's real business
91
+ > locations, whose ids the availability engine recognizes) intersected with the
92
+ > service's own location ids — NOT from `service.locations` alone (its entries can
93
+ > carry an id the engine doesn't recognize → scoping returns 0 slots). Default to the
94
+ > carried/first and pass the single-location filter below; with no business locations,
95
+ > leave it unscoped. (Staff does NOT multiply rows — only location does.) See FLOW.md §7.
96
+ ```js
97
+ // APPOINTMENT — serviceId is a single GUID STRING. Staff (§8) OPTIONAL; location ALWAYS when >1:
44
98
  const a = await wix.availabilityTimeSlots.listAvailabilityTimeSlots({
45
- serviceId, fromLocalDate, toLocalDate, timeZone, bookable: true, cursorPaging: { limit: 50 },
99
+ serviceId, fromLocalDate, toLocalDate, timeZone, bookable: true, cursorPaging: { limit: 100 },
100
+ // staff: resourceTypes: [{ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, resourceIds: [resourceId] }], includeResourceTypeIds: [STAFF_MEMBER_RESOURCE_TYPE_ID],
101
+ // location: locations: [{ _id: locationId, locationType: "BUSINESS" }],
46
102
  });
47
- // slots: a.timeSlots[] — localStartDate/localEndDate/scheduleId at the TOP level.
48
-
49
- // CLASS — different namespace, PLURAL serviceIds; slots carry eventInfo.eventId, no scheduleId:
103
+ // slots a.timeSlots[] — localStartDate/localEndDate/scheduleId at the TOP level.
104
+ // CLASS — different namespace, PLURAL serviceIds; slots carry eventInfo.eventId, no scheduleId.
105
+ // CLASS filters staff + location via eventFilter:
50
106
  const c = await wix.eventTimeSlots.listEventTimeSlots({
51
107
  serviceIds: [serviceId], fromLocalDate, toLocalDate, timeZone, includeNonBookable: false,
108
+ // eventFilter: { "resources.id": { $hasSome: [resourceId] }, "location.id": [locationId] },
52
109
  });
53
110
  ```
54
111
 
55
- `fromLocalDate`/`toLocalDate` are **local** date strings (`YYYY-MM-DDThh:mm:ss`, no `Z`) with an explicit `timeZone`.
56
-
57
- ## Book — redirect to Wix-hosted bookings checkout
58
-
59
- Same pattern as the ecom guide's hosted checkout: create a redirect session for the chosen slot and send the visitor to Wix.
60
-
112
+ ## Optional staff selection
113
+ The staff list is on the service (`s.staffMemberDetails.staffMembers` → `[{ staffMemberId, name }]`;
114
+ `staffMemberId` IS the resource id despite the name). Show a picker only when >1 staff.
115
+ Filtering uses that `staffMemberId` (the `resourceId`/`resourceIds` above).
116
+ When a specific staff is chosen, set `slot.resource = { _id: staffMemberId, name }` on the
117
+ slot you pass to `createBooking` (the booking books that resource); leave it unset for
118
+ "any staff" → the **ANY_RESOURCE fallback** already in the booking snippet below.
119
+
120
+ ## Booking form — schema-driven
121
+ The booking form is a `@wix/forms` form on the service (`service.form._id`). Read
122
+ its schema and render fields by `componentType`, collecting values keyed by
123
+ `target` (`../../bookings/FLOW.md` § 4):
61
124
  ```js
62
- import { redirects } from "https://esm.sh/@wix/redirects@1"; // register in modules
63
-
64
- const { redirectSession } = await wix.redirects.createRedirectSession({
65
- bookingsCheckout: {
66
- slotAvailability: {
67
- slot: {
68
- serviceId: slot.serviceId,
69
- scheduleId: slot.scheduleId, // APPOINTMENT slots
70
- startDate: slot.localStartDate,
71
- endDate: slot.localEndDate,
72
- timezone: timeZone,
73
- },
74
- },
75
- timezone: timeZone,
76
- },
77
- callbacks: { postFlowUrl: window.location.href }, // return to the brought-in site after checkout
78
- });
79
- window.location.href = redirectSession.fullUrl;
125
+ const { form } = await wix.forms.getForm(service.form._id);
126
+ const RENDERABLE = ["TEXT_INPUT", "PHONE_INPUT", "DROPDOWN"];
127
+ const fields = (form.formFields ?? []).filter(
128
+ (f) => f.fieldType === "INPUT" && !f.hidden &&
129
+ RENDERABLE.includes(f.inputOptions?.stringOptions?.componentType),
130
+ );
131
+ // render each by f.inputOptions.stringOptions.componentType; collect values by f.inputOptions.target.
132
+ // SKIP complex object-valued fields (e.g. multi-line ADDRESS, no string componentType) —
133
+ // sending a string for them fails createBooking with "must be object". Only
134
+ // first_name/last_name/email are enforced, so skipping optional complex fields is safe.
80
135
  ```
81
136
 
82
- The hosted flow collects contact details, takes payment when the service is paid, **confirms the booking (holds the seat)**, and sends the customer email — none of which the client site has to implement.
83
-
84
- > **CLASS sessions:** the verified `slotAvailability.slot` shape above is the APPOINTMENT one. For a CLASS service, slots come from `eventTimeSlots` with `eventInfo.eventId` and no `scheduleId` — verify the class slot→redirect mapping against the redirects SDK docs at wiring time before wiring a CLASS service; don't guess the shape.
137
+ ## Book — the ecom Cart V2 sequence
138
+ Mirror `../../astro/templates/bookings/bookingDriver.ts` (the exact payloads).
139
+ Run the same sequence through `wix.*`; the cart holds the seat, so no elevation.
140
+ ```js
141
+ const BOOKING_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
142
+ const STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
143
+
144
+ // Build the slot. APPOINTMENT carries scheduleId; CLASS carries eventId (Wix
145
+ // derives the rest). No staff chosen → the ANY_RESOURCE fallback.
146
+ // When a specific staff was chosen, use `resource: { _id: resourceId, name }`
147
+ // INSTEAD of `resourceSelections` (drop the ANY_RESOURCE fallback for that slot).
148
+ const slot =
149
+ slotType === "CLASS"
150
+ ? { serviceId, eventId, timezone }
151
+ : {
152
+ serviceId, scheduleId, startDate, endDate, timezone,
153
+ resourceSelections: [{ resourceTypeId: STAFF_MEMBER_RESOURCE_TYPE_ID, selectionMethod: "ANY_RESOURCE" }],
154
+ location: { locationType: "OWNER_BUSINESS" },
155
+ };
156
+
157
+ // selectedPaymentOption must match the service: online-only → ONLINE,
158
+ // in-person-only → OFFLINE, else ONLINE. DERIVE it — do not hardcode ONLINE: a
159
+ // free / pay-in-person service booked ONLINE is rejected by the cart with
160
+ // INSUFFICIENT_INVENTORY (it's the only service kind that reaches placeOrder).
161
+ const o = service.payment?.options;
162
+ const selectedPaymentOption =
163
+ o?.online && !o?.inPerson ? "ONLINE" : !o?.online && o?.inPerson ? "OFFLINE" : "ONLINE";
164
+
165
+ // 1. createBooking → CREATED. Arg 1 is the BOOKING object (slot nested under
166
+ // bookedEntity); formSubmission/sendSmsReminder go in the options arg.
167
+ const { booking } = await wix.bookings.createBooking(
168
+ { selectedPaymentOption, totalParticipants: 1, bookedEntity: { slot } },
169
+ { formSubmission, sendSmsReminder: true }, // formSubmission keyed by each field's `target`
170
+ );
171
+ // 2. createCart — one catalog item per booking._id, catalogReference.appId = BOOKING_APP_ID, channel WEB.
172
+ const cart = await wix.createCart({ catalogItems: [{ quantity: 1, catalogReference: { catalogItemId: booking._id, appId: BOOKING_APP_ID } }], cart: { source: { channelType: "WEB" } } });
173
+ // 3. calculateCart → { cart, summary }, then compute the checkout decision.
174
+ const { cart: calc, summary } = await wix.calculateCart(cart._id);
175
+ const checkoutRequired =
176
+ service.bookingPolicy?.cancellationFeePolicy?.enabled
177
+ ? true
178
+ : Number(summary?.priceSummary?.total?.amount ?? 0) === 0
179
+ ? false
180
+ : calc?.lineItems?.[0]?.paymentConfig?.paymentOption !== "FULL_PAYMENT_OFFLINE";
181
+ // 4. paid → redirect (ecomCheckout.checkoutId = cartId) ; free/offline → placeOrder.
182
+ if (checkoutRequired) {
183
+ const { redirectSession } = await wix.redirects.createRedirectSession({ ecomCheckout: { checkoutId: cart._id }, callbacks: { postFlowUrl: window.location.href } });
184
+ window.location.href = redirectSession.fullUrl;
185
+ } else {
186
+ await wix.placeOrder(cart._id); // booked — no redirect
187
+ }
188
+ ```
85
189
 
86
- ## Deferred (needs the server runtime — `@wix/astro`)
190
+ > Confirm the `@wix/auto_sdk_ecom_cart-v-2` function registration shape on a
191
+ > CDN/own client at wiring time; the payloads above match the astro `bookingDriver.ts`.
87
192
 
88
- On-site booking flow (calendar + form + elevated `confirmBooking` seat-hold), manage/cancel via anonymous action tokens, and the native waitlist (v1, Manage Bookings scope) — all implemented in the astro vertical (`references/astro/bookings/`); out of scope for client-only integration.
193
+ ## Out of scope
194
+ Waitlist and on-site manage/cancel — out of scope (same as the astro vertical;
195
+ neither has a headless SoT — display-only policy flags only, no join/cancel logic).
196
+ Show bookable slots only; post-booking self-service is handled by the Wix-hosted
197
+ flow / member area.
@@ -21,8 +21,6 @@ typography:
21
21
  fontWeight: <number> # optional
22
22
  lineHeight: <Dimension|number> # optional
23
23
  letterSpacing: <Dimension> # optional
24
- spacing:
25
- <scale-level>: <Dimension|number>
26
24
  rounded: # corner radii (DESIGN.md's name for radius tokens)
27
25
  <scale-level>: <Dimension>
28
26
  containers: # EXTENSION (this skill): content/reading widths
@@ -51,11 +49,12 @@ googleFontsHref: <string> # EXTENSION (this skill): the ready Google Fonts <li
51
49
  |---|---|---|
52
50
  | `colors` | `--color-<key>` | semantic role keys (see color roles below) |
53
51
  | `typography.<level>.fontFamily` | `--font-<level>` | `display` + `body` required; `mono` optional |
54
- | `spacing` | `--spacing-<key>` | full `2xs…4xl` rhythm scale |
55
52
  | `rounded` | `--radius-<key>` | `sm` + `md` required |
56
53
  | `containers` | `--container-<key>` | **extension** — content widths, a separate axis from spacing (`prose, md, 3xl, 6xl`) |
57
54
  | `googleFontsHref` | the Layout `<link>` href | **extension** — the ready stylesheet URL for the chosen families |
58
55
 
56
+ > **Spacing is not a DESIGN.md token.** The build uses Tailwind v4's built-in numeric spacing scale directly (`gap-4`, `py-24`, `px-6`, …). A named `--spacing-<size>` scale was removed because its t-shirt names (`md`, `3xl`, …) collided with the width utilities (`max-w-3xl` resolved to `--spacing-3xl` instead of `--container-3xl`). Do not author a `spacing` group.
57
+
59
58
  ### Color roles — wix-native keys are the contract
60
59
 
61
60
  The skill's component-CSS templates reference a fixed set of `--color-*` names (`STYLING.md` § "Required tokens"). So the **canonical color token keys in our DESIGN.md are the wix editorial roles** — and these are valid DESIGN.md color token names (the format leaves token names free-form):
@@ -41,7 +41,7 @@ Every input you need is either inlined in your prompt or — for seeded entity I
41
41
  | Scope | Where its inputs come from |
42
42
  |---|---|
43
43
  | `seed` | All **inlined**: `brand`, `intent.<pack>`, `siteId`, recipe path(s). Do NOT re-derive these. |
44
- | `components` | **Inlined**: `brand`. **Read from disk**: the design tokens (the DESIGN.md vocabulary — `colors`/`typography`/`spacing`/`rounded`/`containers`) from `.wix/design-tokens.css` (gate-verified present; also the CSS variables the build consumes). Components do not need seeded IDs. |
44
+ | `components` | **Inlined**: `brand`. **Read from disk**: the design tokens (the DESIGN.md vocabulary — `colors`/`typography`/`rounded`/`containers`; spacing is Tailwind's numeric scale, not a token) from `.wix/design-tokens.css` (gate-verified present; also the CSS variables the build consumes). Components do not need seeded IDs. |
45
45
  | `pages` / `pages-*` | **Inlined:** `brand`. **Read from disk:** the design tokens (`.wix/design-tokens.css`, DESIGN.md vocabulary); your `seeded.<vertical>` slice from `.wix/seeded.json` (products, posts, collections IDs). Page data wiring uses live SDK queries; the `seeded` data is for path resolution + demo content authoring. |
46
46
 
47
47
  If a required **inlined** input is missing from your prompt, or your **`.wix/seeded.json` slice** is absent (e.g. you are dispatched as `pages-products` but `.wix/seeded.json` has no `seeded.stores`), fail fast — return `status: "failed"` with `errors: [{ code: "SEEDED_JSON_SLICE_MISSING", missing: "seeded.stores.products" }]` (or `PROMPT_INCOMPLETE` for an inlined gap). Do NOT re-fetch via curl — the gap means an upstream phase didn't complete, and re-querying would mask the real bug.
@@ -120,11 +120,11 @@ The full styling contract (tokens-as-utilities default, when global semantic cla
120
120
 
121
121
  - **camelCase for identifiers, kebab-case for filenames, PascalCase for components.** Example: `ProductCard.astro`, `queryBlogPosts` function, `cart-updated` event.
122
122
  - **No inline styles beyond design-token CSS variables.** `style={{ color: "red" }}` is forbidden; `style={{ color: "var(--color-accent)" }}` is fine.
123
- - **Tailwind v4 `@reference` is mandatory in any scoped CSS that uses `@apply`.** Tailwind v4 isolates `@apply` per file — utilities defined in the main entry CSS (where `@theme` lives) are NOT visible to `components-*.css` unless that file prepends `@reference "./global.css";` on line 1. If your scope writes a `components-<vertical>.css` that uses `@apply` with theme tokens (e.g., `@apply gap-sm font-display text-sm`), the file MUST start with:
123
+ - **Tailwind v4 `@reference` is mandatory in any scoped CSS that uses `@apply`.** Tailwind v4 isolates `@apply` per file — utilities defined in the main entry CSS (where `@theme` lives) are NOT visible to `components-*.css` unless that file prepends `@reference "./global.css";` on line 1. If your scope writes a `components-<vertical>.css` that uses `@apply` with theme tokens (e.g., `@apply gap-3 font-display text-sm`), the file MUST start with:
124
124
  ```css
125
125
  @reference "./global.css";
126
126
  ```
127
- Without it, the build breaks at release time with `Cannot apply unknown utility class 'gap-sm' …` even though `tsc` and `astro check` pass clean — only the bundler catches it.
127
+ Without it, the build breaks at release time with `Cannot apply unknown utility class 'gap-3' …` even though `tsc` and `astro check` pass clean — only the bundler catches it.
128
128
  - **Fail loud, never silently.** If data is missing, a required field is absent, or an REST call returns an unexpected shape, return `status: "failed"` with details. Do not invent placeholders or swallow errors.
129
129
 
130
130
  ## Return contract
@@ -188,7 +188,7 @@ The `data` shapes for the scopes this file owns — `components` and `pages`/`pa
188
188
  | Issuing REST calls from a `components` or `pages` scope | Components/Pages are frontend-only; read `seeded` data from your `.wix/seeded.json` slice (pages only) — never curl |
189
189
  | Re-querying when your `seeded.<vertical>` slice is missing from `.wix/seeded.json` | Fail fast with `SEEDED_JSON_SLICE_MISSING` — an upstream phase didn't complete (do NOT re-query) |
190
190
  | Depending on mutable shared state the orchestrator holds in scratch | Every input is inlined; the one shared file you may read is your `.wix/seeded.json` slice (read-only) |
191
- | Inventing class names for layout/spacing/typography (`.productCard`, `.heroSection`) | Tailwind utilities derived from `@theme` tokens (`class="flex flex-col gap-md"`, `class="py-4xl"`). For one-off page decoration, co-located `<style>` block. |
191
+ | Inventing class names for layout/spacing/typography (`.productCard`, `.heroSection`) | Tailwind utilities derived from `@theme` tokens (`class="flex flex-col gap-4"`, `class="py-24"`). For one-off page decoration, co-located `<style>` block. |
192
192
  | Removing a marker after inserting at it | Marker stays; other verticals may contribute after you |
193
193
  | Trailing narrative prose after the return JSON | JSON block must be the last content |
194
194
  | Fabricated timestamps in the return JSON | Do not include timing fields — orchestrator captures them |
@@ -8,15 +8,15 @@ Every visual decision in a generated site falls into one of three categories. Ea
8
8
 
9
9
  | Category | Lives in | Owned by | Use for |
10
10
  |---|---|---|---|
11
- | **Tokens (composed as utilities)** | the canonical `DESIGN.md` + its projections: the `@theme` block in `src/styles/global.css` (written by `scripts/compose.mjs`), plus `.wix/design-tokens.css` and `.wix/site.d.ts` (written by `scripts/emit-design-tokens.mjs`) — all from Designer's `DESIGN.md` | Designer picks values (Phase 2); `compose.mjs` writes `@theme` | All color, spacing, typography scale, radii, aspect ratios, shadows, transitions. Pages compose tokens at call sites as Tailwind utilities — `class="py-4xl bg-sand aspect-[16/5]"`. |
11
+ | **Tokens (composed as utilities)** | the canonical `DESIGN.md` + its projections: the `@theme` block in `src/styles/global.css` (written by `scripts/compose.mjs`), plus `.wix/design-tokens.css` and `.wix/site.d.ts` (written by `scripts/emit-design-tokens.mjs`) — all from Designer's `DESIGN.md` | Designer picks values (Phase 2); `compose.mjs` writes `@theme` | All color, typography scale, radii, aspect ratios, shadows, transitions (spacing is Tailwind's built-in numeric scale). Pages compose tokens at call sites as Tailwind utilities — `class="py-24 bg-sand aspect-[16/5]"`. |
12
12
  | **Global semantic classes** | `src/styles/global.css` (outside `@theme`) and `src/styles/components-<pack>.css` | Designer + Phase 3 component agents | Compound multi-element patterns, interactive states (`:hover`, `:focus`, `:disabled`), and JS/React DOM query targets. |
13
13
  | **Co-located styles** | `<style>` block at the bottom of the same `.astro` file (or component CSS module for islands) | Page or component author | One-off page decoration: hero stamps, custom dividers, ornamental overlays that won't be reused elsewhere. |
14
14
 
15
15
  ## Default direction
16
16
 
17
- **Tokens-as-utilities is the default.** When you're about to write a class for layout, spacing, typography, alignment, simple background/text color, or aspect ratio, write Tailwind utilities derived from `@theme` instead. The site's design tokens give you `py-4xl`, `gap-sm`, `bg-paper-warm`, `text-ink`, `font-display`, `aspect-[16/5]` etc. Compose them in markup; do not invent semantic classes for these concerns.
17
+ **Tokens-as-utilities is the default.** When you're about to write a class for layout, spacing, typography, alignment, simple background/text color, or aspect ratio, write Tailwind utilities derived from `@theme` instead. The site's design tokens give you `bg-paper-warm`, `text-ink`, `font-display`, `aspect-[16/5]` etc.; spacing is Tailwind-numeric (`py-24`, `gap-3`). Compose them in markup; do not invent semantic classes for these concerns.
18
18
 
19
- The token namespace is the contract. The design tokens (the DESIGN.md token vocabulary — `colors`/`typography`/`spacing`/`rounded`/`containers`) live on disk at `.wix/design-tokens.css` (gate-verified present before the build wave) — **read that file at the start of any pages or components scope** to know which tokens this run published. The orchestrator does **not** inline them in your prompt. (`.wix/site.d.ts` is also on disk for the build.) If a token you need isn't in `.wix/design-tokens.css`, that's a designer-side gap — flag it in your return JSON, don't paper over it with a custom class.
19
+ The token namespace is the contract. The design tokens (the DESIGN.md token vocabulary — `colors`/`typography`/`rounded`/`containers`) live on disk at `.wix/design-tokens.css` (gate-verified present before the build wave) — **read that file at the start of any pages or components scope** to know which tokens this run published. The orchestrator does **not** inline them in your prompt. (`.wix/site.d.ts` is also on disk for the build.) If a token you need isn't in `.wix/design-tokens.css`, that's a designer-side gap — flag it in your return JSON, don't paper over it with a custom class.
20
20
 
21
21
  ## Decision tree
22
22
 
@@ -32,24 +32,22 @@ If none fit, you're probably trying to do something the tokens don't support yet
32
32
 
33
33
  These are layout/spacing/typography concerns that should always be utilities, never standalone classes:
34
34
 
35
- - Section padding / margins (`py-4xl`, `mt-3xl`, `mb-2xl`)
36
- - Flex / grid layouts (`flex flex-col gap-md`, `grid grid-cols-3 gap-lg`)
35
+ - Section padding / margins (`py-24`, `mt-16`, `mb-12`)
36
+ - Flex / grid layouts (`flex flex-col gap-4`, `grid grid-cols-3 gap-6`)
37
37
  - Aspect ratios (`aspect-[4/5]`, `aspect-square`)
38
38
  - Plain typography choices (`text-display-lg font-display`, `text-mute uppercase tracking-wide`)
39
39
  - Background / foreground color application without state (`bg-paper`, `text-ink-soft`)
40
- - Container widths derived from tokens (`max-w-prose`, `w-full`) **only when** the designer declared matching `--container-*` keys in `@theme`
40
+ - Container widths (`max-w-prose`, `max-w-6xl`, `max-w-3xl`, `w-full`) — these resolve to the `--container-*` keys the designer declared.
41
41
 
42
- ### Prose / reading width (CMS, FAQ, About)
42
+ ### Spacing & reading width
43
43
 
44
- Tailwind v4 resolves `max-w-3xl` to `var(--container-3xl)`, **not** `var(--spacing-3xl)`. If the designer published `--spacing-3xl: 5rem` but no `--container-3xl`, FAQ/About columns collapse to ~80px.
44
+ **Spacing is Tailwind's built-in numeric scale** — `gap-4`, `py-24`, `px-6`, `mt-16`, etc. There is **no** named `--spacing-<size>` scale (it was removed: its t-shirt names `md`/`3xl`/… collided with the width utilities, so `max-w-3xl` resolved to `--spacing-3xl` ≈ 96px). Common steps: `1`=0.25rem, `2`=0.5, `3`=0.75, `4`=1rem, `6`=1.5, `8`=2, `12`=3, `16`=4, `24`=6rem.
45
45
 
46
- **Page agents (Phase 4):**
46
+ > **Never reintroduce a named `--spacing-*` scale.** In Tailwind v4 the `--spacing-*` namespace also generates the width/sizing families (`max-w-*`, `w-*`, `min-w-*`, …), so any `--spacing-<key>` token shadows the matching `max-w-<key>`. The current fix works because the skill's spacing steps happen to equal Tailwind's numeric defaults — but if a brand ever needs genuinely *custom* spacing **values**, put them under a different namespace (e.g. `--rhythm-*`) or use arbitrary values (`py-[5.5rem]`); do not revive `--spacing-*`.
47
47
 
48
- - **Do not** use `max-w-2xl`, `max-w-3xl`, etc. unless `global.css` `@theme` documents the matching `--container-*` key (grep `@theme` before choosing).
49
- - **Prefer** `container-reading` (designer `@utility`), `max-w-6xl` when `--container-6xl` exists, or explicit arbitrary width `max-w-[48rem]`.
50
- - **Do not** confuse spacing scale with container scale — `py-3xl` uses `--spacing-3xl`; `max-w-3xl` uses `--container-3xl`.
48
+ **Width / reading columns:** `max-w-md`, `max-w-3xl`, `max-w-6xl`, `max-w-prose` now resolve correctly to the `--container-*` keys (no spacing collision), so use them freely; `container-reading` (the designer `@utility`) or an arbitrary `max-w-[48rem]` also work.
51
49
 
52
- If a designer's `global.css` contains rules like `.featured-section { padding-block: var(--spacing-4xl); }` or `.product-card-body { display: flex; flex-direction: column; gap: var(--spacing-xs); }`, those are misplaced — they belong in markup as utilities. Inventing such classes ships broken layouts: every consumer needs the designer to have pre-declared the class, and Tailwind v4 silently drops the references when the rule is missing.
50
+ If a designer's `global.css` contains rules like `.featured-section { padding-block: 6rem; }` or `.product-card-body { display: flex; flex-direction: column; gap: 0.5rem; }`, those are misplaced — they belong in markup as utilities (`py-24`, `flex flex-col gap-2`). Inventing such classes ships broken layouts: every consumer needs the designer to have pre-declared the class, and Tailwind v4 silently drops the references when the rule is missing.
53
51
 
54
52
  ## Required tokens — the component-CSS template contract
55
53
 
@@ -68,11 +66,10 @@ The per-pack `components-<pack>.css` templates at `<SKILL_ROOT>/references/astro
68
66
  | `--color-error` | optional | `var(--color-accent)` or hardcode | ecom cart-item-unavailable, stores back-in-stock-error |
69
67
  | `--font-display` | **required** | — | every pack — headings + labels |
70
68
  | `--font-body` | **required** | — | every pack — body text + UI |
71
- | `--spacing-2xs` … `--spacing-4xl` | **required (full scale)** | — | every template uses `gap: var(--spacing-md)` and similar |
72
69
  | `--radius-sm`, `--radius-md` | **required** | — | buttons, inputs, cards |
73
70
  | `--radius-lg`, `--radius-xl` | optional | — | stores product-card |
74
71
 
75
- The templates do NOT use `@apply` — every rule is `property: var(--token);` directly. This means missing utilities don't fail at build time (the way `@apply gap-sm` would). Missing tokens degrade silently to `var(missing) → unset → initial`. That makes the failure mode "ugly component" not "broken build" — easier to recover from but harder to detect, so verify the full set above is in `@theme` before returning.
72
+ The templates do NOT use `@apply` — every rule is `property: var(--token);` directly. This means missing utilities don't fail at build time (the way `@apply gap-3` would). Missing tokens degrade silently to `var(missing) → unset → initial`. That makes the failure mode "ugly component" not "broken build" — easier to recover from but harder to detect, so verify the full set above is in `@theme` before returning.
76
73
 
77
74
  **Why this contract exists.** The component templates are authored against a stable, brand-agnostic token vocabulary so they don't need to be regenerated per run. Using `@apply` against brand-specific tokens (e.g. `bark`, `cream`, `parchment`) that designers don't publish fails the build with `Cannot apply unknown utility class`. Direct `var()` against the required-token list above avoids that class of failure entirely.
78
75
 
@@ -119,15 +116,15 @@ For any listed class the Composer's `global.css` declares: if a template referen
119
116
  Astro's `<style>` blocks scope automatically to the `.astro` file by default — no class-name collision risk across routes. When a page needs decoration that doesn't fit utilities and isn't reusable, the rule lives next to its only caller:
120
117
 
121
118
  ```astro
122
- <section class="relative py-4xl">
119
+ <section class="relative py-24">
123
120
  <div class="hero-stamp">Made in small batches</div>
124
121
  </section>
125
122
 
126
123
  <style>
127
124
  .hero-stamp {
128
125
  position: absolute;
129
- bottom: var(--spacing-lg);
130
- right: var(--spacing-lg);
126
+ bottom: 1.5rem;
127
+ right: 1.5rem;
131
128
  background: rgba(27, 26, 23, 0.75);
132
129
  color: var(--color-paper);
133
130
  padding: 0.5rem 0.875rem;
@@ -95,8 +95,8 @@ If you find one of these missing at runtime, that's an orchestrator-side bug —
95
95
 
96
96
  Stores-specific component CSS lives in `src/styles/components-stores.css` (pre-copied from the template by the orchestrator before Step 4.5 — see the `components` scope-routing note above and `../astro/stores/COMPONENTS_CSS.md`), NOT in the designer's `global.css`. The classes the pack owns:
97
97
 
98
- - `.product-card`, `.product-card-media`, `.product-card-ribbon`, `.product-card-index` — the product card itself, including the `overflow: hidden` + `border-radius` clipping context. Whoever writes the `border-radius` here also writes any inner padding required to keep child content inside the rounded edges. Without inner padding, a price `<p>` sits flush against the rounded bottom corner and descenders get clipped. Add `padding-bottom: var(--spacing-md)` on the card plus horizontal padding on each text block when you set the radius. See `references/shared/STYLING.md` § "Component-specific CSS is owned by the component, not the designer".
99
- - `.product-grid` — the layout that lists product cards. Both `/products` (pages-products scope) and `/category/[slug]` (pages-categories scope) consume it from one place. Include `display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--spacing-xl);` plus the View-Transitions opacity fade for in-flight navigations.
98
+ - `.product-card`, `.product-card-media`, `.product-card-ribbon`, `.product-card-index` — the product card itself, including the `overflow: hidden` + `border-radius` clipping context. Whoever writes the `border-radius` here also writes any inner padding required to keep child content inside the rounded edges. Without inner padding, a price `<p>` sits flush against the rounded bottom corner and descenders get clipped. Add `padding-bottom: 1rem` on the card plus horizontal padding on each text block when you set the radius. See `references/shared/STYLING.md` § "Component-specific CSS is owned by the component, not the designer".
99
+ - `.product-grid` — the layout that lists product cards. Both `/products` (pages-products scope) and `/category/[slug]` (pages-categories scope) consume it from one place. Include `display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 2rem;` plus the View-Transitions opacity fade for in-flight navigations.
100
100
  - `.offer-callout` family (`.offer-callout`, `-item`, `-badge`, `-name`, `-detail`, `-foot`) — discount panel rendered above `<ProductPurchase>` on `/products/[slug]`.
101
101
 
102
102
  The designer's `global.css` declares only tokens, the `.btn` family, decorative slots, the editorial-rule, and the site-shell shells. If `global.css` ships with a partial rule for any class above, that's a designer bug — flag it in your return JSON's `errors` array (`{code: "GLOBAL_CSS_LEAK", class: "<name>"}`) and override with the complete rule in `components-stores.css`.
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: bookings
3
- description: "Service-based booking — services catalog, availability calendar, and booking flow via Wix Bookings."
3
+ description: "Service-based booking — services catalog (with location + category filters and staff selection), availability calendar, and booking flow for appointments and classes via Wix Bookings."
4
4
  triggers: ["booking", "appointment", "schedule", "reserve", "class", "session", "consult", "therapy", "lesson", "coaching", "trainer", "tutor", "salon", "spa", "clinic"]
5
5
  requires: []
6
6
 
7
7
  features:
8
8
  - name: "Services catalog"
9
- description: "Browse bookable services (appointments or group classes) with descriptions, duration, pricing, and instructor info."
9
+ description: "Browse bookable appointment and class services with descriptions, duration, and pricing — filterable by category and (for multi-location businesses) by location."
10
+ - name: "Staff selection"
11
+ description: "On multi-staff services, choose a specific staff member (or any) and see that staff's availability."
10
12
  - name: "Online booking"
11
- description: "Pick an available time slot, complete a booking form, and receive confirmation — all without leaving your site."
12
- - name: "Group classes"
13
- description: "Capacity-aware class sign-up with party size, the leading instructor per session, and a waitlist when a session is full."
14
- - name: "Manage booking"
15
- description: "View and cancel a booking from the confirmation page or a shared manage link, no account login required."
13
+ description: "Pick an available time slot on a day-grouped availability calendar, complete the service's booking form, and confirm."
14
+ - name: "Secure checkout"
15
+ description: "Free or pay-in-person bookings confirm instantly; paid services hand off to Wix's secure hosted checkout."
16
16
 
17
17
  apps:
18
18
  - name: "Wix Bookings"
@@ -24,8 +24,8 @@ routes:
24
24
  name: "Service Detail"
25
25
  - route: "/booking-confirmation"
26
26
  name: "Booking Confirmation"
27
- - route: "/manage-booking"
28
- name: "Manage Booking"
27
+ - route: "Hosted by Wix"
28
+ name: "Secure Checkout"
29
29
 
30
30
  disabled: false
31
31
  ---
@@ -192,7 +192,6 @@ function designMdToTokens(fm) {
192
192
  return {
193
193
  colors,
194
194
  fonts,
195
- spacing: fm.spacing ?? {},
196
195
  radii: fm.rounded ?? {}, // DESIGN.md calls corner radii `rounded`
197
196
  containers: fm.containers ?? {}, // custom group
198
197
  ...(typeof fm.googleFontsHref === "string" ? { googleFontsHref: fm.googleFontsHref } : {}),
@@ -241,7 +240,6 @@ const SYSTEM_FONTS = new Set([
241
240
 
242
241
  const colors = { ...(designTokens.colors ?? {}) };
243
242
  const fonts = { ...(designTokens.fonts ?? {}) };
244
- const spacing = { ...(designTokens.spacing ?? {}) };
245
243
  const radii = { ...(designTokens.radii ?? {}) };
246
244
  const containers = { ...(designTokens.containers ?? {}) };
247
245
 
@@ -272,12 +270,10 @@ ensure(colors, "error", "#c0392b", "--color-");
272
270
  ensure(fonts, "display", "Georgia, serif", "--font-");
273
271
  ensure(fonts, "body", "system-ui, sans-serif", "--font-");
274
272
 
275
- // Spacing — full 2xs..4xl scale.
276
- const SPACING_DEFAULTS = {
277
- "2xs": "0.25rem", xs: "0.5rem", sm: "0.75rem", md: "1rem", lg: "1.5rem",
278
- xl: "2rem", "2xl": "3rem", "3xl": "4rem", "4xl": "6rem",
279
- };
280
- for (const [k, v] of Object.entries(SPACING_DEFAULTS)) ensure(spacing, k, v, "--spacing-");
273
+ // Spacing is NOT a named scale — Tailwind v4's built-in numeric spacing
274
+ // (`--spacing` base → gap-4, py-24, …) is used directly. Emitting named
275
+ // --spacing-<t-shirt> tokens collided with the width utilities (max-w-3xl etc.
276
+ // drew from --spacing-3xl), so the named scale was removed (CODEAI-696).
281
277
 
282
278
  // Radii — sm + md required.
283
279
  ensure(radii, "sm", "0.25rem", "--radius-");
@@ -296,8 +292,6 @@ emitGroup("--color-", colors);
296
292
  themeLines.push("");
297
293
  emitGroup("--font-", fonts);
298
294
  themeLines.push("");
299
- emitGroup("--spacing-", spacing);
300
- themeLines.push("");
301
295
  emitGroup("--radius-", radii);
302
296
  themeLines.push("");
303
297
  emitGroup("--container-", containers);
@@ -310,14 +304,12 @@ const errors = [];
310
304
  const REQUIRED = [
311
305
  ...["paper", "paper-warm", "ink", "mute", "rule", "accent"].map((k) => `--color-${k}`),
312
306
  "--font-display", "--font-body",
313
- ...["2xs", "xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"].map((k) => `--spacing-${k}`),
314
307
  "--radius-sm", "--radius-md",
315
308
  ...["prose", "md", "3xl", "6xl"].map((k) => `--container-${k}`),
316
309
  ];
317
310
  const present = new Set([
318
311
  ...Object.keys(colors).map((k) => `--color-${k}`),
319
312
  ...Object.keys(fonts).map((k) => `--font-${k}`),
320
- ...Object.keys(spacing).map((k) => `--spacing-${k}`),
321
313
  ...Object.keys(radii).map((k) => `--radius-${k}`),
322
314
  ...Object.keys(containers).map((k) => `--container-${k}`),
323
315
  ]);
@@ -325,12 +317,6 @@ for (const tok of REQUIRED) {
325
317
  if (!present.has(tok)) errors.push({ code: "MISSING_REQUIRED_TOKEN", token: tok });
326
318
  }
327
319
 
328
- // Container ≠ spacing: a --container-* value must never equal a --spacing-* value.
329
- const spacingValues = new Set(Object.values(spacing));
330
- for (const [k, v] of Object.entries(containers)) {
331
- if (spacingValues.has(v)) errors.push({ code: "CONTAINER_EQUALS_SPACING", token: `--container-${k}`, value: v });
332
- }
333
-
334
320
  // ── skeleton load + substitution helpers ──────────────────────────────────────
335
321
  function readTemplate(name) {
336
322
  const p = join(TEMPLATES, name);
@@ -526,7 +512,6 @@ const manifest = {
526
512
  homeMarkers: homeMarkerPacks.map((p) => `home:${p}`),
527
513
  tokensApplied: {
528
514
  colors: Object.keys(colors).length,
529
- spacing: Object.keys(spacing).length,
530
515
  containers: Object.keys(containers).length,
531
516
  radii: Object.keys(radii).length,
532
517
  fonts: Object.keys(fonts).length,
@@ -115,7 +115,6 @@ if (!design || typeof design !== "object" || Object.keys(design).length === 0) {
115
115
 
116
116
  const colors = design.colors ?? {};
117
117
  const typography = design.typography ?? {};
118
- const spacing = design.spacing ?? {};
119
118
  const rounded = design.rounded ?? {};
120
119
  const containers = design.containers ?? {};
121
120
  const fontFamilyOf = (lvl) => (typography[lvl] && typeof typography[lvl] === "object" ? typography[lvl].fontFamily : typography[lvl]);
@@ -136,7 +135,6 @@ for (const lvl of Object.keys(typography)) {
136
135
  if (fam) cssLines.push(` --font-${lvl}: ${fam};`);
137
136
  }
138
137
  for (const [k, v] of Object.entries(rounded)) cssLines.push(` --radius-${k}: ${v};`);
139
- for (const [k, v] of Object.entries(spacing)) cssLines.push(` --spacing-${k}: ${v};`);
140
138
  for (const [k, v] of Object.entries(containers)) cssLines.push(` --container-${k}: ${v};`);
141
139
  cssLines.push("}", "");
142
140
  writeFileSync(join(wixDir, "design-tokens.css"), cssLines.join("\n"));
@@ -151,7 +149,6 @@ export type DesignTokens = {
151
149
  colors: ${recordType(Object.keys(colors))};
152
150
  fonts: ${recordType(Object.keys(typography))};
153
151
  radii: ${recordType(Object.keys(rounded))};
154
- spacing: ${recordType(Object.keys(spacing))};
155
152
  containers: ${recordType(Object.keys(containers))};
156
153
  };
157
154
  `;
@@ -52,6 +52,18 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
52
52
  ### [Create and Update Booking Services](references/bookings/create-and-update-booking-services.md)
53
53
  **Technical:** Full CRUD operations for Wix Bookings services using Services API. Covers service types (APPOINTMENT, CLASS, COURSE), pricing configuration, location setup, and schedule management.
54
54
 
55
+ ### [Create Booking Service from Prompt](references/bookings/create-booking-service-from-prompt.md)
56
+ **Technical:** Use when the user wants to create a booking service — e.g. "create a yoga class for $50", "set up consultations", "add a personal training appointment". Routes to the correct type-specific recipe (APPOINTMENT, CLASS, or COURSE), gathers business context, applies defaults, and creates the service.
57
+
58
+ ### [Create Appointment Service](references/bookings/create-appointment-service.md)
59
+ **Technical:** Use when the user wants to create an appointment/consultation/1-on-1 service — e.g. "set up consultations for $75", "create a meeting service". Handles staff assignment, session duration, and pricing via bulkCreateServices API.
60
+
61
+ ### [Create Class Service](references/bookings/create-class-service.md)
62
+ **Technical:** Use when the user wants to create a group class — e.g. "create a yoga class for $50", "set up a pilates class". Handles group capacity, recurring sessions, and pricing via bulkCreateServices API.
63
+
64
+ ### [Create Course Service](references/bookings/create-course-service.md)
65
+ **Technical:** Use when the user wants to create a multi-session course — e.g. "create a 6-week workshop", "set up a training program for $300". Handles group capacity, full-course pricing, and fixed series via bulkCreateServices API.
66
+
55
67
  ### [End-to-End Booking Flow](references/bookings/end-to-end-booking-flow.md)
56
68
  **Technical:** Complete booking flow from service discovery to payment. Query services, check availability with Time Slots V2, create bookings, and process payment via eCommerce checkout.
57
69