@stackonward/cli 1.5.3 → 1.6.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.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +112 -56
- package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/android.svg +10 -0
- package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/apple.svg +3 -0
- package/dist/resources/components/nuxt/site-source/app/assets/icons/brand/whatsapp.svg +5 -0
- package/dist/resources/components/nuxt/site-source/app/components/app/Sidebar.vue +1 -1
- package/dist/resources/components/nuxt/site-source/app/components/checkout/Status.vue +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/consent/Banner.vue +23 -8
- package/dist/resources/components/nuxt/site-source/app/components/content/CallToAction.vue +59 -0
- package/dist/resources/components/nuxt/site-source/app/components/content/Card.vue +35 -0
- package/dist/resources/components/nuxt/site-source/app/components/content/Compatibility.vue +10 -0
- package/dist/resources/components/nuxt/site-source/app/components/content/Faq.vue +56 -0
- package/dist/resources/components/nuxt/site-source/app/components/content/Features.vue +8 -0
- package/dist/resources/components/nuxt/site-source/app/components/content/List.vue +1 -8
- package/dist/resources/components/nuxt/site-source/app/components/content/Reviews.vue +93 -0
- package/dist/resources/components/nuxt/site-source/app/components/countdown/Timer.vue +112 -0
- package/dist/resources/components/nuxt/site-source/app/components/locale/Menu.vue +3 -3
- package/dist/resources/components/nuxt/site-source/app/components/site/BackToTop.vue +30 -23
- package/dist/resources/components/nuxt/site-source/app/components/site/Header.vue +2 -2
- package/dist/resources/components/nuxt/site-source/app/components/steps/Progress.vue +36 -0
- package/dist/resources/components/nuxt/site-source/app/components/steps/Showcase.vue +340 -0
- package/dist/resources/components/nuxt/site-source/app/components/theme/Menu.vue +4 -4
- package/dist/resources/components/nuxt/site-source/app/components/ui/Button.vue +28 -2
- package/dist/resources/components/nuxt/site-source/app/components/ui/GradientBorder.vue +5 -5
- package/dist/resources/components/nuxt/site-source/app/components/ui/Icon.vue +14 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/Image.vue +126 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/Marquee.vue +99 -0
- package/dist/resources/components/nuxt/site-source/app/components/ui/Modal.vue +26 -88
- package/dist/resources/components/nuxt/site-source/app/components/ui/Popover.vue +87 -93
- package/dist/resources/components/nuxt/site-source/app/components/ui/Text.vue +84 -12
- package/dist/resources/components/nuxt/site-source/app/components/ui/Video.vue +105 -0
- package/dist/resources/components/nuxt/site-source/app/components/whatsapp/Chat.vue +126 -0
- package/dist/resources/components/nuxt/site-source/app/components/youtube/Embed.vue +286 -73
- package/dist/resources/components/nuxt/site-source/app/composables/useFloatingBottomStack.ts +44 -0
- package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +6 -8
- package/dist/resources/components/nuxt/site-source/app/consent/types.ts +1 -4
- package/dist/resources/components/nuxt/site-source/app/content/types.ts +3 -0
- package/dist/resources/components/nuxt/site-source/app/theme/accordion.ts +5 -4
- package/dist/resources/components/nuxt/site-source/app/theme/breadcrumb.ts +14 -4
- package/dist/resources/components/nuxt/site-source/app/theme/button.ts +138 -28
- package/dist/resources/components/nuxt/site-source/app/theme/carousel.ts +11 -7
- package/dist/resources/components/nuxt/site-source/app/theme/checkbox.ts +9 -3
- package/dist/resources/components/nuxt/site-source/app/theme/drawer.ts +6 -0
- package/dist/resources/components/nuxt/site-source/app/theme/form-field.ts +8 -0
- package/dist/resources/components/nuxt/site-source/app/theme/icon.ts +6 -0
- package/dist/resources/components/nuxt/site-source/app/theme/index.ts +8 -1
- package/dist/resources/components/nuxt/site-source/app/theme/input.ts +34 -18
- package/dist/resources/components/nuxt/site-source/app/theme/modal.ts +9 -5
- package/dist/resources/components/nuxt/site-source/app/theme/pagination.ts +9 -2
- package/dist/resources/components/nuxt/site-source/app/theme/radio-group.ts +7 -0
- package/dist/resources/components/nuxt/site-source/app/theme/select.ts +19 -7
- package/dist/resources/components/nuxt/site-source/app/theme/stepper.ts +15 -6
- package/dist/resources/components/nuxt/site-source/app/theme/textarea.ts +6 -0
- package/dist/resources/components/nuxt/site-source/app/theme/toast.ts +1 -4
- package/dist/resources/components/nuxt/site-source/app/theme/tooltip.ts +5 -1
- package/dist/resources/components/registry.json +133 -45
- package/dist/resources/components/vue/ui/BottomSheet.vue +5 -1
- package/dist/resources/components/vue/ui/ScrollArea.vue +2 -1
- package/dist/resources/registry.json +1 -1
- package/dist/resources/templates/base/.claude/docs/architecture-template.md +2 -1
- package/dist/resources/templates/base/AGENTS.md +7 -0
- package/dist/resources/templates/base/CLAUDE.md +7 -0
- package/dist/resources/templates/nuxt/.claude/docs/stack.md +14 -1
- package/dist/resources/templates/nuxt/DESIGN.md +146 -0
- package/dist/resources/templates/nuxt/app/app.config.ts +2 -0
- package/dist/resources/templates/nuxt/app/pages/app/index.vue +9 -7
- package/dist/resources/templates/nuxt/app/pages/index.vue +12 -27
- package/dist/resources/templates/nuxt/app/pages/ui/index.vue +219 -3
- package/dist/resources/templates/nuxt/nuxt.config.ts +2 -4
- package/dist/resources/templates/nuxt/package.json +2 -1
- package/dist/resources/templates/nuxt/stylelint.config.mjs +12 -0
- package/package.json +3 -3
- package/dist/resources/components/nuxt/site-source/app/assets/icons/common/.gitkeep +0 -0
|
@@ -160,8 +160,16 @@
|
|
|
160
160
|
"target": "app/assets/icons/brand/.gitkeep"
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
|
-
"path": "site-source/app/assets/icons/
|
|
164
|
-
"target": "app/assets/icons/
|
|
163
|
+
"path": "site-source/app/assets/icons/brand/android.svg",
|
|
164
|
+
"target": "app/assets/icons/brand/android.svg"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"path": "site-source/app/assets/icons/brand/apple.svg",
|
|
168
|
+
"target": "app/assets/icons/brand/apple.svg"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"path": "site-source/app/assets/icons/brand/whatsapp.svg",
|
|
172
|
+
"target": "app/assets/icons/brand/whatsapp.svg"
|
|
165
173
|
},
|
|
166
174
|
{
|
|
167
175
|
"path": "site-source/app/components/app/Header.vue",
|
|
@@ -176,29 +184,81 @@
|
|
|
176
184
|
"target": "app/components/app/Sidebar.vue"
|
|
177
185
|
},
|
|
178
186
|
{
|
|
179
|
-
"path": "site-source/app/components/
|
|
180
|
-
"target": "app/components/
|
|
187
|
+
"path": "site-source/app/components/checkout/Status.vue",
|
|
188
|
+
"target": "app/components/checkout/Status.vue"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"path": "site-source/app/components/checkout/Summary.vue",
|
|
192
|
+
"target": "app/components/checkout/Summary.vue"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"path": "site-source/app/components/consent/Banner.vue",
|
|
196
|
+
"target": "app/components/consent/Banner.vue"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"path": "site-source/app/components/consent/Settings.vue",
|
|
200
|
+
"target": "app/components/consent/Settings.vue"
|
|
181
201
|
},
|
|
182
202
|
{
|
|
183
203
|
"path": "site-source/app/components/content/Article.vue",
|
|
184
204
|
"target": "app/components/content/Article.vue"
|
|
185
205
|
},
|
|
206
|
+
{
|
|
207
|
+
"path": "site-source/app/components/content/CallToAction.vue",
|
|
208
|
+
"target": "app/components/content/CallToAction.vue"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"path": "site-source/app/components/content/Card.vue",
|
|
212
|
+
"target": "app/components/content/Card.vue"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"path": "site-source/app/components/content/Compatibility.vue",
|
|
216
|
+
"target": "app/components/content/Compatibility.vue"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"path": "site-source/app/components/content/Document.vue",
|
|
220
|
+
"target": "app/components/content/Document.vue"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"path": "site-source/app/components/content/Faq.vue",
|
|
224
|
+
"target": "app/components/content/Faq.vue"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"path": "site-source/app/components/content/Features.vue",
|
|
228
|
+
"target": "app/components/content/Features.vue"
|
|
229
|
+
},
|
|
186
230
|
{
|
|
187
231
|
"path": "site-source/app/components/content/List.vue",
|
|
188
232
|
"target": "app/components/content/List.vue"
|
|
189
233
|
},
|
|
190
234
|
{
|
|
191
|
-
"path": "site-source/app/components/
|
|
192
|
-
"target": "app/components/
|
|
235
|
+
"path": "site-source/app/components/content/Reviews.vue",
|
|
236
|
+
"target": "app/components/content/Reviews.vue"
|
|
193
237
|
},
|
|
194
238
|
{
|
|
195
|
-
"path": "site-source/app/components/
|
|
196
|
-
"target": "app/components/
|
|
239
|
+
"path": "site-source/app/components/countdown/Timer.vue",
|
|
240
|
+
"target": "app/components/countdown/Timer.vue"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"path": "site-source/app/components/feedback/Toast.vue",
|
|
244
|
+
"target": "app/components/feedback/Toast.vue"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"path": "site-source/app/components/google/SignInButton.vue",
|
|
248
|
+
"target": "app/components/google/SignInButton.vue"
|
|
197
249
|
},
|
|
198
250
|
{
|
|
199
251
|
"path": "site-source/app/components/locale/Menu.vue",
|
|
200
252
|
"target": "app/components/locale/Menu.vue"
|
|
201
253
|
},
|
|
254
|
+
{
|
|
255
|
+
"path": "site-source/app/components/site/BackToTop.vue",
|
|
256
|
+
"target": "app/components/site/BackToTop.vue"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"path": "site-source/app/components/site/Brand.vue",
|
|
260
|
+
"target": "app/components/site/Brand.vue"
|
|
261
|
+
},
|
|
202
262
|
{
|
|
203
263
|
"path": "site-source/app/components/site/Footer.vue",
|
|
204
264
|
"target": "app/components/site/Footer.vue"
|
|
@@ -212,16 +272,12 @@
|
|
|
212
272
|
"target": "app/components/site/Shell.vue"
|
|
213
273
|
},
|
|
214
274
|
{
|
|
215
|
-
"path": "site-source/app/components/
|
|
216
|
-
"target": "app/components/
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
"path": "site-source/app/components/content/Document.vue",
|
|
220
|
-
"target": "app/components/content/Document.vue"
|
|
275
|
+
"path": "site-source/app/components/steps/Progress.vue",
|
|
276
|
+
"target": "app/components/steps/Progress.vue"
|
|
221
277
|
},
|
|
222
278
|
{
|
|
223
|
-
"path": "site-source/app/components/
|
|
224
|
-
"target": "app/components/
|
|
279
|
+
"path": "site-source/app/components/steps/Showcase.vue",
|
|
280
|
+
"target": "app/components/steps/Showcase.vue"
|
|
225
281
|
},
|
|
226
282
|
{
|
|
227
283
|
"path": "site-source/app/components/theme/Menu.vue",
|
|
@@ -235,6 +291,22 @@
|
|
|
235
291
|
"path": "site-source/app/components/ui/GradientBorder.vue",
|
|
236
292
|
"target": "app/components/ui/GradientBorder.vue"
|
|
237
293
|
},
|
|
294
|
+
{
|
|
295
|
+
"path": "site-source/app/components/ui/Icon.vue",
|
|
296
|
+
"target": "app/components/ui/Icon.vue"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"path": "site-source/app/components/ui/Image.vue",
|
|
300
|
+
"target": "app/components/ui/Image.vue"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"path": "site-source/app/components/ui/InfiniteScroll.vue",
|
|
304
|
+
"target": "app/components/ui/InfiniteScroll.vue"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"path": "site-source/app/components/ui/Marquee.vue",
|
|
308
|
+
"target": "app/components/ui/Marquee.vue"
|
|
309
|
+
},
|
|
238
310
|
{
|
|
239
311
|
"path": "site-source/app/components/ui/Modal.vue",
|
|
240
312
|
"target": "app/components/ui/Modal.vue"
|
|
@@ -248,20 +320,12 @@
|
|
|
248
320
|
"target": "app/components/ui/Text.vue"
|
|
249
321
|
},
|
|
250
322
|
{
|
|
251
|
-
"path": "site-source/app/components/
|
|
252
|
-
"target": "app/components/
|
|
323
|
+
"path": "site-source/app/components/ui/Video.vue",
|
|
324
|
+
"target": "app/components/ui/Video.vue"
|
|
253
325
|
},
|
|
254
326
|
{
|
|
255
|
-
"path": "site-source/app/components/
|
|
256
|
-
"target": "app/components/
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
"path": "site-source/app/components/google/SignInButton.vue",
|
|
260
|
-
"target": "app/components/google/SignInButton.vue"
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
"path": "site-source/app/components/ui/InfiniteScroll.vue",
|
|
264
|
-
"target": "app/components/ui/InfiniteScroll.vue"
|
|
327
|
+
"path": "site-source/app/components/whatsapp/Chat.vue",
|
|
328
|
+
"target": "app/components/whatsapp/Chat.vue"
|
|
265
329
|
},
|
|
266
330
|
{
|
|
267
331
|
"path": "site-source/app/components/youtube/Embed.vue",
|
|
@@ -275,6 +339,10 @@
|
|
|
275
339
|
"path": "site-source/app/composables/useConsent.ts",
|
|
276
340
|
"target": "app/composables/useConsent.ts"
|
|
277
341
|
},
|
|
342
|
+
{
|
|
343
|
+
"path": "site-source/app/composables/useFloatingBottomStack.ts",
|
|
344
|
+
"target": "app/composables/useFloatingBottomStack.ts"
|
|
345
|
+
},
|
|
278
346
|
{
|
|
279
347
|
"path": "site-source/app/composables/useGoogleIdentity.ts",
|
|
280
348
|
"target": "app/composables/useGoogleIdentity.ts"
|
|
@@ -295,6 +363,14 @@
|
|
|
295
363
|
"path": "site-source/app/composables/useToasts.ts",
|
|
296
364
|
"target": "app/composables/useToasts.ts"
|
|
297
365
|
},
|
|
366
|
+
{
|
|
367
|
+
"path": "site-source/app/consent/types.ts",
|
|
368
|
+
"target": "app/consent/types.ts"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"path": "site-source/app/content/types.ts",
|
|
372
|
+
"target": "app/content/types.ts"
|
|
373
|
+
},
|
|
298
374
|
{
|
|
299
375
|
"path": "site-source/app/layouts/app.vue",
|
|
300
376
|
"target": "app/layouts/app.vue"
|
|
@@ -303,6 +379,10 @@
|
|
|
303
379
|
"path": "site-source/app/layouts/default.vue",
|
|
304
380
|
"target": "app/layouts/default.vue"
|
|
305
381
|
},
|
|
382
|
+
{
|
|
383
|
+
"path": "site-source/app/navigation/types.ts",
|
|
384
|
+
"target": "app/navigation/types.ts"
|
|
385
|
+
},
|
|
306
386
|
{
|
|
307
387
|
"path": "site-source/app/plugins/consent.ts",
|
|
308
388
|
"target": "app/plugins/consent.ts"
|
|
@@ -311,6 +391,10 @@
|
|
|
311
391
|
"path": "site-source/app/plugins/google-identity.client.ts",
|
|
312
392
|
"target": "app/plugins/google-identity.client.ts"
|
|
313
393
|
},
|
|
394
|
+
{
|
|
395
|
+
"path": "site-source/app/site/config.ts",
|
|
396
|
+
"target": "app/site/config.ts"
|
|
397
|
+
},
|
|
314
398
|
{
|
|
315
399
|
"path": "site-source/app/theme/accordion.ts",
|
|
316
400
|
"target": "app/theme/accordion.ts"
|
|
@@ -331,6 +415,18 @@
|
|
|
331
415
|
"path": "site-source/app/theme/checkbox.ts",
|
|
332
416
|
"target": "app/theme/checkbox.ts"
|
|
333
417
|
},
|
|
418
|
+
{
|
|
419
|
+
"path": "site-source/app/theme/drawer.ts",
|
|
420
|
+
"target": "app/theme/drawer.ts"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"path": "site-source/app/theme/form-field.ts",
|
|
424
|
+
"target": "app/theme/form-field.ts"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"path": "site-source/app/theme/icon.ts",
|
|
428
|
+
"target": "app/theme/icon.ts"
|
|
429
|
+
},
|
|
334
430
|
{
|
|
335
431
|
"path": "site-source/app/theme/index.ts",
|
|
336
432
|
"target": "app/theme/index.ts"
|
|
@@ -347,6 +443,10 @@
|
|
|
347
443
|
"path": "site-source/app/theme/pagination.ts",
|
|
348
444
|
"target": "app/theme/pagination.ts"
|
|
349
445
|
},
|
|
446
|
+
{
|
|
447
|
+
"path": "site-source/app/theme/radio-group.ts",
|
|
448
|
+
"target": "app/theme/radio-group.ts"
|
|
449
|
+
},
|
|
350
450
|
{
|
|
351
451
|
"path": "site-source/app/theme/select.ts",
|
|
352
452
|
"target": "app/theme/select.ts"
|
|
@@ -355,6 +455,10 @@
|
|
|
355
455
|
"path": "site-source/app/theme/stepper.ts",
|
|
356
456
|
"target": "app/theme/stepper.ts"
|
|
357
457
|
},
|
|
458
|
+
{
|
|
459
|
+
"path": "site-source/app/theme/textarea.ts",
|
|
460
|
+
"target": "app/theme/textarea.ts"
|
|
461
|
+
},
|
|
358
462
|
{
|
|
359
463
|
"path": "site-source/app/theme/toast.ts",
|
|
360
464
|
"target": "app/theme/toast.ts"
|
|
@@ -362,25 +466,9 @@
|
|
|
362
466
|
{
|
|
363
467
|
"path": "site-source/app/theme/tooltip.ts",
|
|
364
468
|
"target": "app/theme/tooltip.ts"
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
"path": "site-source/app/consent/types.ts",
|
|
368
|
-
"target": "app/consent/types.ts"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"path": "site-source/app/content/types.ts",
|
|
372
|
-
"target": "app/content/types.ts"
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
"path": "site-source/app/navigation/types.ts",
|
|
376
|
-
"target": "app/navigation/types.ts"
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
"path": "site-source/app/site/config.ts",
|
|
380
|
-
"target": "app/site/config.ts"
|
|
381
469
|
}
|
|
382
470
|
],
|
|
383
|
-
"dependencies": [],
|
|
471
|
+
"dependencies": ["@stackonward/composables"],
|
|
384
472
|
"devDependencies": [],
|
|
385
473
|
"registryDependencies": [],
|
|
386
474
|
"framework": "nuxt",
|
|
@@ -63,7 +63,11 @@ onBeforeUnmount(() => {
|
|
|
63
63
|
<Teleport to="body">
|
|
64
64
|
<Transition name="bottom-sheet-fade">
|
|
65
65
|
<div v-if="modelValue" class="bottom-sheet" role="presentation">
|
|
66
|
-
<button
|
|
66
|
+
<button
|
|
67
|
+
class="bottom-sheet__overlay"
|
|
68
|
+
aria-label="Close sheet"
|
|
69
|
+
@click="handleOverlayClick"
|
|
70
|
+
/>
|
|
67
71
|
<section class="bottom-sheet__panel" role="dialog" aria-modal="true" :aria-label="title">
|
|
68
72
|
<div v-if="showHandle" class="bottom-sheet__handle" aria-hidden="true" />
|
|
69
73
|
<header v-if="title || description || $slots.header" class="bottom-sheet__header">
|
|
@@ -86,7 +86,8 @@ function pointerUp(event: PointerEvent): void {
|
|
|
86
86
|
defineExpose({
|
|
87
87
|
scrollTo,
|
|
88
88
|
scrollToTop: (smooth = true) => scrollTo(position().x, 0, smooth),
|
|
89
|
-
scrollToBottom: (smooth = true) =>
|
|
89
|
+
scrollToBottom: (smooth = true) =>
|
|
90
|
+
scrollTo(position().x, viewport.value?.scrollHeight ?? 0, smooth),
|
|
90
91
|
getScrollPosition: position,
|
|
91
92
|
});
|
|
92
93
|
</script>
|
|
@@ -389,8 +389,9 @@ Adding features to an existing module or making significant changes. Does not re
|
|
|
389
389
|
## 3. Database Changes
|
|
390
390
|
|
|
391
391
|
For each new/modified field:
|
|
392
|
+
|
|
392
393
|
| Column | Type | Nullable | Default | Comment | Table |
|
|
393
|
-
|
|
394
|
+
| ------ | ---- | -------- | ------- | ------- | ----- |
|
|
394
395
|
|
|
395
396
|
Migration SQL:
|
|
396
397
|
|
|
@@ -47,6 +47,12 @@ The selected stack has complete coding rules in `.claude/docs/stack.md`. Shared
|
|
|
47
47
|
|
|
48
48
|
**Read the selected stack and applicable nested `CLAUDE.md` before writing code.**
|
|
49
49
|
|
|
50
|
+
## Visual Design Standards (CRITICAL)
|
|
51
|
+
|
|
52
|
+
For a stack with a visual interface, **MUST** read the design standard identified by `.claude/docs/stack.md` before choosing icons, colors, sizing or component overrides. Use that stack's installed component system, token owner and asset conventions. Icon artwork, accessible labels and interactive target size are separate responsibilities; local size changes must use the existing component contract.
|
|
53
|
+
|
|
54
|
+
**FORBIDDEN**: inventing a second icon library or theme implementation for an existing capability. Non-visual stacks do not acquire UI dependencies or a required design document from this rule.
|
|
55
|
+
|
|
50
56
|
## Technology Stack and Environment
|
|
51
57
|
|
|
52
58
|
Read `package.json`, `.env.example`, configuration consumers and `.claude/docs/stack.md` before changing dependencies or environment variables. A sample variable does not prove that runtime code reads it. Nuxt uses `pnpm dev` / `pnpm preview`; MCP uses `pnpm start:stdio` after build. Never put private credentials in Nuxt public configuration or MCP tool output.
|
|
@@ -423,6 +429,7 @@ Durable standards belong in `.claude/docs/`; concrete designs and evidence belon
|
|
|
423
429
|
|
|
424
430
|
- [ ] Read changed owners, nested rules, dependencies and direct consumers.
|
|
425
431
|
- [ ] Contracts, enum authority, configuration and lifecycle are consistent.
|
|
432
|
+
- [ ] Visual changes follow the installed stack design standard, token/component owner and accessibility contract.
|
|
426
433
|
- [ ] Relevant success, validation, authorization and recovery behavior is verified.
|
|
427
434
|
- [ ] Declared stack checks pass; unavailable checks are reported accurately.
|
|
428
435
|
- [ ] No secrets, dead imports or deferred production code enter the diff.
|
|
@@ -47,6 +47,12 @@ The selected stack has complete coding rules in `.claude/docs/stack.md`. Shared
|
|
|
47
47
|
|
|
48
48
|
**Read the selected stack and applicable nested `CLAUDE.md` before writing code.**
|
|
49
49
|
|
|
50
|
+
## Visual Design Standards (CRITICAL)
|
|
51
|
+
|
|
52
|
+
For a stack with a visual interface, **MUST** read the design standard identified by `.claude/docs/stack.md` before choosing icons, colors, sizing or component overrides. Use that stack's installed component system, token owner and asset conventions. Icon artwork, accessible labels and interactive target size are separate responsibilities; local size changes must use the existing component contract.
|
|
53
|
+
|
|
54
|
+
**FORBIDDEN**: inventing a second icon library or theme implementation for an existing capability. Non-visual stacks do not acquire UI dependencies or a required design document from this rule.
|
|
55
|
+
|
|
50
56
|
## Technology Stack and Environment
|
|
51
57
|
|
|
52
58
|
Read `package.json`, `.env.example`, configuration consumers and `.claude/docs/stack.md` before changing dependencies or environment variables. A sample variable does not prove that runtime code reads it. Nuxt uses `pnpm dev` / `pnpm preview`; MCP uses `pnpm start:stdio` after build. Never put private credentials in Nuxt public configuration or MCP tool output.
|
|
@@ -423,6 +429,7 @@ Durable standards belong in `.claude/docs/`; concrete designs and evidence belon
|
|
|
423
429
|
|
|
424
430
|
- [ ] Read changed owners, nested rules, dependencies and direct consumers.
|
|
425
431
|
- [ ] Contracts, enum authority, configuration and lifecycle are consistent.
|
|
432
|
+
- [ ] Visual changes follow the installed stack design standard, token/component owner and accessibility contract.
|
|
426
433
|
- [ ] Relevant success, validation, authorization and recovery behavior is verified.
|
|
427
434
|
- [ ] Declared stack checks pass; unavailable checks are reported accurately.
|
|
428
435
|
- [ ] No secrets, dead imports or deferred production code enter the diff.
|
|
@@ -132,6 +132,7 @@ Nuxt component directories remain namespaces. `components/site/Header.vue` is `H
|
|
|
132
132
|
| NX-H3 | Per-page hardcoded theme overrides | Existing token/theme owner |
|
|
133
133
|
| NX-H4 | Direct DOM mutation for reactive UI | Vue refs and state |
|
|
134
134
|
| NX-H5 | Global component CSS leaking across routes | Utility classes or scoped owner styles |
|
|
135
|
+
| NX-H6 | Parallel icon libraries/renderers or global SVG overrides | UiIcon, local Lucide, Nuxt UI slots and DESIGN.md |
|
|
135
136
|
| NX-E1 | Business enum metadata copied into pages | Actual backend metadata/generated contract |
|
|
136
137
|
| NX-E2 | Invented `useEnum` endpoint | Use installed owner and declared contract |
|
|
137
138
|
| NX-M1 | Mixed locale persistence formats | Explicit locale boundary mapping |
|
|
@@ -371,6 +372,18 @@ const emit = defineEmits<{ submit: [] }>();
|
|
|
371
372
|
|
|
372
373
|
Use Tailwind utilities and theme tokens. Scoped CSS is for behavior that utilities cannot express cleanly. Dark/light modes must use the same semantic tokens; never fix one page with a hardcoded light background that breaks dark mode.
|
|
373
374
|
|
|
375
|
+
### Icon Design Contract
|
|
376
|
+
|
|
377
|
+
**MUST** read [DESIGN.md](../../DESIGN.md) for icon geometry, the size scale, semantic colors and acceptance. The installed system uses local Lucide functional icons and a separate `brand` collection. `UiIcon` owns standalone `name`, `size?: string | number` and `label?: string`; Nuxt UI controls retain their internal icon slots and use the same provider.
|
|
378
|
+
|
|
379
|
+
The ordinary default is 20px through `app/theme/icon.ts` and `--site-icon-md`. Choose one `size-icon-*` utility or explicit `size` value when the caller requires another size. Preserve source geometry and proportional strokes. Functional colors inherit semantic tokens; brand proportions and identity colors remain asset-owned.
|
|
380
|
+
|
|
381
|
+
✅ `<UiIcon name="i-lucide-search" />` uses the shared default; `<UiIcon name="i-lucide-search" class="size-icon-sm" />` selects a local scale level.
|
|
382
|
+
|
|
383
|
+
❌ A second icon wrapper, handwritten duplicate SVG, mixed functional family, competing `size`/size class or global `svg` style bypasses the design owner.
|
|
384
|
+
|
|
385
|
+
Icon-only controls retain at least 44 × 44px targets and accessible names on the button/link. Decorative icons omit `label`; standalone meaning supplies a localized `label`. Do not communicate status only through color. Keep `fallbackToApi: false`, register local brand assets in `nuxt.config.ts` and verify missing icon names against the installed collection.
|
|
386
|
+
|
|
374
387
|
### Complete Create/Edit Form, Recovery and Close Lifecycle
|
|
375
388
|
|
|
376
389
|
The component below consumes the three owners defined in section VI. It includes a real query-driven list, create/edit entry, typed validation, mutation submission, post-save refresh and failure recovery. All types/functions it imports are defined above or exported by the installed framework. Nuxt UI's `validate` callback avoids adding an uninstalled schema library to this template.
|
|
@@ -664,7 +677,7 @@ Read [Architecture](architecture.md), [Security and Lifecycle](security-lifecycl
|
|
|
664
677
|
- [ ] Private configuration never enters public payloads.
|
|
665
678
|
- [ ] Every visible action maps to a real implementation and error path.
|
|
666
679
|
- [ ] All applicable NX-S/A/T/C/H/E/M/P/U rules are checked.
|
|
667
|
-
- [ ] Design coverage includes children, overlays and loading/empty/error/forbidden states.
|
|
680
|
+
- [ ] Design coverage includes children, overlays, icon sizes/names/targets and loading/empty/error/forbidden states.
|
|
668
681
|
- [ ] `pnpm typecheck` and `pnpm build` pass for code/contract changes.
|
|
669
682
|
- [ ] Browser and network evidence proves the touched interactions.
|
|
670
683
|
- [ ] No uninstalled lint/test/authorization capability is claimed.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Nuxt Design Standard
|
|
2
|
+
|
|
3
|
+
This document defines the visual contract for the application-owned Nuxt UI foundation. Read it with the [Nuxt Engineering Standard](.claude/docs/stack.md) before changing icons, controls or theme defaults. Application branding remains in its own assets and semantic color tokens.
|
|
4
|
+
|
|
5
|
+
## Design Decisions
|
|
6
|
+
|
|
7
|
+
| Decision | Primary-source pattern | Application choice | Reason |
|
|
8
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
|
|
9
|
+
| Functional icon family | [Lucide specification](https://lucide.dev/contribute/icons/specification) defines a consistent canvas, stroke and visual weight. | Use the installed Lucide collection with its original geometry. | One family keeps related actions visually coherent. |
|
|
10
|
+
| Size hierarchy | [Carbon icon usage](https://carbondesignsystem.com/elements/icons/usage/) uses a small, consistent scale and separates artwork from touch targets. | Use 16/20/24/32px for interface icons; 20px is the ordinary default. | Central sizing supports both compact and prominent contexts. |
|
|
11
|
+
| Accessible meaning | [Lucide accessibility](https://lucide.dev/how-to/accessibility) distinguishes decorative, standalone and interactive icons. | Hide decoration; label standalone meaning or the interactive control that owns an action. | Assistive technology announces each meaning once. |
|
|
12
|
+
| Rendering and delivery | [Nuxt Icon](https://nuxt.com/modules/icon) supports application defaults, local collections and SVG rendering. | Reuse Nuxt Icon through `UiIcon`, bundle Lucide locally and disable external API fallback. | SSR and client rendering use the same locally owned assets and configuration. |
|
|
13
|
+
|
|
14
|
+
## Ownership
|
|
15
|
+
|
|
16
|
+
| Owner | Contract |
|
|
17
|
+
| ------------------------------------------------ | ------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `app/assets/css/site-shell.css` | `--site-icon-*` size tokens, `size-icon-*` Tailwind aliases and semantic colors |
|
|
19
|
+
| `app/theme/icon.ts` | Nuxt Icon defaults: 20px token, SVG mode, non-shrinking alignment and decorative attributes |
|
|
20
|
+
| `app/app.config.ts` | Composes `icon` defaults and the Nuxt UI theme |
|
|
21
|
+
| `app/components/ui/Icon.vue` | Typed `name`, `size` and accessible `label` adapter |
|
|
22
|
+
| `app/theme/button.ts`, `input.ts`, `checkbox.ts` | Internal icon slots and the control's geometry |
|
|
23
|
+
| `nuxt.config.ts` | Locally bundled `lucide`, `brand` SVG collection and `fallbackToApi: false` |
|
|
24
|
+
| `app/assets/icons/brand/` | Reviewed third-party identity assets |
|
|
25
|
+
|
|
26
|
+
Registry-installed source belongs to this application. **MUST** change the owning token or component when a repeated pattern changes. A one-location size choice stays at its caller. **FORBIDDEN**: adding a second icon component, runtime registry, icon font or icon library for an existing Lucide capability.
|
|
27
|
+
|
|
28
|
+
## Functional Icon Geometry
|
|
29
|
+
|
|
30
|
+
Functional icons **MUST** use `i-lucide-*` names from the installed collection. Their design uses a square 24 × 24 viewBox, 2-unit centered strokes, rounded open-path caps and rounded joins. Preserve the family's edge clearance, internal spacing and visual balance; identical CSS dimensions alone do not establish equal optical weight. These constraints follow the [Lucide specification](https://lucide.dev/contribute/icons/specification).
|
|
31
|
+
|
|
32
|
+
Keep the original aspect ratio and geometry when rendering at another size. Strokes scale proportionally with the viewBox; the 2-unit source stroke is not a fixed 2 CSS-pixel stroke at every display size. **FORBIDDEN**: per-location stroke-width overrides, non-uniform scaling, padding embedded in duplicate SVGs or arbitrary `translate` corrections. Align icons and text with the control's flex/grid layout. A family-level optical issue is resolved in the asset or by selecting a clearer existing icon.
|
|
33
|
+
|
|
34
|
+
Use the same icon for the same action across routes. Distinct actions must not share an ambiguous symbol in the same context. Prefer familiar action/object meanings and retain text for unfamiliar actions. Selection, loading and error states use the established control and status contracts; a spinner alone does not explain what is pending.
|
|
35
|
+
|
|
36
|
+
## Size Scale
|
|
37
|
+
|
|
38
|
+
Values use rem and therefore follow the user's root text size. Pixel equivalents below assume a 16px root.
|
|
39
|
+
|
|
40
|
+
| Token / utility | Value | Intended use |
|
|
41
|
+
| ----------------------------------- | -------------- | -------------------------------------------------------- |
|
|
42
|
+
| `--site-icon-xs` / `size-icon-xs` | 0.75rem / 12px | Internal checkbox mark only |
|
|
43
|
+
| `--site-icon-sm` / `size-icon-sm` | 1rem / 16px | Compact inline or control icon |
|
|
44
|
+
| `--site-icon-md` / `size-icon-md` | 1.25rem / 20px | Default functional icon, navigation and ordinary actions |
|
|
45
|
+
| `--site-icon-lg` / `size-icon-lg` | 1.5rem / 24px | Prominent control or section icon |
|
|
46
|
+
| `--site-icon-xl` / `size-icon-xl` | 2rem / 32px | Status or feature illustration |
|
|
47
|
+
| `--site-icon-2xl` / `size-icon-2xl` | 2.5rem / 40px | Large status or feature illustration |
|
|
48
|
+
| `--site-icon-3xl` / `size-icon-3xl` | 3rem / 48px | Large empty-state or explanatory illustration |
|
|
49
|
+
|
|
50
|
+
Use the default unless the surrounding component calls for another level. Buttons and inputs map `xs`/`sm` to 16px, `md`/`lg` to 20px and `xl` to 24px; the button's `2xl` also uses 24px. Internal Nuxt UI icons keep their component slot ownership and share the same Nuxt Icon provider. Do not manually duplicate an icon already provided by a control's `icon`, `leading-icon` or `trailing-icon` prop.
|
|
51
|
+
|
|
52
|
+
An icon-only button or link **MUST** expose a target of at least 44 × 44 CSS pixels, including on narrow viewports. The artwork can remain 16, 20 or 24px. This separates visual density from interaction size, as described by [Carbon](https://carbondesignsystem.com/elements/icons/usage/) and [Lucide](https://lucide.dev/how-to/accessibility). Control padding, minimum dimensions and focus behavior belong to the interactive wrapper.
|
|
53
|
+
|
|
54
|
+
## Component API and Overrides
|
|
55
|
+
|
|
56
|
+
`UiIcon` accepts `name: string`, `size?: string | number` and `label?: string`. Numeric `size` values are CSS pixels; strings accept CSS lengths, including rem and token references. Without `size`, the application default is `var(--site-icon-md)`.
|
|
57
|
+
|
|
58
|
+
✅ **Use the shared default or one explicit size mechanism.**
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<UiIcon name="i-lucide-image" />
|
|
62
|
+
<UiIcon name="i-lucide-image" class="size-icon-lg" />
|
|
63
|
+
<UiIcon name="i-lucide-image" :size="24" />
|
|
64
|
+
<UiIcon name="i-lucide-image" size="var(--site-icon-xl)" />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`size-icon-*` is the preferred local scale selection. Use `size` when a measured integration requires a specific value. **MUST** choose one sizing mechanism for an instance; `size` and a sizing class must not compete. Color is supplied with semantic classes or inherited from the parent, not a separate icon-specific color prop.
|
|
68
|
+
|
|
69
|
+
❌ **Competing sizes and page-owned artwork bypass the shared contract.**
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<UiIcon name="i-lucide-image" :size="18" class="size-icon-lg" />
|
|
73
|
+
<svg viewBox="0 0 32 20" class="h-4 w-7" stroke-width="3" />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**FORBIDDEN**: global `svg` width, height, stroke or fill rules. They also affect logos, charts, cropping overlays and other non-icon graphics. Nuxt's path-aware component naming remains enabled; standalone application icons use `UiIcon`, while Nuxt UI controls render their own internal icons.
|
|
77
|
+
|
|
78
|
+
## Color and Brand Assets
|
|
79
|
+
|
|
80
|
+
Functional icons inherit `currentColor`. Use the same semantic foreground as adjacent text, or an explicit semantic role such as `text-primary`, `text-success` or `text-warning`. Hover, active, disabled and focus states come from the control/theme owner. A status must also have recognizable shape or text; color alone is insufficient.
|
|
81
|
+
|
|
82
|
+
Verify foreground/background contrast in each supported theme. Meaningful interface icons must reach at least 3:1 against adjacent colors under [WCAG non-text contrast](https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html); ordinary paired text requires at least 4.5:1. Aim for 4.5:1 for icons where practical, following the stronger [Carbon guidance](https://carbondesignsystem.com/elements/icons/usage/). Use the product's accessible interaction token instead of assuming its logo color is suitable for a small control. Inactive controls are exempt from the non-text contrast requirement, but their disabled state must remain understandable within the labeled control.
|
|
83
|
+
|
|
84
|
+
Third-party identities are local `brand:*` assets, including `brand:apple`, `brand:android` and `brand:whatsapp`. Preserve their proportions and any required identity colors; monochrome variants inherit the foreground only when the supplied asset supports that treatment. Product logos use the product's approved assets and brand specification. **FORBIDDEN**: recoloring a provider logo as a generic status icon, forcing logos into Lucide geometry or embedding a product's palette in shared icon defaults.
|
|
85
|
+
|
|
86
|
+
Local brand files use concise kebab-case names under `app/assets/icons/brand/`. Nuxt config registers that collection. Do not restore obsolete `common:*` names, slash-name conversion, compatibility aliases or global SVG symbol injection.
|
|
87
|
+
|
|
88
|
+
## Accessible Names and Interaction
|
|
89
|
+
|
|
90
|
+
`UiIcon` is decorative by default: `aria-hidden="true"` and `focusable="false"`. A non-empty `label` exposes `role="img"` and an accessible name. The label describes meaning, not the icon's filename. These defaults implement the distinctions in the [Lucide accessibility guide](https://lucide.dev/how-to/accessibility).
|
|
91
|
+
|
|
92
|
+
✅ **Nearby text owns the meaning; the icon stays decorative.**
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<span class="inline-flex items-center gap-2 text-success">
|
|
96
|
+
<UiIcon name="i-lucide-circle-check" />
|
|
97
|
+
Ready
|
|
98
|
+
</span>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
✅ **A standalone semantic icon receives a localized label.**
|
|
102
|
+
|
|
103
|
+
```vue
|
|
104
|
+
<UiIcon name="i-lucide-circle-check" :label="t('status.ready')" class="text-success" />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
✅ **An icon action is labeled on its semantic button.**
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<UiButton
|
|
111
|
+
square
|
|
112
|
+
color="neutral"
|
|
113
|
+
variant="ghost"
|
|
114
|
+
icon="i-lucide-x"
|
|
115
|
+
:aria-label="t('actions.close')"
|
|
116
|
+
@click="close"
|
|
117
|
+
/>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
❌ **A labeled picture is not a keyboard-operable, named action.**
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<UiIcon name="i-lucide-x" label="Close" @click="close" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use the existing locale owner for labels, visible focus on controls and the component's keyboard behavior. A tooltip supplements a control's name; it does not replace that name. Do not add `label` to an icon that repeats its button's accessible name or adjacent text.
|
|
127
|
+
|
|
128
|
+
## Additions and Verification
|
|
129
|
+
|
|
130
|
+
1. Search existing component, navigation and state owners for the same meaning; reuse the established Lucide name and component before adding an asset.
|
|
131
|
+
2. Confirm the chosen name exists in the installed local collection. Keep `fallbackToApi: false`; an unbundled icon is a missing dependency or registration, not a reason to fetch a public icon API.
|
|
132
|
+
3. Review any new brand asset for ownership, intrinsic dimensions, aspect ratio and identity color. Keep product-only assets in the product. A genuinely missing functional symbol requires an explicit design decision before another collection is introduced.
|
|
133
|
+
4. Keep glyph size, control hit target and text alignment separate. Inspect optical weight at 16/20/24/32px, the ordinary 20px default, an explicit override and the component's actual size variants.
|
|
134
|
+
5. Verify decorative versus meaningful accessibility, keyboard action/focus, status text, loading/disabled states, light/dark contrast and narrow layout. Inspect actual rendered SVG dimensions and viewBox; a successful typecheck alone is not visual evidence.
|
|
135
|
+
6. For shared-source changes, verify source → registry → packed CLI → externally generated application. The generated `/ui` catalog provides default, size, semantic-label, brand and control examples. Application-specific routes verify their own composition.
|
|
136
|
+
|
|
137
|
+
### Acceptance Checklist
|
|
138
|
+
|
|
139
|
+
- [ ] Functional icons come from the installed Lucide family; brand assets remain separate.
|
|
140
|
+
- [ ] The 20px default, selected size tokens and explicit `size` override render correctly without competing size declarations.
|
|
141
|
+
- [ ] Source geometry and proportional strokes are preserved; no arbitrary per-icon offsets or global SVG overrides exist.
|
|
142
|
+
- [ ] Icon-only controls retain at least 44 × 44px targets and visible keyboard focus.
|
|
143
|
+
- [ ] Decorative icons are silent; standalone meaning and actions have names at the correct owner.
|
|
144
|
+
- [ ] Status remains understandable without color, and supported themes have adequate contrast.
|
|
145
|
+
- [ ] Icons resolve from local assets/bundles with no external icon API fallback.
|
|
146
|
+
- [ ] Changed shared assets, rules and defaults survive packed CLI generation and the generated app's checks.
|
|
@@ -50,7 +50,7 @@ useSeo({
|
|
|
50
50
|
<span>Output mix</span>
|
|
51
51
|
<ul class="workspace-page__formats">
|
|
52
52
|
<li v-for="format in outputFormats" :key="format">
|
|
53
|
-
<
|
|
53
|
+
<UiIcon name="i-lucide-check" class="text-primary" />
|
|
54
54
|
{{ format }}
|
|
55
55
|
</li>
|
|
56
56
|
</ul>
|
|
@@ -119,7 +119,13 @@ useSeo({
|
|
|
119
119
|
gap: 18px;
|
|
120
120
|
border: 1px solid var(--site-color-border);
|
|
121
121
|
border-radius: var(--site-radius-lg);
|
|
122
|
-
background:
|
|
122
|
+
background:
|
|
123
|
+
linear-gradient(
|
|
124
|
+
180deg,
|
|
125
|
+
color-mix(in srgb, var(--site-color-primary) 4%, transparent),
|
|
126
|
+
transparent
|
|
127
|
+
),
|
|
128
|
+
var(--site-color-surface);
|
|
123
129
|
padding: 20px;
|
|
124
130
|
}
|
|
125
131
|
|
|
@@ -148,7 +154,7 @@ useSeo({
|
|
|
148
154
|
gap: 12px;
|
|
149
155
|
border: 1px solid var(--site-color-border);
|
|
150
156
|
border-radius: var(--site-radius-md);
|
|
151
|
-
background:
|
|
157
|
+
background: color-mix(in srgb, var(--site-color-primary) 4%, transparent);
|
|
152
158
|
padding: 14px;
|
|
153
159
|
}
|
|
154
160
|
|
|
@@ -193,10 +199,6 @@ useSeo({
|
|
|
193
199
|
color: var(--site-color-muted);
|
|
194
200
|
}
|
|
195
201
|
|
|
196
|
-
.workspace-page__formats svg {
|
|
197
|
-
color: var(--site-color-primary);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
202
|
.workspace-page__metric strong {
|
|
201
203
|
color: var(--site-color-primary);
|
|
202
204
|
font-size: 58px;
|