create-nymbal-app 0.4.0 → 0.5.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 (163) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +7 -4
  3. package/dist/index.js.map +1 -1
  4. package/package.json +5 -4
  5. package/scaffold/project/astro/src/pages/index.astro +13 -15
  6. package/scaffold/templates/astro/_dot.node-version +1 -1
  7. package/scaffold/templates/astro/astro.config.mjs +23 -0
  8. package/scaffold/templates/astro/package.json +26 -26
  9. package/scaffold/templates/astro/src/components/SkipNav.astro +3 -1
  10. package/scaffold/templates/astro/src/elements/index.ts +11 -0
  11. package/scaffold/templates/astro/src/layouts/BaseLayout.astro +15 -4
  12. package/scaffold/templates/astro/src/lib/api.ts +113 -21
  13. package/scaffold/templates/astro/src/lib/chrome.ts +59 -0
  14. package/scaffold/templates/astro/src/lib/client.ts +4 -4
  15. package/scaffold/templates/astro/src/lib/fragments.ts +48 -0
  16. package/scaffold/templates/astro/src/lib/server-elements.ts +5 -0
  17. package/scaffold/templates/astro/src/middleware.ts +37 -0
  18. package/scaffold/templates/astro/src/pages/account/addresses.astro +5 -97
  19. package/scaffold/templates/astro/src/pages/account/login.astro +7 -34
  20. package/scaffold/templates/astro/src/pages/account/profile.astro +6 -56
  21. package/scaffold/templates/astro/src/pages/account/register.astro +7 -73
  22. package/scaffold/templates/astro/src/pages/cart.astro +14 -7
  23. package/scaffold/templates/astro/src/pages/category/[slug].astro +17 -21
  24. package/scaffold/templates/astro/src/pages/checkout.astro +30 -94
  25. package/scaffold/templates/astro/src/pages/fragments/footer.astro +5 -2
  26. package/scaffold/templates/astro/src/pages/fragments/header.astro +12 -2
  27. package/scaffold/templates/astro/src/pages/fragments/nav.astro +8 -5
  28. package/scaffold/templates/astro/src/pages/fragments/product/[slug].astro +9 -10
  29. package/scaffold/templates/astro/src/pages/index.astro +18 -15
  30. package/scaffold/templates/astro/src/pages/order/[orderNumber].astro +22 -44
  31. package/scaffold/templates/astro/src/pages/product/[slug].astro +44 -30
  32. package/scaffold/templates/astro/src/pages/products/index.astro +16 -21
  33. package/scaffold/templates/astro/src/pages/search.astro +10 -11
  34. package/scaffold/templates/astro/src/scripts/init-client.ts +14 -0
  35. package/scaffold/templates/astro/src/scripts/pdp.ts +24 -21
  36. package/scaffold/templates/astro/src/scripts/plp.ts +15 -93
  37. package/scaffold/templates/astro/src/scripts/search.ts +7 -132
  38. package/scaffold/templates/astro/src/scripts/signed-in-redirect.ts +4 -0
  39. package/scaffold/templates/astro/src/styles/global.css +49 -45
  40. package/scaffold/templates/astro/src/styles/pages/account.css +0 -90
  41. package/scaffold/templates/astro/src/styles/pages/confirmation.css +6 -0
  42. package/scaffold/templates/astro/src/styles/pages/home.css +9 -100
  43. package/scaffold/templates/astro/src/styles/pages/pdp.css +5 -171
  44. package/scaffold/templates/astro/src/styles/pages/plp.css +3 -42
  45. package/scaffold/templates/astro/src/styles/pages/search.css +4 -110
  46. package/scaffold/templates/astro/src/styles/tokens.css +56 -0
  47. package/scaffold/templates/nextjs/_dot.node-version +1 -1
  48. package/scaffold/templates/nextjs/app/(catalog)/category/[slug]/page.tsx +74 -0
  49. package/scaffold/templates/nextjs/app/(catalog)/home/[key]/page.tsx +80 -0
  50. package/scaffold/templates/nextjs/app/(catalog)/layout.tsx +13 -0
  51. package/scaffold/templates/nextjs/app/(catalog)/not-found.tsx +7 -0
  52. package/scaffold/templates/nextjs/app/(catalog)/product/[slug]/page.tsx +182 -0
  53. package/scaffold/templates/nextjs/app/(catalog)/products/page.tsx +62 -0
  54. package/scaffold/templates/nextjs/app/(catalog)/search/SearchView.tsx +90 -0
  55. package/scaffold/templates/nextjs/app/(catalog)/search/page.tsx +46 -0
  56. package/scaffold/templates/nextjs/app/(shopper)/account/addresses/AddressesView.tsx +5 -0
  57. package/scaffold/templates/nextjs/app/(shopper)/account/addresses/page.tsx +17 -0
  58. package/scaffold/templates/nextjs/app/(shopper)/account/login/LoginView.tsx +28 -0
  59. package/scaffold/templates/nextjs/app/(shopper)/account/login/page.tsx +23 -0
  60. package/scaffold/templates/nextjs/app/(shopper)/account/profile/ProfileView.tsx +5 -0
  61. package/scaffold/templates/nextjs/app/(shopper)/account/profile/page.tsx +20 -0
  62. package/scaffold/templates/nextjs/app/(shopper)/account/register/RegisterView.tsx +26 -0
  63. package/scaffold/templates/nextjs/app/(shopper)/account/register/page.tsx +11 -0
  64. package/scaffold/templates/nextjs/app/(shopper)/cart/CartView.tsx +5 -0
  65. package/scaffold/templates/nextjs/app/(shopper)/cart/page.tsx +22 -0
  66. package/scaffold/templates/nextjs/app/(shopper)/checkout/CheckoutView.tsx +27 -0
  67. package/scaffold/templates/nextjs/app/(shopper)/checkout/page.tsx +39 -0
  68. package/scaffold/templates/nextjs/app/(shopper)/layout.tsx +12 -0
  69. package/scaffold/templates/nextjs/app/{order → (shopper)/order}/[orderNumber]/page.tsx +24 -5
  70. package/scaffold/templates/nextjs/app/error.tsx +3 -7
  71. package/scaffold/templates/nextjs/app/fragments/footer/route.ts +13 -0
  72. package/scaffold/templates/nextjs/app/fragments/header/[key]/route.ts +18 -0
  73. package/scaffold/templates/nextjs/app/fragments/nav/[key]/route.ts +23 -0
  74. package/scaffold/templates/nextjs/app/fragments/product/[slug]/route.ts +22 -0
  75. package/scaffold/templates/nextjs/app/globals.css +5 -23
  76. package/scaffold/templates/nextjs/app/layout.tsx +8 -5
  77. package/scaffold/templates/nextjs/app/not-found.tsx +9 -33
  78. package/scaffold/templates/nextjs/app/tokens.css +56 -0
  79. package/scaffold/templates/nextjs/components/client/ListingControls.tsx +87 -0
  80. package/scaffold/templates/nextjs/components/client/NymbalSetup.tsx +48 -6
  81. package/scaffold/templates/nextjs/components/client/ProductInteractive.tsx +30 -15
  82. package/scaffold/templates/nextjs/components/client/elements.tsx +15 -0
  83. package/scaffold/templates/nextjs/components/server/Header.tsx +33 -102
  84. package/scaffold/templates/nextjs/components/server/NotFoundView.tsx +39 -0
  85. package/scaffold/templates/nextjs/components/server/SkipNav.tsx +7 -3
  86. package/scaffold/templates/nextjs/elements/index.ts +12 -0
  87. package/scaffold/templates/nextjs/lib/api.ts +151 -55
  88. package/scaffold/templates/nextjs/lib/chrome.ts +40 -0
  89. package/scaffold/templates/nextjs/lib/fragments.ts +16 -0
  90. package/scaffold/templates/nextjs/lib/header.ts +39 -0
  91. package/scaffold/templates/nextjs/lib/render.ts +14 -0
  92. package/scaffold/templates/nextjs/next.config.mjs +32 -0
  93. package/scaffold/templates/nextjs/package.json +28 -27
  94. package/scaffold/templates/nextjs/styles/pages/account.module.css +0 -70
  95. package/scaffold/templates/nextjs/styles/pages/confirmation.module.css +6 -85
  96. package/scaffold/templates/nextjs/styles/pages/home.module.css +2 -103
  97. package/scaffold/templates/nextjs/styles/pages/pdp.module.css +3 -183
  98. package/scaffold/templates/nextjs/styles/pages/plp.module.css +9 -143
  99. package/scaffold/templates/nextjs/styles/pages/search.module.css +1 -145
  100. package/scaffold/templates/nextjs/tsconfig.dist.json +5 -0
  101. package/scaffold/templates/astro/src/components/AddressForm.astro +0 -107
  102. package/scaffold/templates/astro/src/components/Breadcrumb.astro +0 -20
  103. package/scaffold/templates/astro/src/components/CategoryNav.astro +0 -27
  104. package/scaffold/templates/astro/src/components/CheckoutSteps.astro +0 -29
  105. package/scaffold/templates/astro/src/components/Footer.astro +0 -108
  106. package/scaffold/templates/astro/src/components/Header.astro +0 -198
  107. package/scaffold/templates/astro/src/components/ImageGallery.astro +0 -42
  108. package/scaffold/templates/astro/src/components/OrderSummary.astro +0 -53
  109. package/scaffold/templates/astro/src/components/Pagination.astro +0 -13
  110. package/scaffold/templates/astro/src/components/PriceDisplay.astro +0 -25
  111. package/scaffold/templates/astro/src/components/ProductCard.astro +0 -44
  112. package/scaffold/templates/astro/src/components/RelatedProducts.astro +0 -28
  113. package/scaffold/templates/astro/src/components/ReviewForm.astro +0 -74
  114. package/scaffold/templates/astro/src/components/ReviewList.astro +0 -41
  115. package/scaffold/templates/astro/src/components/StockBadge.astro +0 -15
  116. package/scaffold/templates/astro/src/scripts/auth-forms.ts +0 -82
  117. package/scaffold/templates/astro/src/scripts/cart-page.ts +0 -118
  118. package/scaffold/templates/astro/src/scripts/checkout.ts +0 -271
  119. package/scaffold/templates/astro/src/scripts/image-gallery.ts +0 -22
  120. package/scaffold/templates/astro/src/scripts/profile-form.ts +0 -57
  121. package/scaffold/templates/astro/src/scripts/review-form.ts +0 -27
  122. package/scaffold/templates/astro/src/styles/pages/cart.css +0 -215
  123. package/scaffold/templates/astro/src/styles/pages/checkout.css +0 -270
  124. package/scaffold/templates/nextjs/app/account/addresses/page.tsx +0 -10
  125. package/scaffold/templates/nextjs/app/account/login/page.tsx +0 -23
  126. package/scaffold/templates/nextjs/app/account/profile/page.tsx +0 -10
  127. package/scaffold/templates/nextjs/app/account/register/page.tsx +0 -10
  128. package/scaffold/templates/nextjs/app/cart/page.tsx +0 -10
  129. package/scaffold/templates/nextjs/app/category/[slug]/page.tsx +0 -61
  130. package/scaffold/templates/nextjs/app/checkout/page.tsx +0 -17
  131. package/scaffold/templates/nextjs/app/page.tsx +0 -63
  132. package/scaffold/templates/nextjs/app/product/[slug]/page.tsx +0 -111
  133. package/scaffold/templates/nextjs/app/products/page.tsx +0 -40
  134. package/scaffold/templates/nextjs/app/search/page.tsx +0 -24
  135. package/scaffold/templates/nextjs/components/client/AddressManager.tsx +0 -181
  136. package/scaffold/templates/nextjs/components/client/CartPage.tsx +0 -121
  137. package/scaffold/templates/nextjs/components/client/CheckoutForm.tsx +0 -482
  138. package/scaffold/templates/nextjs/components/client/ImageGallery.tsx +0 -68
  139. package/scaffold/templates/nextjs/components/client/LoginForm.tsx +0 -89
  140. package/scaffold/templates/nextjs/components/client/MiniCartWrapper.tsx +0 -12
  141. package/scaffold/templates/nextjs/components/client/ProductGrid.tsx +0 -137
  142. package/scaffold/templates/nextjs/components/client/ProfileForm.tsx +0 -112
  143. package/scaffold/templates/nextjs/components/client/RegisterForm.tsx +0 -131
  144. package/scaffold/templates/nextjs/components/client/ReviewForm.tsx +0 -116
  145. package/scaffold/templates/nextjs/components/client/SearchBarWrapper.tsx +0 -27
  146. package/scaffold/templates/nextjs/components/client/SearchResults.tsx +0 -181
  147. package/scaffold/templates/nextjs/components/server/Breadcrumb.tsx +0 -36
  148. package/scaffold/templates/nextjs/components/server/CategoryNav.tsx +0 -29
  149. package/scaffold/templates/nextjs/components/server/CheckoutSteps.tsx +0 -60
  150. package/scaffold/templates/nextjs/components/server/Footer.module.css +0 -64
  151. package/scaffold/templates/nextjs/components/server/Footer.tsx +0 -79
  152. package/scaffold/templates/nextjs/components/server/Header.module.css +0 -147
  153. package/scaffold/templates/nextjs/components/server/OrderSummary.tsx +0 -79
  154. package/scaffold/templates/nextjs/components/server/PriceDisplay.tsx +0 -31
  155. package/scaffold/templates/nextjs/components/server/ProductCard.tsx +0 -59
  156. package/scaffold/templates/nextjs/components/server/RelatedProducts.tsx +0 -31
  157. package/scaffold/templates/nextjs/components/server/ReviewList.tsx +0 -62
  158. package/scaffold/templates/nextjs/styles/pages/cart.module.css +0 -240
  159. package/scaffold/templates/nextjs/styles/pages/checkout.module.css +0 -404
  160. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/layout.tsx +0 -0
  161. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/orders/[orderNumber]/page.tsx +0 -0
  162. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/orders/page.tsx +0 -0
  163. /package/scaffold/templates/nextjs/app/{account → (shopper)/account}/page.tsx +0 -0
