@wix/agent-skills 1.16.0 → 1.16.2

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 (94) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +72 -8
  3. package/skills/wix-app/references/CODE_QUALITY.md +0 -1
  4. package/skills/wix-app/references/DASHBOARD_MODAL.md +21 -14
  5. package/skills/wix-app/references/DASHBOARD_PAGE.md +30 -184
  6. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +19 -3
  7. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +172 -0
  8. package/skills/wix-app/references/dashboard-page/WDS_LAYOUT.md +187 -0
  9. package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +12 -7
  10. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +31 -2
  11. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +6 -1
  12. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +60 -0
  13. package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +6 -6
  14. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +167 -0
  15. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +2 -2
  16. package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +187 -0
  17. package/skills/wix-app/scripts/scan-a11y-code.cjs +777 -0
  18. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +201 -0
  19. package/skills/wix-base44-connector/SKILL.md +185 -0
  20. package/skills/wix-base44-connector/scripts/utils.js +212 -0
  21. package/skills/wix-design-system/SKILL.md +19 -15
  22. package/skills/wix-docs/SKILL.md +75 -1
  23. package/skills/wix-headless/references/CAPABILITIES.md +16 -3
  24. package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
  25. package/skills/wix-headless/references/SEED.md +1 -1
  26. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +560 -102
  27. package/skills/wix-headless/references/inline-recipes/setup-forms.md +210 -143
  28. package/skills/wix-manage/SKILL.md +20 -17
  29. package/skills/wix-manage/references/accessibility/scan-site-accessibility.md +98 -0
  30. package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +1 -2
  31. package/skills/wix-manage/references/domains/domain-search-purchase-and-connect.md +2603 -0
  32. package/skills/wix-manage/references/events/create-wix-event.md +112 -90
  33. package/skills/wix-manage/references/events/manage-wix-events.md +33 -94
  34. package/skills/wix-manage/references/sites/create-headless-site.md +1 -0
  35. package/skills/wix-manage/references/sites/manage-oauth-apps.md +154 -0
  36. package/skills/wix-manage/references/sites/read-site-context.md +125 -0
  37. package/skills/wix-manage/references/sites/site-import.md +67 -15
  38. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +273 -0
  39. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +1 -1
  40. package/skills/wix-vibe-headless/SKILL.md +2 -1
  41. package/skills/wix-vibe-headless/install/deploy.cjs +63 -3
  42. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +104 -0
  43. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +74 -0
  44. package/skills/wix-vibe-headless/platforms/base44.md +15 -40
  45. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +7 -11
  46. package/skills/wix-vibe-headless/references/blog/seed/SEED.md +1 -1
  47. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +7 -11
  48. package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +1 -1
  49. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +1 -2
  50. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +7 -12
  51. package/skills/wix-vibe-headless/references/events/seed/SEED.md +1 -1
  52. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +7 -11
  53. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +7 -11
  54. package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +1 -1
  55. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +7 -11
  56. package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +1 -1
  57. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +7 -11
  58. package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +1 -1
  59. package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +1 -1
  60. package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +5 -3
  61. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +75 -13
  62. package/skills/wix-vibe-headless/references/storefront/app/components/ProductCard.jsx +29 -86
  63. package/skills/wix-vibe-headless/references/storefront/app/components/ProductGrid.jsx +3 -8
  64. package/skills/wix-vibe-headless/references/storefront/app/components/VariantPicker.jsx +78 -105
  65. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +92 -0
  66. package/skills/wix-vibe-headless/references/storefront/app/hooks/useVariantOptions.js +76 -0
  67. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +1 -1
  68. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +44 -5
  69. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +0 -326
  70. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +0 -115
  71. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +0 -264
  72. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +0 -37
  73. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +0 -86
  74. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +0 -119
  75. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +0 -103
  76. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +0 -76
  77. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +0 -82
  78. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +0 -89
  79. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +0 -124
  80. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +0 -91
  81. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +0 -71
  82. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +0 -70
  83. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +0 -73
  84. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +0 -98
  85. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +0 -278
  86. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +0 -94
  87. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +0 -43
  88. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +0 -66
  89. package/skills/wix-app/references/auto-patterns-dashboard/views.md +0 -76
  90. package/skills/wix-app/scripts/generate-auto-patterns.js +0 -510
  91. package/skills/wix-manage/references/domains/domain-search-and-purchase.md +0 -437
  92. package/skills/wix-manage/references/stores/create-product-from-image.md +0 -1031
  93. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +0 -623
  94. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +0 -106
@@ -33,11 +33,11 @@ so you don't need to open them:**
33
33
  | `components/CollectionCard.jsx`, `CollectionGrid.jsx` | collections listing UI (grid + card, with empty state) |
34
34
  | `components/ProjectCard.jsx`, `ProjectGrid.jsx` | projects listing UI (grid + card, with empty state) |
35
35
  | `components/ProjectMedia.jsx` | one gallery item — branches on `item.type` (IMAGE / VIDEO) |
36
- | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 4) |
36
+ | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
37
37
  | `pages/Portfolio.jsx` | collections gallery route (`/portfolio`) |
38
38
  | `pages/CollectionPage.jsx` | collection page route (`/collection/:slug`) |
39
39
  | `pages/ProjectDetail.jsx` | project detail route (`/project/:slug`) |
