create-brainerce-store 1.87.0 → 1.88.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +9 -2
- package/messages/en.json +10 -1
- package/messages/he.json +10 -1
- package/package.json +1 -1
- package/templates/nextjs/base/.eslintrc.json +1 -0
- package/templates/nextjs/base/AI-GUIDE.md +224 -221
- package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +296 -296
- package/templates/nextjs/base/src/app/checkout/page.tsx +89 -6
- package/templates/nextjs/base/src/app/products/layout.tsx.ejs +18 -0
- package/templates/nextjs/base/src/components/auth/register-form.tsx +381 -375
- package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +14 -8
- package/templates/nextjs/base/src/core/hooks/use-page-exists.ts +37 -0
- package/templates/nextjs/base/src/core/lib/add-to-cart-error.ts +27 -2
- package/templates/nextjs/base/src/core/lib/brainerce.server.ts +121 -121
- package/templates/nextjs/base/src/core/lib/coupon-error.ts +29 -0
- package/templates/nextjs/base/src/ui/cart/cart-bundle-offer.tsx +6 -2
- package/templates/nextjs/base/src/ui/cart/cart-upgrade-banner.tsx +6 -4
- package/templates/nextjs/base/src/ui/cart/coupon-input.tsx +138 -134
- package/templates/nextjs/base/src/ui/cart/tax-estimate-line.tsx +10 -3
- package/templates/nextjs/base/src/ui/layout/newsletter-signup.tsx +5 -1
- package/templates/nextjs/base/src/ui/layout/site-footer.tsx.ejs +2 -2
- package/templates/nextjs/base/src/ui/product/frequently-bought-together.tsx +6 -2
- package/templates/nextjs/base/src/ui/product/product-card.tsx +6 -2
- package/templates/nextjs/base/src/ui/product/product-client-section.tsx +4 -36
- package/templates/nextjs/designs/atelier/ui/cart/cart-bundle-offer.tsx +6 -2
- package/templates/nextjs/designs/atelier/ui/cart/cart-upgrade-banner.tsx +6 -4
- package/templates/nextjs/designs/atelier/ui/cart/coupon-input.tsx +146 -142
- package/templates/nextjs/designs/atelier/ui/cart/tax-estimate-line.tsx +10 -3
- package/templates/nextjs/designs/atelier/ui/product/frequently-bought-together.tsx +6 -2
- package/templates/nextjs/designs/atelier/ui/product/product-card.tsx +6 -2
- package/templates/nextjs/designs/atelier/ui/product/product-client-section.tsx +3 -28
- package/templates/nextjs/ui-canvas/cart/cart-bundle-offer.tsx +6 -2
- package/templates/nextjs/ui-canvas/cart/cart-upgrade-banner.tsx +6 -4
- package/templates/nextjs/ui-canvas/cart/coupon-input.tsx +123 -119
- package/templates/nextjs/ui-canvas/cart/tax-estimate-line.tsx +4 -3
- package/templates/nextjs/ui-canvas/layout/newsletter-signup.tsx +5 -1
- package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +2 -2
- package/templates/nextjs/ui-canvas/product/frequently-bought-together.tsx +6 -2
- package/templates/nextjs/ui-canvas/product/product-card.tsx +6 -2
- package/templates/nextjs/ui-canvas/product/product-client-section.tsx +3 -28
- package/templates/nextjs/base/src/app/products/layout.tsx +0 -18
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "create-brainerce-store",
|
|
34
|
-
version: "1.
|
|
34
|
+
version: "1.88.1",
|
|
35
35
|
description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
|
|
36
36
|
homepage: "https://brainerce.com",
|
|
37
37
|
repository: {
|
|
@@ -359,7 +359,14 @@ var BRAINERCE_RUNTIME_DEPS = Object.freeze({
|
|
|
359
359
|
// clear it could not. An exact pin ages once and then stays eligible.
|
|
360
360
|
// Keep it exact when you raise it: a range here is not a convenience, it is
|
|
361
361
|
// an open invitation for tomorrow's release to break yesterday's template.
|
|
362
|
-
|
|
362
|
+
// 3.5.0 (published 2026-09-25, past the one-week cooldown): the floor the template
|
|
363
|
+
// actually needs. Files in `templates/` read `Order.invoiceUrl` /
|
|
364
|
+
// `invoiceNumber` and `TrackingEventItem.productId`, which 2.7.0 does not
|
|
365
|
+
// declare, so a CLEAN scaffold pinned to 2.7.0 failed `next build` with 7 type
|
|
366
|
+
// errors (2026-10-04 QA). The dead `product.downloads` read the 3.x types
|
|
367
|
+
// rejected was removed from the template in the same change. Raise this only
|
|
368
|
+
// after scaffolding a clean store and running `tsc` + `next build` against it.
|
|
369
|
+
brainerce: "3.5.0",
|
|
363
370
|
"isomorphic-dompurify": "^3.8.0"
|
|
364
371
|
});
|
|
365
372
|
|
package/messages/en.json
CHANGED
|
@@ -108,6 +108,8 @@
|
|
|
108
108
|
"addedToCart": "Added to Cart!",
|
|
109
109
|
"cartFull": "Your cart is full. Remove an item before adding this one.",
|
|
110
110
|
"addToCartFailed": "We could not add this to your cart. Please try again.",
|
|
111
|
+
"addNotEnoughStock": "There is not enough stock left for that quantity.",
|
|
112
|
+
"addItemUnavailable": "This item is no longer available.",
|
|
111
113
|
"outOfStock": "Out of Stock",
|
|
112
114
|
"inStock": "In Stock",
|
|
113
115
|
"unavailable": "Unavailable",
|
|
@@ -546,7 +548,14 @@
|
|
|
546
548
|
},
|
|
547
549
|
"coupon": {
|
|
548
550
|
"placeholder": "Coupon code",
|
|
549
|
-
"invalidCode": "Invalid coupon code"
|
|
551
|
+
"invalidCode": "Invalid coupon code",
|
|
552
|
+
"notActive": "This coupon is not active yet.",
|
|
553
|
+
"expired": "This coupon has expired.",
|
|
554
|
+
"usageLimitReached": "This coupon is no longer available.",
|
|
555
|
+
"perCustomerLimitReached": "You have already used this coupon the maximum number of times.",
|
|
556
|
+
"minimumNotMet": "Your order does not reach the minimum amount for this coupon.",
|
|
557
|
+
"regionRestricted": "This coupon is not available in your region.",
|
|
558
|
+
"noEligibleItems": "This coupon does not apply to any items in your cart."
|
|
550
559
|
},
|
|
551
560
|
"contact": {
|
|
552
561
|
"title": "Contact Us",
|
package/messages/he.json
CHANGED
|
@@ -108,6 +108,8 @@
|
|
|
108
108
|
"addedToCart": "נוסף לעגלה!",
|
|
109
109
|
"cartFull": "העגלה מלאה. הסירו מוצר אחד לפני הוספת המוצר הזה.",
|
|
110
110
|
"addToCartFailed": "לא הצלחנו להוסיף את המוצר לעגלה. נסו שוב.",
|
|
111
|
+
"addNotEnoughStock": "אין מספיק מלאי לכמות הזו.",
|
|
112
|
+
"addItemUnavailable": "המוצר הזה אינו זמין עוד.",
|
|
111
113
|
"outOfStock": "אזל מהמלאי",
|
|
112
114
|
"inStock": "במלאי",
|
|
113
115
|
"unavailable": "לא זמין",
|
|
@@ -546,7 +548,14 @@
|
|
|
546
548
|
},
|
|
547
549
|
"coupon": {
|
|
548
550
|
"placeholder": "קוד קופון",
|
|
549
|
-
"invalidCode": "קוד קופון לא תקין"
|
|
551
|
+
"invalidCode": "קוד קופון לא תקין",
|
|
552
|
+
"notActive": "הקופון הזה עדיין לא פעיל.",
|
|
553
|
+
"expired": "תוקף הקופון הזה פג.",
|
|
554
|
+
"usageLimitReached": "הקופון הזה כבר לא זמין.",
|
|
555
|
+
"perCustomerLimitReached": "כבר השתמשת בקופון הזה את מספר הפעמים המרבי.",
|
|
556
|
+
"minimumNotMet": "סכום ההזמנה לא מגיע למינימום הנדרש לקופון הזה.",
|
|
557
|
+
"regionRestricted": "הקופון הזה לא זמין באזור שלך.",
|
|
558
|
+
"noEligibleItems": "הקופון הזה לא חל על אף מוצר בעגלה שלך."
|
|
550
559
|
},
|
|
551
560
|
"contact": {
|
|
552
561
|
"title": "צור קשר",
|
package/package.json
CHANGED
|
@@ -1,221 +1,224 @@
|
|
|
1
|
-
# AI-GUIDE — designing this store safely
|
|
2
|
-
|
|
3
|
-
You (an AI coding assistant, or a human designer) have been asked to redesign
|
|
4
|
-
this storefront. This guide is the contract that lets you go as bold as you
|
|
5
|
-
want without breaking commerce. It was distilled from real agency builds.
|
|
6
|
-
|
|
7
|
-
## The one rule
|
|
8
|
-
|
|
9
|
-
**`src/core/` is the platform's. `src/ui/` is yours.**
|
|
10
|
-
|
|
11
|
-
Everything under `src/ui/` may be rewritten from scratch — layouts, markup,
|
|
12
|
-
classes, animations, entire visual languages. (Canvas-mode stores —
|
|
13
|
-
scaffolded with `--canvas` — ship `src/ui/` as bare unstyled skeletons with
|
|
14
|
-
`DESIGN ME` markers; same rules apply, you're just starting from zero.) Everything under `src/core/`
|
|
15
|
-
(and `src/app/api/`) keeps the store working: cart, checkout, variants,
|
|
16
|
-
inventory, auth, SEO, i18n. Never modify it. If a redesign seems to require a
|
|
17
|
-
core change, stop and say so instead.
|
|
18
|
-
|
|
19
|
-
## File map
|
|
20
|
-
|
|
21
|
-
| Path | Owner | Notes |
|
|
22
|
-
|------|-------|-------|
|
|
23
|
-
| `src/ui/layout/` | you | header, footer, announcement bar |
|
|
24
|
-
| `src/ui/home/` | you | homepage sections |
|
|
25
|
-
| `src/ui/product/` | you | card, grid, listing, product page, price, badges, selectors |
|
|
26
|
-
| `src/ui/cart/` | you | cart page pieces |
|
|
27
|
-
| `src/ui/shared/` | you | loader, image wrapper |
|
|
28
|
-
| `src/app/globals.css` | you | tokens + global styles |
|
|
29
|
-
| `src/components/ui/` | shared | shadcn/ui primitives — compose them, extend via variants |
|
|
30
|
-
| `src/core/**` | platform | hooks, lib, providers — read, never edit |
|
|
31
|
-
| `src/components/{checkout,auth,account,seo}` | platform | core-styled in v1; they inherit your tokens |
|
|
32
|
-
| `src/app/**` | platform | thin routes; edit only to wire new ui components |
|
|
33
|
-
|
|
34
|
-
## The dialect (shadcn/ui + lucide + cn)
|
|
35
|
-
|
|
36
|
-
This store speaks the same component dialect as Lovable / v0 / Bolt output:
|
|
37
|
-
|
|
38
|
-
- **`src/components/ui/` holds official shadcn/ui primitives** (button, card,
|
|
39
|
-
badge, input, label, textarea, select, checkbox, radio-group, separator,
|
|
40
|
-
accordion, dialog, sheet, skeleton, tabs, tooltip). Compose them in `src/ui/`
|
|
41
|
-
instead of hand-rolling controls. Need a new look? **Extend via CVA
|
|
42
|
-
variants** (add a variant to `buttonVariants`/`badgeVariants`), never by
|
|
43
|
-
inline-overriding the same styles in twenty call sites.
|
|
44
|
-
- **Icons: lucide-react only.** No inline `<svg>` paths, no emoji-as-icons,
|
|
45
|
-
no second icon set. Consistent 16/20/24px sizes.
|
|
46
|
-
- **Class merging: `cn()`** from `@/core/lib/utils` (clsx + tailwind-merge) —
|
|
47
|
-
it's the same helper shadcn expects, already wired into every primitive.
|
|
48
|
-
- **Tokens drive everything**: primitives read `--primary`, `--card`,
|
|
49
|
-
`--input`, `--ring`, `--radius`, etc. from `globals.css`. Restyle the store
|
|
50
|
-
by changing tokens (and variants), not by scattering literal colors.
|
|
51
|
-
- `rounded`, `rounded-md`, `rounded-lg` all derive from `--radius`
|
|
52
|
-
(shadcn convention), so corner radius follows the theme automatically.
|
|
53
|
-
|
|
54
|
-
## The hook contract (your data supply)
|
|
55
|
-
|
|
56
|
-
UI components get *everything* from `@/core/hooks/*` and
|
|
57
|
-
`@/core/providers/store-provider` — never from `@/core/lib` directly
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
`ui/
|
|
74
|
-
`
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- **
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- **
|
|
157
|
-
(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
- **
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
- **
|
|
191
|
-
`
|
|
192
|
-
|
|
193
|
-
- **
|
|
194
|
-
|
|
195
|
-
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
-
|
|
1
|
+
# AI-GUIDE — designing this store safely
|
|
2
|
+
|
|
3
|
+
You (an AI coding assistant, or a human designer) have been asked to redesign
|
|
4
|
+
this storefront. This guide is the contract that lets you go as bold as you
|
|
5
|
+
want without breaking commerce. It was distilled from real agency builds.
|
|
6
|
+
|
|
7
|
+
## The one rule
|
|
8
|
+
|
|
9
|
+
**`src/core/` is the platform's. `src/ui/` is yours.**
|
|
10
|
+
|
|
11
|
+
Everything under `src/ui/` may be rewritten from scratch — layouts, markup,
|
|
12
|
+
classes, animations, entire visual languages. (Canvas-mode stores —
|
|
13
|
+
scaffolded with `--canvas` — ship `src/ui/` as bare unstyled skeletons with
|
|
14
|
+
`DESIGN ME` markers; same rules apply, you're just starting from zero.) Everything under `src/core/`
|
|
15
|
+
(and `src/app/api/`) keeps the store working: cart, checkout, variants,
|
|
16
|
+
inventory, auth, SEO, i18n. Never modify it. If a redesign seems to require a
|
|
17
|
+
core change, stop and say so instead.
|
|
18
|
+
|
|
19
|
+
## File map
|
|
20
|
+
|
|
21
|
+
| Path | Owner | Notes |
|
|
22
|
+
|------|-------|-------|
|
|
23
|
+
| `src/ui/layout/` | you | header, footer, announcement bar |
|
|
24
|
+
| `src/ui/home/` | you | homepage sections |
|
|
25
|
+
| `src/ui/product/` | you | card, grid, listing, product page, price, badges, selectors |
|
|
26
|
+
| `src/ui/cart/` | you | cart page pieces |
|
|
27
|
+
| `src/ui/shared/` | you | loader, image wrapper |
|
|
28
|
+
| `src/app/globals.css` | you | tokens + global styles |
|
|
29
|
+
| `src/components/ui/` | shared | shadcn/ui primitives — compose them, extend via variants |
|
|
30
|
+
| `src/core/**` | platform | hooks, lib, providers — read, never edit |
|
|
31
|
+
| `src/components/{checkout,auth,account,seo}` | platform | core-styled in v1; they inherit your tokens |
|
|
32
|
+
| `src/app/**` | platform | thin routes; edit only to wire new ui components |
|
|
33
|
+
|
|
34
|
+
## The dialect (shadcn/ui + lucide + cn)
|
|
35
|
+
|
|
36
|
+
This store speaks the same component dialect as Lovable / v0 / Bolt output:
|
|
37
|
+
|
|
38
|
+
- **`src/components/ui/` holds official shadcn/ui primitives** (button, card,
|
|
39
|
+
badge, input, label, textarea, select, checkbox, radio-group, separator,
|
|
40
|
+
accordion, dialog, sheet, skeleton, tabs, tooltip). Compose them in `src/ui/`
|
|
41
|
+
instead of hand-rolling controls. Need a new look? **Extend via CVA
|
|
42
|
+
variants** (add a variant to `buttonVariants`/`badgeVariants`), never by
|
|
43
|
+
inline-overriding the same styles in twenty call sites.
|
|
44
|
+
- **Icons: lucide-react only.** No inline `<svg>` paths, no emoji-as-icons,
|
|
45
|
+
no second icon set. Consistent 16/20/24px sizes.
|
|
46
|
+
- **Class merging: `cn()`** from `@/core/lib/utils` (clsx + tailwind-merge) —
|
|
47
|
+
it's the same helper shadcn expects, already wired into every primitive.
|
|
48
|
+
- **Tokens drive everything**: primitives read `--primary`, `--card`,
|
|
49
|
+
`--input`, `--ring`, `--radius`, etc. from `globals.css`. Restyle the store
|
|
50
|
+
by changing tokens (and variants), not by scattering literal colors.
|
|
51
|
+
- `rounded`, `rounded-md`, `rounded-lg` all derive from `--radius`
|
|
52
|
+
(shadcn convention), so corner radius follows the theme automatically.
|
|
53
|
+
|
|
54
|
+
## The hook contract (your data supply)
|
|
55
|
+
|
|
56
|
+
UI components get *everything* from `@/core/hooks/*` and
|
|
57
|
+
`@/core/providers/store-provider` — never from `@/core/lib` directly, except
|
|
58
|
+
the approved presentation helpers listed (as `!@/core/lib/...`) in the `src/ui`
|
|
59
|
+
`no-restricted-imports` rule in `.eslintrc.json`. `next build` runs that lint,
|
|
60
|
+
so importing any other `@/core/lib` module from `src/ui` fails the build:
|
|
61
|
+
|
|
62
|
+
- `useHomeData()` → `{ products, banners, loading }`
|
|
63
|
+
- `useProductListing()` → products + filters/sort/facets/pagination + handlers
|
|
64
|
+
- `useProductPage(product)` → variant/image selection, `priceInfo`, `inventory`,
|
|
65
|
+
`quantity`, `handleAddToCart`, `addingToCart`, `addedMessage`, `addToCartError`,
|
|
66
|
+
customization + modifier state and errors
|
|
67
|
+
- `addToCartError` is `'CART_FULL' | 'FAILED' | null`. **Render it, and render it
|
|
68
|
+
outside any `modifierGroups.length > 0` block** — a cart holds at most 50
|
|
69
|
+
distinct products and the 51st add is refused, so a design that drops this
|
|
70
|
+
leaves the shopper tapping a button that resets and adds nothing. Copy lives
|
|
71
|
+
in `productDetail.cartFull` / `productDetail.addToCartFailed`.
|
|
72
|
+
- Five more components hold the same message in their OWN local state, because
|
|
73
|
+
they add to the cart without this hook: `ui/product/product-card.tsx`,
|
|
74
|
+
`ui/product/frequently-bought-together.tsx`, `ui/cart/cart-bundle-offer.tsx`
|
|
75
|
+
(all three: `addError`), `app/checkout/page.tsx` (`bumpError`) and
|
|
76
|
+
`ui/cart/cart-upgrade-banner.tsx` (`upgradeError`). All five use
|
|
77
|
+
`toAddToCartError()` from `@/core/lib/add-to-cart-error`. **Nothing checks that
|
|
78
|
+
you kept them** — `check-template-parity.js` compares `@/ui/...` imports and SDK
|
|
79
|
+
calls only, so dropping one of these leaves a green build and a silent button.
|
|
80
|
+
- `ui/cart/cart-item.tsx` carries the same idea for the two writes it owns,
|
|
81
|
+
in its own `lineError` state: a failed quantity change or a failed Remove
|
|
82
|
+
used to log to the console and leave the row looking untouched. It uses
|
|
83
|
+
`getErrorCode()` from the same file (not `toAddToCartError()` — the 50-line
|
|
84
|
+
cap cannot refuse a quantity change, and "we could not add this" would be a
|
|
85
|
+
lie about a removal) and four `cart.*` strings, mapped in `LINE_ERROR_KEYS`:
|
|
86
|
+
`notEnoughStock`, `itemUnavailable`, `quantityUpdateFailed`, `removeFailed`.
|
|
87
|
+
The first two are for refusals a retry can never clear, so do not collapse
|
|
88
|
+
them into the generic one. Nothing checks that you kept any of this either.
|
|
89
|
+
- `ui/cart/cart-upgrade-banner.tsx` additionally swaps one cart line for
|
|
90
|
+
another, and the ORDER of its two SDK calls is a correctness property, not a
|
|
91
|
+
style choice: it **adds the upgrade first and removes the original second**,
|
|
92
|
+
so a failed add leaves the cart untouched instead of losing the shopper's
|
|
93
|
+
line. It also carries `upgradeAdded`, which stops a retry adding the upgrade
|
|
94
|
+
twice, and a third message, `cart.upgradeOriginalNotRemoved`, for the case
|
|
95
|
+
where the add landed and the removal did not. If you rebuild that component,
|
|
96
|
+
keep all three; the reasoning is in the JSDoc on `handleUpgrade`.
|
|
97
|
+
- `useCartPage()` / `useCart()` → `{ cart, itemCount, totals, refreshCart }`
|
|
98
|
+
|
|
99
|
+
Hooks return data and handlers, never JSX. All catalog content (names, prices,
|
|
100
|
+
images, stock) comes from them — **never hardcode products**.
|
|
101
|
+
|
|
102
|
+
## Verification loop (before you declare anything done)
|
|
103
|
+
|
|
104
|
+
1. **Feature coverage.** Call `get-required-features` on the `brainerce-docs`
|
|
105
|
+
MCP server (wired by the scaffolder in `.mcp.json`, no setup) and confirm
|
|
106
|
+
every mandatory entry is still reachable. First step, not last: you were
|
|
107
|
+
invited to rewrite `src/ui/` from scratch, and a feature that lived only in
|
|
108
|
+
the shipped reference is gone now with nothing to notice it by. `tsc`
|
|
109
|
+
cannot see a missing feature, and these components render nothing while the
|
|
110
|
+
merchant has the feature off, so a deleted one and an idle one look the
|
|
111
|
+
same. `CLAUDE.md` lists the files people usually lose; the MCP tool is the
|
|
112
|
+
actual specification.
|
|
113
|
+
2. `pnpm exec tsc --noEmit` → must be 0.
|
|
114
|
+
3. `pnpm dev` → drive the real flow you changed: home → listing → product →
|
|
115
|
+
add to cart → cart → checkout page renders.
|
|
116
|
+
4. Screenshot desktop (1440) AND mobile (390), in the store's language.
|
|
117
|
+
5. If the store is RTL: check nothing is left-anchored that should be
|
|
118
|
+
right-anchored, and directional arrows point with the reading direction.
|
|
119
|
+
|
|
120
|
+
## The quality bar (distilled from Lovable / v0 / Bolt system prompts)
|
|
121
|
+
|
|
122
|
+
Beautiful is the top priority — the first render must wow. Hard rules:
|
|
123
|
+
|
|
124
|
+
1. **Design system first**: define all HSL semantic tokens in globals.css
|
|
125
|
+
BEFORE styling any component. Never direct colors (`text-white`,
|
|
126
|
+
`bg-black`) in classNames — tokens only.
|
|
127
|
+
2. **Color budget: exactly 3-5 colors.** 1 brand color, 2-3 neutrals, 1-2
|
|
128
|
+
accents. Never purple/violet unless the merchant asked. No gradients by
|
|
129
|
+
default — solid colors; gradients only as subtle accents if requested.
|
|
130
|
+
3. **Max 2 font families** (1 display + 1 body, both with the store's
|
|
131
|
+
script). Body line-height 1.4-1.6. Nothing below 14px.
|
|
132
|
+
4. **Mobile-first**; flexbox by default, grid for true 2D; no absolute
|
|
133
|
+
positioning unless necessary.
|
|
134
|
+
5. **Tailwind discipline**: spacing scale only (`p-4`, never `p-[16px]`),
|
|
135
|
+
`gap-*` over margins between siblings, `text-balance` on headlines.
|
|
136
|
+
6. **Real imagery everywhere** — product photos from the catalog do the
|
|
137
|
+
heavy lifting; free stock hotlinks (Pexels/Unsplash) for editorial
|
|
138
|
+
sections. NEVER abstract filler shapes (gradient circles, blobs).
|
|
139
|
+
NEVER emojis as icons — one icon set, consistent 16/20/24px sizes.
|
|
140
|
+
7. **Content-rich**: no section may render empty or thin. Empty states are
|
|
141
|
+
designed states with copy and structure, not blank space.
|
|
142
|
+
8. Component variants in the design system, not inline overrides.
|
|
143
|
+
9. Semantic HTML + ARIA + alt text + `sr-only` labels — always.
|
|
144
|
+
10. **Verify with your eyes**: screenshot after each major surface, critique
|
|
145
|
+
it ("would a Lovable user accept this?"), fix, and only then move on.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## Motion language (CSS-first)
|
|
149
|
+
|
|
150
|
+
The store should feel alive, premium and intentional — every animation guides
|
|
151
|
+
attention, confirms an action, or rewards exploration. Rules:
|
|
152
|
+
|
|
153
|
+
- **Timing tokens** (use these, never ad-hoc numbers): 80ms instant feedback ·
|
|
154
|
+
180ms hover · 300ms UI state change · 500-600ms section entrance · 900ms
|
|
155
|
+
hero only. Stagger siblings 40-80ms.
|
|
156
|
+
- **Easing**: entrances `cubic-bezier(0.16, 1, 0.3, 1)` (out-expo); springy
|
|
157
|
+
pops `cubic-bezier(0.34, 1.56, 0.64, 1)` (out-back); loops and hovers
|
|
158
|
+
ease-in-out. Exits faster than entrances.
|
|
159
|
+
- **Reveals**: below-the-fold sections animate on viewport entry
|
|
160
|
+
(IntersectionObserver — see `src/ui/shared/reveal.tsx`), never tied to
|
|
161
|
+
scroll distance. One orchestrated reveal per section beats scattered
|
|
162
|
+
effects; `viewport once` — do not re-animate on every scroll.
|
|
163
|
+
- **Micro-interactions**: CSS/WAAPI first (see `src/ui/shared/fly-to-cart.ts`
|
|
164
|
+
for the pattern) — ripple, bump, image zoom, quick-add slide-up are all
|
|
165
|
+
achievable without a library.
|
|
166
|
+
- **Pointer effects** (magnetic buttons, tilt cards, custom cursor, mouse
|
|
167
|
+
parallax): desktop only — gate on `(pointer: fine)` and no-op under
|
|
168
|
+
`prefers-reduced-motion`. `will-change: transform` ONLY on elements animated
|
|
169
|
+
per-frame; never sprinkle it broadly.
|
|
170
|
+
- **Never `transition-all`** — always name the property
|
|
171
|
+
(`transition-transform`, `transition-opacity`). Animate only compositor
|
|
172
|
+
properties (`transform`, `opacity`) — never layout properties
|
|
173
|
+
(width/height/top/margin). 60fps is non-negotiable.
|
|
174
|
+
- **Ambient effects** (gradient blobs, particles): maximum one per page, hero
|
|
175
|
+
only — never on inner pages.
|
|
176
|
+
- **framer-motion / GSAP are not installed.** They are allowed, but they are
|
|
177
|
+
new runtime dependencies — flag it to the user before adding.
|
|
178
|
+
|
|
179
|
+
## Hard-won gotchas (each one cost a real debugging session)
|
|
180
|
+
|
|
181
|
+
- **RTL + cloned images**: `next/image` renders its `<img>` with `inset: 0`.
|
|
182
|
+
If you clone one for an animation and set `left`, the leftover `right: 0`
|
|
183
|
+
wins in RTL documents (over-constrained boxes resolve to the RIGHT).
|
|
184
|
+
Set `inset: 'auto'` before positioning.
|
|
185
|
+
- **Never name a custom class `overline`** — it is a Tailwind utility
|
|
186
|
+
(`text-decoration-line: overline`) and will draw a line above your text.
|
|
187
|
+
- **`rounded` / `rounded-md` / `rounded-lg` all follow `--radius`** (shadcn
|
|
188
|
+
convention, wired in tailwind.config.ts) — change the token, not the
|
|
189
|
+
classes, when you want sharper or softer corners.
|
|
190
|
+
- **Hebrew has no uppercase** — remove `text-transform: uppercase` and wide
|
|
191
|
+
`letter-spacing` from heading styles; they are Latin idioms that distort
|
|
192
|
+
Hebrew (tracking is acceptable only at caption sizes).
|
|
193
|
+
- **Logical properties only**: `ms-/me-`, `ps-/pe-`, `start-/end-`,
|
|
194
|
+
`text-start/end`. Never `ml-/mr-`, `left-/right-`, `text-left/right`,
|
|
195
|
+
`flex-row-reverse`.
|
|
196
|
+
- **Motion**: wrap every animation in a `prefers-reduced-motion: reduce`
|
|
197
|
+
guard. One well-orchestrated reveal beats scattered effects.
|
|
198
|
+
- **Fonts**: any face used for body or headlines must include the store's
|
|
199
|
+
script (e.g. Google Fonts `hebrew` subset). Latin-only display faces are
|
|
200
|
+
accent-only.
|
|
201
|
+
|
|
202
|
+
## Never do
|
|
203
|
+
|
|
204
|
+
- Modify `src/core/`, `src/app/api/`, or checkout/auth/account internals.
|
|
205
|
+
- Remove accessibility attributes (`aria-*`, `role`, `alt`, focus styles).
|
|
206
|
+
- Hardcode catalog content, prices, or currency symbols.
|
|
207
|
+
- Hardcode the store name. `SiteHeader` / `SiteFooter` receive it as a prop
|
|
208
|
+
from the layout; anywhere else use `useStoreName()` (client) or
|
|
209
|
+
`resolveStoreName(storeInfo)` (server) from `src/core/lib/`. The name the
|
|
210
|
+
scaffold knew may be nothing more than the project directory.
|
|
211
|
+
- Swallow the add-to-cart / checkout error states — restyle them, keep them.
|
|
212
|
+
- Drop a `src/ui/` component that is the only place a mandatory feature
|
|
213
|
+
exists. Rebuilding the look is free; losing the SDK call inside it is not,
|
|
214
|
+
and it is silent. See "Rebuilding the look is free" in `CLAUDE.md` for the
|
|
215
|
+
files people usually lose, and `get-required-features` for the real list.
|
|
216
|
+
- Introduce new runtime dependencies without flagging it.
|
|
217
|
+
|
|
218
|
+
## Adding things
|
|
219
|
+
|
|
220
|
+
- New homepage section → new component in `src/ui/home/`, composed in
|
|
221
|
+
`home-client.tsx`, data via existing hooks only.
|
|
222
|
+
- New UI copy → add keys to **both** `messages/he.json` and
|
|
223
|
+
`messages/en.json` (or all supported locales), use `useTranslations()`.
|
|
224
|
+
- New microinteraction → CSS-first; JS only for things CSS cannot do.
|