@@ -12,7 +12,12 @@
12
12
  text-align: center;
13
13
  padding: 4rem var(--nymbal-spacing-md, 1rem);
14
14
  min-height: 420px;
15
- background: linear-gradient(135deg, var(--nymbal-color-primary, #18181b) 0%, #27272a 50%, var(--nymbal-color-accent, #2563eb) 100%);
15
+ background: linear-gradient(
16
+ 135deg,
17
+ var(--nymbal-color-primary, #18181b) 0%,
18
+ #27272a 50%,
19
+ var(--nymbal-color-accent, #2563eb) 100%
20
+ );
16
21
  color: var(--nymbal-color-text-inverse, #fff);
17
22
  overflow: hidden;
18
23
  }
@@ -95,55 +100,6 @@
95
100
  border-color: rgba(255, 255, 255, 0.5);
96
101
  }
97
102
 
98
- /* ---- Category grid ---- */
99
-
100
- .category-grid {
101
- display: grid;
102
- grid-template-columns: repeat(2, 1fr);
103
- gap: var(--nymbal-spacing-md, 1rem);
104
- }
105
-
106
- @media (min-width: 641px) {
107
- .category-grid {
108
- grid-template-columns: repeat(4, 1fr);
109
- gap: var(--nymbal-spacing-lg, 1.5rem);
110
- }
111
- }
112
-
113
- .category-card {
114
- display: flex;
115
- flex-direction: column;
116
- align-items: center;
117
- justify-content: center;
118
- text-align: center;
119
- padding: var(--nymbal-spacing-lg, 1.5rem) var(--nymbal-spacing-md, 1rem);
120
- background-color: var(--nymbal-color-surface, #fff);
121
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
122
- border-radius: var(--nymbal-radius-lg, 0.75rem);
123
- text-decoration: none;
124
- transition: border-color var(--nymbal-transition-fast, 150ms) ease,
125
- box-shadow var(--nymbal-transition-fast, 150ms) ease,
126
- transform var(--nymbal-transition-fast, 150ms) ease;
127
- min-height: 140px;
128
- }
129
-
130
- .category-card:hover {
131
- border-color: var(--nymbal-color-accent, #2563eb);
132
- box-shadow: var(--nymbal-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
133
- transform: translateY(-2px);
134
- }
135
-
136
- .category-card-icon {
137
- font-size: 2rem;
138
- margin-bottom: var(--nymbal-spacing-sm, 0.5rem);
139
- }
140
-
141
- .category-card-name {
142
- font-size: var(--nymbal-font-size-base, 1rem);
143
- font-weight: var(--nymbal-font-weight-medium, 500);
144
- color: var(--nymbal-color-text, #18181b);
145
- }
146
-
147
103
  /* ---- Featured products ---- */
148
104
 
149
105
  .featured-section {
@@ -170,53 +126,6 @@
170
126
  text-decoration: underline;
171
127
  }
172
128
 
173
- /* ---- Product card (shared home / PLP) ---- */
174
-
175
- .product-card {
176
- display: flex;
177
- flex-direction: column;
178
- background-color: var(--nymbal-color-surface, #fff);
179
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
180
- border-radius: var(--nymbal-radius-lg, 0.75rem);
181
- overflow: hidden;
182
- text-decoration: none;
183
- transition: box-shadow var(--nymbal-transition-normal, 200ms) ease,
184
- transform var(--nymbal-transition-fast, 150ms) ease;
185
- }
186
-
187
- .product-card:hover {
188
- box-shadow: var(--nymbal-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
189
- transform: translateY(-2px);
190
- }
191
-
192
- .product-card-image {
193
- aspect-ratio: 1 / 1;
194
- width: 100%;
195
- object-fit: cover;
196
- background-color: #f4f4f5;
197
- }
198
-
199
- .product-card-body {
200
- padding: var(--nymbal-spacing-md, 1rem);
201
- flex: 1;
202
- display: flex;
203
- flex-direction: column;
204
- gap: var(--nymbal-spacing-xs, 0.25rem);
205
- }
206
-
207
- .product-card-name {
208
- font-size: var(--nymbal-font-size-sm, 0.875rem);
209
- font-weight: var(--nymbal-font-weight-medium, 500);
210
- color: var(--nymbal-color-text, #18181b);
211
- line-height: 1.3;
212
- }
213
-
214
- .product-card-price {
215
- font-size: var(--nymbal-font-size-sm, 0.875rem);
216
- font-weight: var(--nymbal-font-weight-bold, 700);
217
- color: var(--nymbal-color-primary, #18181b);
218
- }
219
-
220
129
  /* ---- Newsletter section ---- */
221
130
 
222
131
  .newsletter {
@@ -247,7 +156,7 @@
247
156
  justify-content: center;
248
157
  }
249
158
 
250
- .newsletter-form input[type="email"] {
159
+ .newsletter-form input[type='email'] {
251
160
  flex: 1;
252
161
  min-width: 200px;
253
162
  border-color: rgba(255, 255, 255, 0.2);
@@ -255,11 +164,11 @@
255
164
  color: var(--nymbal-color-text-inverse, #fff);
256
165
  }
257
166
 
258
- .newsletter-form input[type="email"]::placeholder {
167
+ .newsletter-form input[type='email']::placeholder {
259
168
  color: rgba(255, 255, 255, 0.5);
260
169
  }
261
170
 
262
- .newsletter-form input[type="email"]:focus {
171
+ .newsletter-form input[type='email']:focus {
263
172
  border-color: var(--nymbal-color-accent, #2563eb);
264
173
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
265
174
  }
@@ -24,38 +24,7 @@
24
24
  }
25
25
  }
26
26
 
27
- /* ---- Breadcrumbs ---- */
28
-
29
- .pdp-breadcrumbs {
30
- display: flex;
31
- align-items: center;
32
- gap: var(--nymbal-spacing-xs, 0.25rem);
33
- font-size: var(--nymbal-font-size-xs, 0.75rem);
34
- color: var(--nymbal-color-text-muted, #71717a);
35
- margin-bottom: var(--nymbal-spacing-md, 1rem);
36
- flex-wrap: wrap;
37
- }
38
-
39
- .pdp-breadcrumbs a {
40
- color: var(--nymbal-color-text-muted, #71717a);
41
- text-decoration: none;
42
- transition: color var(--nymbal-transition-fast, 150ms) ease;
43
- }
44
-
45
- .pdp-breadcrumbs a:hover {
46
- color: var(--nymbal-color-accent, #2563eb);
47
- }
48
-
49
- .pdp-breadcrumbs .separator {
50
- color: var(--nymbal-color-border, #e4e4e7);
51
- }
52
-
53
- .pdp-breadcrumbs .current {
54
- color: var(--nymbal-color-text, #18181b);
55
- font-weight: var(--nymbal-font-weight-medium, 500);
56
- }
57
-
58
- /* ---- Image gallery ---- */
27
+ /* ---- Image gallery (nymbal-image-gallery) ---- */
59
28
 
60
29
  .pdp-gallery {
61
30
  position: sticky;
@@ -63,50 +32,6 @@
63
32
  align-self: start;
64
33
  }
65
34
 
66
- .pdp-main-image {
67
- width: 100%;
68
- aspect-ratio: 1 / 1;
69
- object-fit: cover;
70
- border-radius: var(--nymbal-radius-lg, 0.75rem);
71
- background-color: #f4f4f5;
72
- margin-bottom: var(--nymbal-spacing-sm, 0.5rem);
73
- }
74
-
75
- .pdp-thumbnails {
76
- display: flex;
77
- gap: var(--nymbal-spacing-sm, 0.5rem);
78
- overflow-x: auto;
79
- scrollbar-width: thin;
80
- padding-bottom: var(--nymbal-spacing-xs, 0.25rem);
81
- }
82
-
83
- .pdp-thumbnail {
84
- width: 64px;
85
- height: 64px;
86
- object-fit: cover;
87
- border-radius: var(--nymbal-radius-md, 0.5rem);
88
- border: 2px solid transparent;
89
- cursor: pointer;
90
- flex-shrink: 0;
91
- background-color: #f4f4f5;
92
- transition: border-color var(--nymbal-transition-fast, 150ms) ease;
93
- }
94
-
95
- .pdp-thumbnail:hover {
96
- border-color: var(--nymbal-color-border, #e4e4e7);
97
- }
98
-
99
- .pdp-thumbnail.active {
100
- border-color: var(--nymbal-color-primary, #18181b);
101
- }
102
-
103
- @media (min-width: 641px) {
104
- .pdp-thumbnail {
105
- width: 80px;
106
- height: 80px;
107
- }
108
- }
109
-
110
35
  /* ---- Product info ---- */
111
36
 
112
37
  .pdp-info {
@@ -128,46 +53,6 @@
128
53
  }
129
54
  }
130
55
 
131
- .pdp-price {
132
- font-size: 1.5rem;
133
- font-weight: var(--nymbal-font-weight-bold, 700);
134
- color: var(--nymbal-color-primary, #18181b);
135
- }
136
-
137
- .pdp-price-range {
138
- font-size: var(--nymbal-font-size-sm, 0.875rem);
139
- color: var(--nymbal-color-text-muted, #71717a);
140
- font-weight: var(--nymbal-font-weight-normal, 400);
141
- margin-left: var(--nymbal-spacing-xs, 0.25rem);
142
- }
143
-
144
- .pdp-stock {
145
- display: inline-flex;
146
- align-items: center;
147
- gap: var(--nymbal-spacing-xs, 0.25rem);
148
- font-size: var(--nymbal-font-size-sm, 0.875rem);
149
- font-weight: var(--nymbal-font-weight-medium, 500);
150
- }
151
-
152
- .pdp-stock[data-level='in'] {
153
- color: var(--nymbal-color-success, #16a34a);
154
- }
155
-
156
- .pdp-stock[data-level='low'] {
157
- color: var(--nymbal-color-warning, #d97706);
158
- }
159
-
160
- .pdp-stock[data-level='out'] {
161
- color: var(--nymbal-color-error, #dc2626);
162
- }
163
-
164
- .pdp-stock-dot {
165
- width: 8px;
166
- height: 8px;
167
- border-radius: var(--nymbal-radius-full, 9999px);
168
- background-color: currentColor;
169
- }
170
-
171
56
  .pdp-description {
172
57
  font-size: var(--nymbal-font-size-sm, 0.875rem);
173
58
  color: var(--nymbal-color-text-muted, #71717a);
@@ -218,59 +103,8 @@ nymbal-add-to-cart {
218
103
  margin-bottom: var(--nymbal-spacing-lg, 1.5rem);
219
104
  }
220
105
 
221
- .review-list {
222
- display: flex;
223
- flex-direction: column;
224
- gap: var(--nymbal-spacing-lg, 1.5rem);
225
- }
226
-
227
- .review-item {
228
- padding-bottom: var(--nymbal-spacing-lg, 1.5rem);
229
- border-bottom: 1px solid var(--nymbal-color-border, #e4e4e7);
230
- }
231
-
232
- .review-item:last-child {
233
- border-bottom: none;
234
- padding-bottom: 0;
235
- }
236
-
237
- .review-header {
238
- display: flex;
239
- align-items: center;
240
- justify-content: space-between;
241
- gap: var(--nymbal-spacing-sm, 0.5rem);
242
- margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
243
- flex-wrap: wrap;
244
- }
245
-
246
- .review-rating {
247
- display: flex;
248
- gap: 2px;
249
- color: var(--nymbal-color-warning, #d97706);
250
- font-size: var(--nymbal-font-size-sm, 0.875rem);
251
- }
252
-
253
- .review-date {
254
- font-size: var(--nymbal-font-size-xs, 0.75rem);
255
- color: var(--nymbal-color-text-muted, #71717a);
256
- }
257
-
258
- .review-title {
259
- font-size: var(--nymbal-font-size-base, 1rem);
260
- font-weight: var(--nymbal-font-weight-medium, 500);
261
- color: var(--nymbal-color-text, #18181b);
262
- margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
263
- }
264
-
265
- .review-body {
266
- font-size: var(--nymbal-font-size-sm, 0.875rem);
267
- color: var(--nymbal-color-text-muted, #71717a);
268
- line-height: 1.6;
269
- }
270
-
271
- .reviews-empty {
272
- text-align: center;
273
- padding: var(--nymbal-spacing-xl, 2rem);
274
- color: var(--nymbal-color-text-muted, #71717a);
275
- font-size: var(--nymbal-font-size-sm, 0.875rem);
106
+ .pdp-review-form {
107
+ margin-top: var(--nymbal-spacing-xl, 2rem);
108
+ padding-top: var(--nymbal-spacing-xl, 2rem);
109
+ border-top: 1px solid var(--nymbal-color-border, #e4e4e7);
276
110
  }
@@ -30,11 +30,6 @@
30
30
  margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
31
31
  }
32
32
 
33
- .plp-count {
34
- font-size: var(--nymbal-font-size-sm, 0.875rem);
35
- color: var(--nymbal-color-text-muted, #71717a);
36
- }
37
-
38
33
  /* ---- Filter sidebar ---- */
39
34
 
40
35
  .plp-sidebar {
@@ -91,7 +86,7 @@
91
86
  margin-bottom: 0;
92
87
  }
93
88
 
94
- .filter-item input[type="checkbox"] {
89
+ .filter-item input[type='checkbox'] {
95
90
  width: 16px;
96
91
  height: 16px;
97
92
  accent-color: var(--nymbal-color-accent, #2563eb);
@@ -108,11 +103,11 @@
108
103
  }
109
104
 
110
105
  .filter-link:hover,
111
- .filter-link[aria-current="page"] {
106
+ .filter-link[aria-current='page'] {
112
107
  color: var(--nymbal-color-accent, #2563eb);
113
108
  }
114
109
 
115
- .filter-link[aria-current="page"] {
110
+ .filter-link[aria-current='page'] {
116
111
  font-weight: var(--nymbal-font-weight-bold, 700);
117
112
  }
118
113
 
@@ -171,40 +166,6 @@
171
166
  min-width: 0;
172
167
  }
173
168
 
174
- /* ---- Pagination ---- */
175
-
176
- .plp-pagination {
177
- display: flex;
178
- justify-content: center;
179
- gap: var(--nymbal-spacing-sm, 0.5rem);
180
- margin-top: var(--nymbal-spacing-xl, 2rem);
181
- padding-top: var(--nymbal-spacing-lg, 1.5rem);
182
- border-top: 1px solid var(--nymbal-color-border, #e4e4e7);
183
- }
184
-
185
- .plp-pagination .btn {
186
- min-width: 100px;
187
- }
188
-
189
- /* ---- Empty state ---- */
190
-
191
- .plp-empty {
192
- text-align: center;
193
- padding: 4rem var(--nymbal-spacing-md, 1rem);
194
- color: var(--nymbal-color-text-muted, #71717a);
195
- }
196
-
197
- .plp-empty-title {
198
- font-size: var(--nymbal-font-size-lg, 1.125rem);
199
- font-weight: var(--nymbal-font-weight-medium, 500);
200
- color: var(--nymbal-color-text, #18181b);
201
- margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
202
- }
203
-
204
- .plp-empty-text {
205
- font-size: var(--nymbal-font-size-sm, 0.875rem);
206
- }
207
-
208
169
  /* ---- nymbal-product-filter integration ---- */
209
170
 
210
171
  nymbal-product-filter {
@@ -52,120 +52,14 @@
52
52
  gap: var(--nymbal-spacing-lg, 1.5rem);
53
53
  }
54
54
 
55
- /* The filter renders nothing when the search has no facets: the results take the width. */
55
+ /* The filter carries `empty` when the search has no facets: the results take the width. The page serves it with
56
+ `empty`, so no sidebar column is drawn before the element runs. */
56
57
  @media (min-width: 769px) {
57
- .search-layout:has(nymbal-product-filter:not(:empty)) {
58
+ .search-layout:has(nymbal-product-filter:not([empty])) {
58
59
  grid-template-columns: 240px minmax(0, 1fr);
59
60
  }
60
61
  }
61
62
 
62
- .search-sidebar:has(nymbal-product-filter:empty) {
63
+ .search-sidebar:has(nymbal-product-filter[empty]) {
63
64
  display: none;
64
65
  }
65
-
66
- /* ---- Results area ---- */
67
-
68
- .search-results {
69
- padding-top: var(--nymbal-spacing-lg, 1.5rem);
70
- }
71
-
72
- .search-results-header {
73
- display: flex;
74
- align-items: center;
75
- justify-content: space-between;
76
- margin-bottom: var(--nymbal-spacing-md, 1rem);
77
- flex-wrap: wrap;
78
- gap: var(--nymbal-spacing-sm, 0.5rem);
79
- }
80
-
81
- .search-query-label {
82
- font-size: var(--nymbal-font-size-sm, 0.875rem);
83
- color: var(--nymbal-color-text-muted, #71717a);
84
- }
85
-
86
- .search-query-label strong {
87
- color: var(--nymbal-color-text, #18181b);
88
- font-weight: var(--nymbal-font-weight-bold, 700);
89
- }
90
-
91
- .search-result-count {
92
- font-size: var(--nymbal-font-size-sm, 0.875rem);
93
- color: var(--nymbal-color-text-muted, #71717a);
94
- }
95
-
96
- /* ---- Results grid (uses .product-grid from global) ---- */
97
-
98
- /* ---- Empty / no results ---- */
99
-
100
- .search-empty {
101
- text-align: center;
102
- padding: 4rem var(--nymbal-spacing-md, 1rem);
103
- }
104
-
105
- .search-empty-icon {
106
- font-size: 3rem;
107
- margin-bottom: var(--nymbal-spacing-md, 1rem);
108
- opacity: 0.3;
109
- }
110
-
111
- .search-empty-title {
112
- font-size: var(--nymbal-font-size-lg, 1.125rem);
113
- font-weight: var(--nymbal-font-weight-medium, 500);
114
- color: var(--nymbal-color-text, #18181b);
115
- margin-bottom: var(--nymbal-spacing-xs, 0.25rem);
116
- }
117
-
118
- .search-empty-text {
119
- font-size: var(--nymbal-font-size-sm, 0.875rem);
120
- color: var(--nymbal-color-text-muted, #71717a);
121
- max-width: 28ch;
122
- margin-inline: auto;
123
- }
124
-
125
- /* ---- Suggestions ---- */
126
-
127
- .search-suggestions {
128
- margin-top: var(--nymbal-spacing-lg, 1.5rem);
129
- }
130
-
131
- .search-suggestions-title {
132
- font-size: var(--nymbal-font-size-sm, 0.875rem);
133
- font-weight: var(--nymbal-font-weight-medium, 500);
134
- color: var(--nymbal-color-text, #18181b);
135
- margin-bottom: var(--nymbal-spacing-sm, 0.5rem);
136
- }
137
-
138
- .search-suggestions-list {
139
- display: flex;
140
- flex-wrap: wrap;
141
- gap: var(--nymbal-spacing-sm, 0.5rem);
142
- }
143
-
144
- .search-suggestion {
145
- display: inline-flex;
146
- padding: var(--nymbal-spacing-xs, 0.25rem) var(--nymbal-spacing-md, 1rem);
147
- font-size: var(--nymbal-font-size-sm, 0.875rem);
148
- color: var(--nymbal-color-text, #18181b);
149
- background-color: var(--nymbal-color-surface, #fff);
150
- border: 1px solid var(--nymbal-color-border, #e4e4e7);
151
- border-radius: var(--nymbal-radius-full, 9999px);
152
- text-decoration: none;
153
- transition: border-color var(--nymbal-transition-fast, 150ms) ease,
154
- background-color var(--nymbal-transition-fast, 150ms) ease;
155
- }
156
-
157
- .search-suggestion:hover {
158
- border-color: var(--nymbal-color-accent, #2563eb);
159
- background-color: rgba(37, 99, 235, 0.04);
160
- }
161
-
162
- /* ---- Loading ---- */
163
-
164
- .search-loading {
165
- display: flex;
166
- align-items: center;
167
- justify-content: center;
168
- padding: 3rem;
169
- color: var(--nymbal-color-text-muted, #71717a);
170
- font-size: var(--nymbal-font-size-sm, 0.875rem);
171
- }
@@ -0,0 +1,56 @@
1
+ /* ----------------------------------------------------------------
2
+ Design tokens: this store's file, edit it to restyle.
3
+ Every Nymbal element reads its look from these custom properties,
4
+ which cross into its shadow root. An element's `part`s can be
5
+ styled further with ::part(), e.g. nymbal-add-to-cart::part(button).
6
+ Defaults: @nymbalhq/web-components/tokens.css.
7
+ ---------------------------------------------------------------- */
8
+
9
+ :root {
10
+ --nymbal-color-primary: #18181b;
11
+ --nymbal-color-primary-hover: #27272a;
12
+ --nymbal-color-accent: #2563eb;
13
+ --nymbal-color-accent-hover: #1d4ed8;
14
+ --nymbal-color-success: #16a34a;
15
+ --nymbal-color-error: #dc2626;
16
+ --nymbal-color-warning: #d97706;
17
+ --nymbal-color-text: #18181b;
18
+ --nymbal-color-text-muted: #71717a;
19
+ --nymbal-color-text-inverse: #ffffff;
20
+ --nymbal-color-border: #e4e4e7;
21
+ --nymbal-color-surface: #ffffff;
22
+ --nymbal-color-overlay: rgba(0, 0, 0, 0.4);
23
+
24
+ --nymbal-font-family: system-ui, -apple-system, sans-serif;
25
+ --nymbal-font-size-xs: 0.75rem;
26
+ --nymbal-font-size-sm: 0.875rem;
27
+ --nymbal-font-size-base: 1rem;
28
+ --nymbal-font-size-lg: 1.125rem;
29
+ --nymbal-font-size-xl: 1.25rem;
30
+ --nymbal-font-weight-normal: 400;
31
+ --nymbal-font-weight-medium: 500;
32
+ --nymbal-font-weight-bold: 700;
33
+
34
+ --nymbal-spacing-xs: 0.25rem;
35
+ --nymbal-spacing-sm: 0.5rem;
36
+ --nymbal-spacing-md: 1rem;
37
+ --nymbal-spacing-lg: 1.5rem;
38
+ --nymbal-spacing-xl: 2rem;
39
+
40
+ --nymbal-radius-sm: 0.25rem;
41
+ --nymbal-radius-md: 0.375rem;
42
+ --nymbal-radius-lg: 0.5rem;
43
+ --nymbal-radius-full: 9999px;
44
+
45
+ --nymbal-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
46
+ --nymbal-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
47
+ --nymbal-shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
48
+
49
+ --nymbal-transition-fast: 150ms ease;
50
+ --nymbal-transition-normal: 200ms ease;
51
+ --nymbal-transition-slow: 300ms ease;
52
+
53
+ --nymbal-z-dropdown: 100;
54
+ --nymbal-z-drawer: 200;
55
+ --nymbal-z-toast: 300;
56
+ }
@@ -1 +1 @@
1
- 22.23.3
1
+ 26.12.1
@@ -0,0 +1,74 @@
1
+ import type { Metadata } from 'next'
2
+ import { notFound } from 'next/navigation'
3
+ import { fetchProducts, fetchCategories, fetchCapabilities, sortFromSearchParams } from '@/lib/api'
4
+ import { cardImageOf, cardProductOf, renderNymbal } from '@/lib/render'
5
+ import { Breadcrumb, ProductGrid } from '@/components/client/elements'
6
+ import { ListingControls } from '@/components/client/ListingControls'
7
+ import styles from '@/styles/pages/plp.module.css'
8
+
9
+ interface CategoryPageProps {
10
+ params: Promise<{ slug: string }>
11
+ searchParams: Promise<{ sort?: string | string[] }>
12
+ }
13
+
14
+ export async function generateMetadata({ params }: CategoryPageProps): Promise<Metadata> {
15
+ const { slug } = await params
16
+ const categories = await fetchCategories()
17
+ const category = categories.find((c) => c.slug === slug)
18
+
19
+ return {
20
+ title: category?.name ?? slug,
21
+ description: category?.description ?? `Browse products in ${slug}.`,
22
+ }
23
+ }
24
+
25
+ export default async function CategoryPage({ params, searchParams }: CategoryPageProps) {
26
+ const { slug } = await params
27
+ const capabilities = await fetchCapabilities()
28
+ const sort = sortFromSearchParams(await searchParams, capabilities?.catalog)
29
+ const [{ items, nextCursor, facets }, categories] = await Promise.all([
30
+ fetchProducts({ limit: 24, category: slug, sort }),
31
+ fetchCategories(),
32
+ ])
33
+
34
+ const category = categories.find((c) => c.slug === slug)
35
+ if (!category) notFound()
36
+ const crumbs = [{ label: 'Home', href: '/' }, { label: category.name }]
37
+ const cards = items.map(cardProductOf)
38
+ // The first card's image is the page's LCP, and the preload scanner never reaches it inside the grid's shadow root:
39
+ // a preload link, which React hoists into the head, starts it.
40
+ const firstImage = cards[0] === undefined ? null : cardImageOf(cards[0]).url
41
+ const grid = {
42
+ products: JSON.stringify(cards),
43
+ source: 'product-list',
44
+ ...(nextCursor !== null && { 'has-more': '' }),
45
+ }
46
+
47
+ return (
48
+ <div className={`container ${styles.page}`}>
49
+ {firstImage !== null && (
50
+ <link rel="preload" as="image" href={firstImage} fetchPriority="high" />
51
+ )}
52
+ <Breadcrumb
53
+ items={crumbs}
54
+ serverHtml={renderNymbal('nymbal-breadcrumb', { items: JSON.stringify(crumbs) }).__html}
55
+ />
56
+ <h1 className={styles.pageTitle}>{category.name}</h1>
57
+ {category.description && <p className="text-muted mb-lg">{category.description}</p>}
58
+ <ListingControls
59
+ sort={sort}
60
+ category={slug}
61
+ facets={facets ?? []}
62
+ sortKeys={capabilities?.catalog.sortKeys ?? []}
63
+ defaultSort={capabilities?.catalog.defaultSort}
64
+ >
65
+ <ProductGrid
66
+ products={cards}
67
+ source="product-list"
68
+ hasMore={nextCursor !== null}
69
+ serverHtml={renderNymbal('nymbal-product-grid', grid).__html}
70
+ />
71
+ </ListingControls>
72
+ </div>
73
+ )
74
+ }