40
- | `rest/wix-config.js` | **you set the ids here** (STEP 2) |
40
+ | `rest/wix-config.js` | the two ids, written by the install step |
41
41
  | `rest/wix-client.js` + `rest/wix-portfolio.js` | REST transport + portfolio read helpers |
42
42
 
43
43
  They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
@@ -47,11 +47,8 @@ a real fallback — a runtime error, or a field the snippets don't cover (see "F
47
47
  end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
48
48
  `references/portfolio/app/` → `src/`.)
49
49
 
50
- ## STEP 2 — Credentials
51
- Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
52
- one place both ids live.
53
50
 
54
- ## STEP 3 — Theme (nothing to style on the shipped components)
51
+ ## STEP 2 — Theme (nothing to style on the shipped components)
55
52
  The shipped components carry **no palette of their own** — they render from base44's design tokens in
56
53
  `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`, `--muted`,
57
54
  `--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`, `text-foreground`,
@@ -59,10 +56,10 @@ The shipped components carry **no palette of their own** — they render from ba
59
56
  are **already set to the brand by the design phase**, so the shipped pages are themed with zero work
60
57
  here. To adjust the palette, edit `index.css` (`:root` **and** `.dark`) — the base44 way; **never add
61
58
  a parallel theme file (e.g. a `theme.css`) or restyle the shipped JSX.** Build the Home/Header you add
62
- (STEP 4) from the **same** base44 tokens/classes so it matches automatically. A dark brand is just
59
+ (STEP 3) from the **same** base44 tokens/classes so it matches automatically. A dark brand is just
63
60
  base44's dark palette in `index.css` — no per-component work.
64
61
 
65
- ## STEP 4 — Wire routes (surgical `find_replace` on `src/App.jsx`, never a rewrite)
62
+ ## STEP 3 — Wire routes (surgical `find_replace` on `src/App.jsx`, never a rewrite)
66
63
  **No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
67
64
  `App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
68
65
  replace it. Portfolio is read-only with no cross-page state, so there's **no provider to wrap** (no
@@ -122,7 +119,7 @@ function Layout() {
122
119
 
123
120
  ## What you build (not shipped)
124
121
  The **home / landing page**, the **`Header`** and a **`Footer`** — the two you drop into the `Layout`
125
- (STEP 4) so they wrap every route — plus the overall brand story, styled with the same base44
122
+ (STEP 3) so they wrap every route — plus the overall brand story, styled with the same base44
126
123
  tokens/classes. **Compose the shipped pieces** — a featured strip is just `queryCollections` (or
127
124
  `queryProjects`) + the shipped `CollectionGrid` / `ProjectGrid`; the nav is a link to `/portfolio`:
128
125
 
@@ -204,9 +201,8 @@ links its own reference page inline; the whole area is here:
204
201
  - Portfolio (collections, projects, project items): https://dev.wix.com/docs/api-reference/business-solutions/portfolio.md
205
202
 
206
203
  ## Hard rules
207
- - Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
208
204
  - Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
209
- - Header/footer live in a `Layout` around `<Outlet/>` (STEP 4) — never edit the shipped
205
+ - Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped
210
206
  `Portfolio`/`CollectionPage`/`ProjectDetail` to add chrome.
211
207
  - The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your
212
208
  `Header` is plain in-flow markup (not `position:fixed`).
@@ -16,7 +16,7 @@ first, then thread their real ids into each project.
16
16
 
17
17
  ```js
18
18
  // build-time exec_tool
19
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
19
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
20
20
  const fs = require("fs");
21
21
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
22
22
  const seed = (() => { const m = { exports: {} };
@@ -36,11 +36,11 @@ map, so you don't need to open them:**
36
36
  | `components/PlanCard.jsx` | plan card — name, description, price, perks, Subscribe (when `buyable`) |
37
37
  | `components/PlanGrid.jsx` | responsive plans grid + empty state |
38
38
  | `components/OrderCard.jsx` | one member order row for the "my plans" list |
39
- | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 4) |
39
+ | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
40
40
  | `pages/Plans.jsx` | the plans-table route (`/plans`) — grid + paging + checkout |
41
41
  | `pages/PlanDetail.jsx` | the plan-detail route (`/plans/:slug`) — perks, terms, checkout |
42
42
  | `pages/MyPlans.jsx` | the member's memberships + post-checkout confirmation (`/my-plans`) |
43
- | `rest/wix-config.js` | **you set the ids here** (STEP 2) |
43
+ | `rest/wix-config.js` | the two ids, written by the install step |
44
44
  | `rest/wix-client.js` + `rest/wix-pricing-plans.js` | REST transport + plans/orders/checkout helpers |
45
45
 
46
46
  They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
@@ -50,11 +50,8 @@ a real fallback — a runtime error, or a field the snippets don't cover (see "F
50
50
  end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
51
51
  `references/pricing-plans/app/` → `src/`.)
52
52
 
53
- ## STEP 2 — Credentials
54
- Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
55
- one place both ids live.
56
53
 
57
- ## STEP 3 — Theme (nothing to style on the shipped components)
54
+ ## STEP 2 — Theme (nothing to style on the shipped components)
58
55
  The shipped components carry **no palette of their own** — they render from base44's design tokens
59
56
  in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`,
60
57
  `--muted`, `--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`,
@@ -62,10 +59,10 @@ in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `
62
59
  `font-display`). Those tokens are **already set to the brand by the design phase**, so the shipped
63
60
  pages are themed with zero work here. To adjust the palette, edit `index.css` (`:root` **and**
64
61
  `.dark`) — the base44 way; **never add a parallel theme file (e.g. a `theme.css`) or restyle the
65
- shipped JSX.** Build the Home/Header you add (STEP 4) from the **same** base44 tokens/classes so it
62
+ shipped JSX.** Build the Home/Header you add (STEP 3) from the **same** base44 tokens/classes so it
66
63
  matches automatically. A dark brand is just base44's dark palette in `index.css` — no per-component work.
67
64
 
68
- ## STEP 4 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
65
+ ## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
69
66
  **No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
70
67
  `App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
71
68
  replace it. Pricing Plans needs **no** cross-page provider (checkout is a redirect, not client
@@ -125,7 +122,7 @@ function Layout() {
125
122
 
126
123
  ## What you build (not shipped)
127
124
  The **home / landing page**, the **`Header`** and a **`Footer`** — the two you drop into the
128
- `Layout` (STEP 4) so they wrap every route — plus the overall brand story, styled with the same
125
+ `Layout` (STEP 3) so they wrap every route — plus the overall brand story, styled with the same
129
126
  base44 tokens/classes. **Compose the shipped pieces** — a "featured plans" strip is just `queryPlans` +
130
127
  the shipped `PlanGrid`; the nav is links to `/plans` and `/my-plans`:
131
128
 
@@ -214,9 +211,8 @@ Fallback only — when you hit an error or need something not shown here: read t
214
211
  file under `src/`, or look it up via the **`wix-docs`** skill.
215
212
 
216
213
  ## Hard rules
217
- - Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
218
214
  - Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
219
- - Header/footer live in a `Layout` around `<Outlet/>` (STEP 4) — never edit the shipped
215
+ - Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped
220
216
  `Plans`/`PlanDetail`/`MyPlans` to add chrome.
221
217
  - The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your
222
218
  `Header` is plain in-flow markup (not `position:fixed`).
@@ -9,7 +9,7 @@ plain data.
9
9
 
10
10
  ```js
11
11
  // build-time exec_tool
12
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
12
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
13
13
  const fs = require("fs");
14
14
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
15
15
  const seed = (() => { const m = { exports: {} };
@@ -35,9 +35,9 @@ so you don't need to open them:**
35
35
  | `components/ItemDialog.jsx` | dish detail modal — description, price/variants, modifier groups (display), quantity, add-to-order |
36
36
  | `components/OrderCartButton.jsx` | header order **icon** button with a live-count badge |
37
37
  | `components/OrderCartDrawer.jsx` | slide-over order cart (mount once; opens from `useOrderCart`) |
38
- | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 4) |
38
+ | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
39
39
  | `pages/Menu.jsx`, `pages/Reservations.jsx` | the two shipped routes (`/menu`, `/reservations`) |
40
- | `rest/wix-config.js` | **you set the ids here** (STEP 2) |
40
+ | `rest/wix-config.js` | the two ids, written by the install step |
41
41
  | `rest/wix-client.js` | REST transport + visitor-token mint/refresh (the refresh token IS the cart identity) |
42
42
  | `rest/wix-restaurants-menu.js` | menu read helpers — `getFullMenu` (the assembled tree; start here) + raw `list*` |
43
43
  | `rest/wix-restaurants-ordering.js` | ordering — operations, add/update/remove, `checkout` |
@@ -50,11 +50,8 @@ real fallback — a runtime error, or a field the snippets don't cover (see "Fal
50
50
  end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
51
51
  `references/restaurants/app/` → `src/`.)
52
52
 
53
- ## STEP 2 — Credentials
54
- Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
55
- one place both ids live.
56
53
 
57
- ## STEP 3 — Theme (nothing to style on the shipped components)
54
+ ## STEP 2 — Theme (nothing to style on the shipped components)
58
55
  The shipped components (menu, item dialog, order cart, reservations) carry **no palette of their
59
56
  own** — they render from base44's design tokens in `src/index.css` (`:root`/`.dark`: `--background`,
60
57
  `--foreground`, `--card`, `--primary`, `--muted`, `--border`, `--radius`, `--font-*`) via shadcn
@@ -62,11 +59,11 @@ Tailwind classes (`bg-card`, `text-foreground`, `bg-primary`, `text-muted-foregr
62
59
  `border-border`, `rounded-lg`, `font-display`). Those tokens are **already set to the brand by the
63
60
  design phase**, so the shipped pages are themed with zero work here. To adjust the palette, edit
64
61
  `index.css` (`:root` **and** `.dark`) — the base44 way; **never add a parallel theme file (e.g. a
65
- `theme.css`) or restyle the shipped JSX.** Build the Home/Header you add (STEP 4) from the **same**
62
+ `theme.css`) or restyle the shipped JSX.** Build the Home/Header you add (STEP 3) from the **same**
66
63
  base44 tokens/classes so it matches automatically. A dark brand is just base44's dark palette in
67
64
  `index.css` — no per-component work.
68
65
 
69
- ## STEP 4 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
66
+ ## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
70
67
  **No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
71
68
  `App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
72
69
  replace it.
@@ -129,7 +126,7 @@ function Layout() {
129
126
 
130
127
  ## What you build (not shipped)
131
128
  The **home / landing page**, the **`Header`** (mount `<OrderCartButton/>` in it) and a **`Footer`** —
132
- the two you drop into the `Layout` (STEP 4) so they wrap every route — plus the overall brand story,
129
+ the two you drop into the `Layout` (STEP 3) so they wrap every route — plus the overall brand story,
133
130
  styled with the same base44 tokens/classes. The nav is an `<OrderCartButton/>` (a clean order-**icon**
134
131
  button with a live-count badge — render it as-is, don't wrap it in your own text button) + links to
135
132
  `/menu` and `/reservations`:
@@ -229,9 +226,8 @@ Fallback only — when you hit an error or need something not shown here: read t
229
226
  file under `src/`, or look it up via the **`wix-docs`** skill.
230
227
 
231
228
  ## Hard rules
232
- - Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
233
229
  - Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
234
- - Header/footer live in a `Layout` around `<Outlet/>` (STEP 4) — never edit the shipped `Menu`/`Reservations` to add chrome.
230
+ - Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped `Menu`/`Reservations` to add chrome.
235
231
  - The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your `Header` is plain in-flow markup (not `position:fixed`).
236
232
  - Order through the shipped cart: `addItem()` → `checkout()` (redirect-session) — never a hand-built `/checkout`, ordering, or reservation URL.
237
233
  - Reservations: offer only `AVAILABLE` slots; pass the hold's `revision` into reserve; `firstName` + `phone` (E.164) are mandatory.
@@ -17,7 +17,7 @@ across exec calls. Online ordering and table reservations run only when the plan
17
17
 
18
18
  ```js
19
19
  // build-time exec_tool
20
- const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix"); // Base44 (generic: use $TOKEN)
20
+ const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
21
21
  const fs = require("fs");
22
22
  // exec_tool's require can return EMPTY exports for these build-time modules — load the file itself:
23
23
  const seed = (() => { const m = { exports: {} };
@@ -1,7 +1,7 @@
1
1
  // Preview-only banner linking the running app to its Wix Business Manager (the back office).
2
2
  // Renders while the app is previewed or run locally, never on the published site, and is
3
3
  // dismissible (persisted per site).
4
- // Mount it at the top of your Layout's fixed region, ABOVE <Header/> (see INSTRUCTIONS STEP 4) —
4
+ // Mount it at the top of your Layout's fixed region, ABOVE <Header/> (see INSTRUCTIONS STEP 3) —
5
5
  // banner + header ride together as one fixed block, so nothing drifts or gaps on scroll.
6
6
  import { useState } from "react";
7
7
  import { WIX_METASITE_ID } from "@/rest/wix-config";
@@ -1,6 +1,8 @@
1
- // Wix credentials — the ONLY file you set these in. Paste both values from your prompt,
2
- // replacing the placeholders. Safe to commit: WIX_CLIENT_ID is a public, buyer-facing id
3
- // (it only mints anonymous visitor tokens); WIX_METASITE_ID just identifies the site.
1
+ // Wix credentials. The install step writes this file: deploy.cjs --client-id … --metasite-id …
2
+ // If you're seeing the placeholders below, re-run that command with the ids from the prompt rather
3
+ // than typing them in here.
4
+ // Safe to commit: WIX_CLIENT_ID is a public, buyer-facing id (it only mints anonymous visitor
5
+ // tokens); WIX_METASITE_ID just identifies the site.
4
6
  // wix-client.js and wix-manage-banner.js read their ids from here.
5
7
  export const WIX_CLIENT_ID = "<YOUR-CLIENT-ID>";
6
8
  export const WIX_METASITE_ID = "<YOUR-METASITE-ID>";
@@ -24,15 +24,18 @@ so you don't need to open them:**
24
24
  | `context/CartContext.jsx` | `useCart()` provider: server cart, add/update/remove, checkout |
25
25
  | `hooks/useProductDetail.js` | PDP data — product + variant resolution for a slug, plus load/add state |
26
26
  | `hooks/useShop.js` | catalog listing — category menu, cursor paging, sort, failure state |
27
- | `components/ProductCard.jsx`, `ProductGrid.jsx` | product listing UI (grid + card, skeletons, empty state). The tile carries quick add for single-variant products, colour dots + an option summary, and pre-order / sold-out / limited-stock / percent-off / merchant-ribbon badges — all from the list query, no extra request |
27
+ | `hooks/useProductCard.js` | headless data layer for a grid tile — returns `leftBadges`, `promoBadge`, `priceDisplay`, `compareAtDisplay`, `colors`, `optionLabel`, `isQuickAddable`, `image`, `hoverImage`; **always use this to build your own product card UI on the grid** |
28
+ | `components/ProductCard.jsx` | reference implementation of a grid tile built on `useProductCard` — read it for inspiration, build your own component rather than using it directly |
29
+ | `components/ProductGrid.jsx` | reference grid layout (2-col mobile → auto-fill desktop) with skeleton + empty state — read it for the skeleton/empty-state patterns, build your own layout rather than using it directly |
28
30
  | `components/ProductGallery.jsx` | PDP main image + thumbnails |
29
31
  | `lib/storeImage.js` | `productImage()` / `productGallery()` / `storeImage()` — normalise Wix image urls |
30
32
  | `components/CartButton.jsx` | header cart **icon** button with a live-count badge |
31
33
  | `components/CartDrawer.jsx` | slide-over cart (mount once; opens from `useCart`) |
32
- | `components/VariantPicker.jsx` | option/variant selector used on the PDP — colour options render as real swatches, and picking one moves the gallery to that colour's photo |
33
- | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 4) |
34
+ | `hooks/useVariantOptions.js` | headless data layer for options/modifiers — returns `optionGroups` + `modifierGroups` (normalised, render-agnostic); **always use this to build your own variant UI on the PDP** |
35
+ | `components/VariantPicker.jsx` | reference implementation of a variant selector built on `useVariantOptions` (pills + colour swatches) — read it for inspiration, but build your own component rather than using it directly |
36
+ | `components/WixManageBanner.jsx` | preview-only manage banner — drop it into your Layout (STEP 3) |
34
37
  | `pages/Shop.jsx`, `pages/ProductDetail.jsx` | the two shipped routes (`/shop`, `/product/:slug`) |
35
- | `rest/wix-config.js` | **you set the ids here** (STEP 2) |
38
+ | `rest/wix-config.js` | the two ids, written by the install step |
36
39
  | `rest/wix-client.js` + `rest/wix-store-*.js` | REST transport + catalog/cart helpers |
37
40
 
38
41
  They're already in place — go **straight to theming + wiring**, nothing to verify first. **Don't
@@ -42,11 +45,8 @@ real fallback — a runtime error, or a field the snippets don't cover (see "Fal
42
45
  end). (Files missing? the install's `deploy` result lists what it wrote; re-run install, or copy
43
46
  `references/storefront/app/` → `src/`.)
44
47
 
45
- ## STEP 2 — Credentials
46
- Write `src/rest/wix-config.js` with your `WIX_CLIENT_ID` and `WIX_METASITE_ID` from the prompt — the
47
- one place both ids live.
48
48
 
49
- ## STEP 3 — Theme (nothing to style on the shipped components)
49
+ ## STEP 2 — Theme (nothing to style on the shipped components)
50
50
  The shipped components carry **no palette of their own** — they render from base44's design tokens
51
51
  in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `--primary`,
52
52
  `--muted`, `--border`, `--radius`, `--font-*`) via shadcn Tailwind classes (`bg-card`,
@@ -54,10 +54,10 @@ in `src/index.css` (`:root`/`.dark`: `--background`, `--foreground`, `--card`, `
54
54
  `font-display`). Those tokens are **already set to the brand by the design phase**, so the shipped
55
55
  pages are themed with zero work here. To adjust the palette, edit `index.css` (`:root` **and**
56
56
  `.dark`) — the base44 way; **never add a parallel theme file (e.g. a `theme.css`) or restyle the
57
- shipped JSX.** Build the Home/Header you add (STEP 4) from the **same** base44 tokens/classes so it
57
+ shipped JSX.** Build the Home/Header you add (STEP 3) from the **same** base44 tokens/classes so it
58
58
  matches automatically. A dark brand is just base44's dark palette in `index.css` — no per-component work.
59
59
 
60
- ## STEP 4 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
60
+ ## STEP 3 — Wire routes + provider (surgical `find_replace` on `src/App.jsx`, never a rewrite)
61
61
  **No file reads needed to wire this.** Every shipped page and `WixManageBanner` is a default export that takes **no props** — wire them exactly as the snippet shows; nothing in those files needs looking up.
62
62
  `App.jsx` carries required platform auth scaffolding (`AuthProvider`/`useAuth`) — edit it in, don't
63
63
  replace it.
@@ -73,6 +73,69 @@ replace it.
73
73
  banner is dismissed.
74
74
  - Routes under the Layout: `/shop` → `Shop`, `/product/:slug` → `ProductDetail` (both shipped, as-is).
75
75
  **You add `/` → your own Home** page.
76
+ - **Build your own variant selector on the PDP — this is required, not optional, and it's your chance to be creative.** `ProductDetail.jsx` ships with a `VariantPicker` import — remove it and replace with your own component built on `useVariantOptions`. Design the controls to fit the brief: the business type, the tone, the audience. A fashion brand might want large colour swatches and a size chart link; a tech store might want a compact dropdown. `VariantPicker.jsx` is in `src/` for reference — read it, don't use it:
77
+ - **Build your own product card for the grid — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships with a `ProductCard` import — replace it with your own component built on `useProductCard`. The hook hands you everything the tile needs (badges, price display, colour dots, quick-add flag, images) — you decide the layout, shape, hover behaviour, and CTA style. A lifestyle brand might want full-bleed images with an overlay gradient; a tech store might want a compact horizontal list item. `ProductCard.jsx` is in `src/` for reference — read it, don't use it:
78
+ - **Build your own grid layout — this is required, not optional, and it's your chance to be creative.** `ProductGrid.jsx` ships as a reference (2-col mobile → auto-fill desktop, 220px min) — replace it with an arrangement that fits the brief. A curated boutique might want a 3-col asymmetric editorial layout; a high-volume store might want a dense 4-col grid; a featured strip on the home page might want horizontal scroll. Keep the skeleton and empty-state patterns from `ProductGrid.jsx` (copy them into your own component) — the states themselves are correct, just the layout is yours to choose.
79
+
80
+ ```jsx
81
+ import { Link } from "react-router-dom";
82
+ import { useCart } from "@/context/CartContext";
83
+ import { useProductCard } from "@/hooks/useProductCard";
84
+
85
+ export default function MyProductCard({ product }) {
86
+ const { addToCart } = useCart();
87
+ const {
88
+ isSoldOut, isPreorder,
89
+ leftBadges, // [{ type: 'pre-order'|'sold-out'|'limited-stock', label }] — render left side of image
90
+ promoBadge, // { type: 'discount'|'ribbon', label } | null — render right side
91
+ priceDisplay, // "€10" or "€10 – €20" (range when variants differ)
92
+ compareAtDisplay, // original price string | null
93
+ colors, // hex strings → render as dots (slice to how many you want)
94
+ optionLabel, // "3 sizes · 2 materials" or empty string
95
+ isQuickAddable, // true for single-variant, in-stock products
96
+ image, // primary image URL | null
97
+ hoverImage, // second image URL | null
98
+ } = useProductCard(product);
99
+
100
+ // Then render however you want:
101
+ return (
102
+ <div>
103
+ {/* image, badges, price, colour dots, quick-add or "Choose options" CTA */}
104
+ {isQuickAddable && <button onClick={() => addToCart(product.id)}>Quick add</button>}
105
+ {!isQuickAddable && !isSoldOut && <Link to={`/product/${product.slug}`}>Choose options</Link>}
106
+ {isSoldOut && isPreorder && <Link to={`/product/${product.slug}`}>Pre-order</Link>}
107
+ </div>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ```jsx
113
+ import { useVariantOptions } from "@/hooks/useVariantOptions";
114
+
115
+ // options/modifiers/selectedOptions/modifierValues come from useProductDetail:
116
+ const { optionGroups, modifierGroups } = useVariantOptions(options, modifiers, selectedOptions, modifierValues);
117
+
118
+ // optionGroups: [{ id, name, isColor, choices: [{ choiceId, name, colorCode, isColorSwatch, inStock, selected }] }]
119
+ // modifierGroups: [{ key, name, mandatory, type: 'choices'|'text', choices?: [{ key, name, selected }], value?: string }]
120
+
121
+ // Then render however you want:
122
+ optionGroups.map((group) =>
123
+ group.choices.map((c) =>
124
+ c.isColorSwatch
125
+ ? <MySwatch key={c.choiceId} color={c.colorCode} active={c.selected} disabled={!c.inStock}
126
+ onClick={() => selectOption(group.id, c.choiceId)} />
127
+ : <MyPill key={c.choiceId} active={c.selected} disabled={!c.inStock}
128
+ onClick={() => selectOption(group.id, c.choiceId)}>{c.name}</MyPill>
129
+ )
130
+ );
131
+ modifierGroups.map((m) =>
132
+ m.type === "text"
133
+ ? <MyInput key={m.key} label={m.name} value={m.value} onChange={(v) => setModifier(m.key, v)} />
134
+ : m.choices.map((c) =>
135
+ <MyPill key={c.key} active={c.selected} onClick={() => setModifier(m.key, c.key)}>{c.name}</MyPill>
136
+ )
137
+ );
138
+ ```
76
139
 
77
140
  ```jsx
78
141
  import { useRef, useState, useEffect } from "react";
@@ -120,7 +183,7 @@ function Layout() {
120
183
 
121
184
  ## What you build (not shipped)
122
185
  The **home / landing page**, the **`Header`** (mount `<CartButton/>` in it) and a **`Footer`** — the
123
- two you drop into the `Layout` (STEP 4) so they wrap every route — plus the overall brand story,
186
+ two you drop into the `Layout` (STEP 3) so they wrap every route — plus the overall brand story,
124
187
  styled from the same base44 tokens/classes. **Compose the shipped pieces** — a
125
188
  featured strip is just `queryProducts` + the shipped `ProductGrid`; the nav is a `<CartButton/>`
126
189
  (a clean cart-**icon** button with a live-count badge — render it as-is, don't wrap it in your own
@@ -243,9 +306,8 @@ reference page inline; these are the areas they sit in:
243
306
  - Headless redirect session (hosted checkout): https://dev.wix.com/docs/api-reference/business-management/headless/redirects.md
244
307
 
245
308
  ## Hard rules
246
- - Set `WIX_CLIENT_ID` (STEP 2) — not the placeholder.
247
309
  - Style via base44 design tokens (`index.css` / shadcn Tailwind classes), never by rewriting the shipped components or adding a parallel theme file.
248
- - Header/footer live in a `Layout` around `<Outlet/>` (STEP 4) — never edit the shipped `Shop`/`ProductDetail` to add chrome.
310
+ - Header/footer live in a `Layout` around `<Outlet/>` (STEP 3) — never edit the shipped `Shop`/`ProductDetail` to add chrome.
249
311
  - The Layout's fixed top region owns positioning: `<WixManageBanner/>` above `<Header/>`; your `Header` is plain in-flow markup (not `position:fixed`).
250
312
  - Checkout goes through the shipped cart (redirect-session) — never a hand-built `/checkout` URL.
251
313
  - Render live Wix data or the shipped empty state — never mock products.
@@ -1,72 +1,29 @@
1
- // Grid tile. Styled with base44 design tokens (shadcn Tailwind classes: bg-card / text-foreground /
2
- // border-border) — re-skin via the app's design tokens (src/index.css :root/.dark), not this JSX.
3
- // The price / stock / ribbon field paths are load-bearing; image URLs go through lib/storeImage so the
4
- // grid, the PDP gallery and the cart all normalise them the same way.
5
- //
6
- // Everything here comes from the list query — the tile costs no extra request. What the list query
7
- // does NOT carry is a stock COUNT (inventory has availabilityStatus only), so "Limited stock" is as
8
- // precise as a tile can honestly get; "Only 2 left" would need per-variant inventory per product.
1
+ // Reference implementation of a product grid tile built on useProductCard.
2
+ // Styled with base44 design tokens (shadcn Tailwind classes: bg-card / text-foreground / border-border).
3
+ // Build your own card on useProductCard instead — read this for inspiration, don't use it directly.
9
4
  import { useState } from "react";
10
5
  import { Link } from "react-router-dom";
11
- import { productGallery, productImage } from "@/lib/storeImage";
12
6
  import { useCart } from "@/context/CartContext";
7
+ import { useProductCard } from "@/hooks/useProductCard";
13
8
 
14
- // Percent off, from the raw amounts — the formatted strings ("€129.00") can't be subtracted.
15
- function discountPercent(product) {
16
- const now = Number(product?.actualPriceRange?.minValue?.amount);
17
- const was = Number(product?.compareAtPriceRange?.minValue?.amount);
18
- if (!now || !was || was <= now) return null;
19
- return Math.round(((was - now) / was) * 100);
20
- }
21
-
22
- // What the buyer gets to choose, previewed on the tile: colour options become real dots (the colours
23
- // say more than "3 colors" could), everything else becomes "4 sizes". Pluralised from the option's own
24
- // name, so the wording follows the merchant's catalogue rather than a hardcoded English word.
25
- function optionsPreview(product) {
26
- const labels = [], colors = [];
27
- for (const o of product?.options || []) {
28
- const choices = (o.choicesSettings?.choices || []).filter((c) => c.visible !== false);
29
- if (!choices.length) continue;
30
- const swatches = choices.map((c) => c.colorCode).filter(Boolean);
31
- if (swatches.length) { colors.push(...swatches); continue; }
32
- const n = o.name.toLowerCase();
33
- labels.push(`${choices.length} ${choices.length === 1 || n.endsWith("s") ? n : `${n}s`}`);
34
- }
35
- return { label: labels.join(" · "), colors };
36
- }
37
-
38
- // One rule for every chip on the tile, so a new badge has an obvious home instead of a fresh colour:
39
- // promotional shouts in the brand colour, informational sits quietly on a card surface, and only a
40
- // blocking state is destructive. Each background travels with its own foreground token.
41
9
  const badge = "px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide rounded-full";
42
- const badgePromo = `${badge} bg-primary text-primary-foreground`;
43
- const badgeInfo = `${badge} bg-card text-foreground border border-border`;
44
- const badgeBlocked = `${badge} bg-destructive text-destructive-foreground`;
10
+ const badgePromo = `${badge} bg-primary text-primary-foreground`;
11
+ const badgeInfo = `${badge} bg-card text-foreground border border-border`;
12
+ const badgeBlocked = `${badge} bg-destructive text-destructive-foreground`;
13
+
14
+ const badgeClass = { "pre-order": badgeInfo, "sold-out": badgeBlocked, "limited-stock": badgeInfo };
45
15
 
46
16
  export default function ProductCard({ product }) {
47
17
  const { addToCart } = useCart();
48
18
  const [adding, setAdding] = useState(false);
49
19
 
50
- const image = productImage(product);
51
- const hoverImage = productGallery(product)[1]?.url;
52
- const price = product?.actualPriceRange?.minValue?.formattedAmount;
53
- const priceMax = product?.actualPriceRange?.maxValue?.formattedAmount;
54
- const compareAt = product?.compareAtPriceRange?.minValue?.formattedAmount;
55
-
56
- const status = product?.inventory?.availabilityStatus;
57
- const soldOut = status === "OUT_OF_STOCK";
58
- const preorder = product?.inventory?.preorderStatus === "ENABLED";
59
- const discount = discountPercent(product);
60
- // A merchant-set ribbon ("New", "Best Seller") is the catalogue's own badge, so it beats anything
61
- // derived here — a "new" label computed from createdDate would flag the whole catalogue at once
62
- // right after seeding. Suppressed when a discount badge is showing, since that ribbon is "Sale".
63
- const ribbon = product?.ribbon?.name;
64
- const { label: optionLabel, colors } = optionsPreview(product);
65
- const hasOptions = (product?.options?.length || 0) > 0;
20
+ const {
21
+ isSoldOut, isPreorder, leftBadges, promoBadge,
22
+ priceDisplay, compareAtDisplay,
23
+ colors, optionLabel,
24
+ isQuickAddable, image, hoverImage,
25
+ } = useProductCard(product);
66
26
 
67
- // No options means a single variant, which the cart resolves from the product id alone — so the
68
- // tile can add straight to the cart with no extra request. The drawer opening is the confirmation
69
- // (and carries the reason when it fails), which is why there's no "Added ✓" state to fake here.
70
27
  const quickAdd = async () => {
71
28
  setAdding(true);
72
29
  await addToCart(product.id);
@@ -79,11 +36,8 @@ export default function ProductCard({ product }) {
79
36
  <Link to={`/product/${product.slug}`} aria-label={product.name} className="block w-full h-full">
80
37
  {image ? (
81
38
  <>
82
- {/* Fade out only when there IS a second shot behind it — a one-photo product would
83
- otherwise hover to an empty frame. */}
84
39
  <img src={image} alt={product.name} loading="lazy"
85
40
  className={`w-full h-full object-cover ${hoverImage ? "transition-opacity duration-300 md:group-hover:opacity-0" : ""}`} />
86
- {/* Second catalogue shot on hover. `hidden` below md so phones never fetch it. */}
87
41
  {hoverImage && (
88
42
  <img src={hoverImage} alt="" aria-hidden="true" loading="lazy"
89
43
  className="hidden md:block absolute inset-0 w-full h-full object-cover opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
@@ -99,51 +53,40 @@ export default function ProductCard({ product }) {
99
53
  </Link>
100
54
 
101
55
  <div className="absolute top-2 left-2 flex flex-col items-start gap-1 pointer-events-none">
102
- {soldOut && preorder && <span className={badgeInfo}>Pre-order</span>}
103
- {soldOut && !preorder && <span className={badgeBlocked}>Sold out</span>}
104
- {status === "PARTIALLY_OUT_OF_STOCK" && <span className={badgeInfo}>Limited stock</span>}
56
+ {leftBadges.map((b) => (
57
+ <span key={b.type} className={badgeClass[b.type]}>{b.label}</span>
58
+ ))}
105
59
  </div>
106
- {/* Discount and ribbon are mutually exclusive, so both can claim the promotional slot. */}
107
60
  <div className="absolute top-2 right-2 pointer-events-none">
108
- {discount ? <span className={badgePromo}>−{discount}%</span>
109
- : ribbon ? <span className={badgePromo}>{ribbon}</span> : null}
61
+ {promoBadge && <span className={badgePromo}>{promoBadge.label}</span>}
110
62
  </div>
111
63
 
112
- {/* Sold out with no pre-order has nothing to offer, so no control appears at all. */}
113
- {(!soldOut || preorder) && (
64
+ {(!isSoldOut || isPreorder) && (
114
65
  <div className="absolute inset-x-2 bottom-2 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
115
- {/* Same tokens as the PDP's Add-to-cart button: one action, one treatment, so the tile's
116
- CTA is the brand colour rather than "whatever the text colour is". */}
117
- {hasOptions || soldOut ? (
118
- <Link to={`/product/${product.slug}`}
119
- className="block w-full text-center py-2.5 px-4 rounded-full bg-primary text-primary-foreground no-underline text-sm font-semibold">
120
- {soldOut ? "Pre-order" : "Choose options"}
121
- </Link>
122
- ) : (
66
+ {isQuickAddable ? (
123
67
  <button onClick={quickAdd} disabled={adding}
124
68
  className="w-full py-2.5 px-4 rounded-full bg-primary text-primary-foreground border-none text-sm font-semibold cursor-pointer transition-opacity hover:opacity-90 disabled:opacity-60 disabled:cursor-wait">
125
69
  {adding ? "Adding…" : "Quick add"}
126
70
  </button>
71
+ ) : (
72
+ <Link to={`/product/${product.slug}`}
73
+ className="block w-full text-center py-2.5 px-4 rounded-full bg-primary text-primary-foreground no-underline text-sm font-semibold">
74
+ {isSoldOut ? "Pre-order" : "Choose options"}
75
+ </Link>
127
76
  )}
128
77
  </div>
129
78
  )}
130
79
  </div>
131
80
 
132
81
  <div className="p-3 flex flex-col gap-1">
133
- {/* Side by side once the tile is wide enough; stacked on a phone's two-up grid, where a long
134
- name wrapping to three lines against a top-right price reads as broken. */}
135
82
  <div className="flex flex-col gap-1 md:flex-row md:items-baseline md:justify-between md:gap-3">
136
- {/* Clamped to two lines: a 220px tile turns a long product name into three or four ragged
137
- lines beside a top-right price. The full name is on the tile's link and the PDP. */}
138
83
  <h3 className="m-0 font-display text-[15px] font-semibold line-clamp-2">
139
84
  <Link to={`/product/${product.slug}`} className="no-underline text-foreground">{product.name}</Link>
140
85
  </h3>
141
86
  <div className="flex gap-2 items-baseline flex-wrap md:justify-end">
142
- {/* A product whose variants differ in price spans a range — showing only minValue reads as
143
- the price of everything, then the PDP appears to raise it once a variant is picked. */}
144
- <span className="font-semibold whitespace-nowrap">{priceMax && priceMax !== price ? `${price} – ${priceMax}` : price}</span>
145
- {compareAt && compareAt !== price && (
146
- <span className="text-muted-foreground line-through text-[13px] whitespace-nowrap">{compareAt}</span>
87
+ <span className="font-semibold whitespace-nowrap">{priceDisplay}</span>
88
+ {compareAtDisplay && (
89
+ <span className="text-muted-foreground line-through text-[13px] whitespace-nowrap">{compareAtDisplay}</span>
147
90
  )}
148
91
  </div>
149
92
  </div>
@@ -1,11 +1,6 @@
1
- // Responsive product grid, with the two states a fresh store spends most of its life in: loading and
2
- // empty. Styled with base44 design tokens (shadcn Tailwind classes).
3
- //
4
- // Two columns on phones — a 220px minmax leaves room for only one, and a one-up catalog scrolls
5
- // forever — then auto-fill from md up.
6
- //
7
- // The empty state carries weight here — a catalog is seeded separately from the build, so it is
8
- // routinely empty when the merchant first opens the page. Say why, rather than showing a bare line.
1
+ // Reference implementation of a product grid — 2-col mobile, auto-fill desktop, with a skeleton
2
+ // loading state and a descriptive empty state. Build your own layout instead (see INSTRUCTIONS.md
3
+ // STEP 3) — read this for the skeleton/empty-state patterns, don't use it directly.
9
4
  import ProductCard from "./ProductCard";
10
5
 
11
6
  // Skeleton tiles match ProductCard's aspect-square + two text lines, so nothing shifts when the real