@maltjoy/mcp-server 0.4.0 → 0.6.0-beta.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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -3
  3. package/dist/doc/VJoyAdminBanner/guideline.md +284 -0
  4. package/dist/doc/VJoyAvailability/guideline.md +352 -0
  5. package/dist/doc/VJoyAvatar/guideline.md +192 -0
  6. package/dist/doc/VJoyAvatarsList/guideline.md +328 -0
  7. package/dist/doc/VJoyBadge/guideline.md +169 -0
  8. package/dist/doc/VJoyBadgeLevel/guideline.md +166 -0
  9. package/dist/doc/VJoyBlockSkeleton/guideline.md +150 -0
  10. package/dist/doc/VJoyBottomSheet/guideline.md +345 -0
  11. package/dist/doc/VJoyBottomSheetTrigger/guideline.md +156 -0
  12. package/dist/doc/VJoyButton/guideline.md +371 -0
  13. package/dist/doc/VJoyButton/metadata.json +0 -16
  14. package/dist/doc/VJoyButtonCard/guideline.md +384 -0
  15. package/dist/doc/VJoyCheckbox/guideline.md +317 -0
  16. package/dist/doc/VJoyCheckboxCard/guideline.md +381 -0
  17. package/dist/doc/VJoyCheckboxGroup/guideline.md +363 -0
  18. package/dist/doc/VJoyCheckboxGroup/metadata.json +7 -7
  19. package/dist/doc/VJoyCollapse/guideline.md +362 -0
  20. package/dist/doc/VJoyCollapseItem/guideline.md +394 -0
  21. package/dist/doc/VJoyCompanyAvatar/guideline.md +162 -0
  22. package/dist/doc/VJoyDialog/guideline.md +425 -0
  23. package/dist/doc/VJoyDialogTrigger/guideline.md +186 -0
  24. package/dist/doc/VJoyDot/guideline.md +177 -0
  25. package/dist/doc/VJoyDrawer/guideline.md +392 -0
  26. package/dist/doc/VJoyDrawerTrigger/guideline.md +145 -0
  27. package/dist/doc/VJoyDropdown/guideline.md +393 -0
  28. package/dist/doc/VJoyDropdown/metadata.json +6 -6
  29. package/dist/doc/VJoyDropdownList/guideline.md +396 -0
  30. package/dist/doc/VJoyDropdownList/metadata.json +7 -7
  31. package/dist/doc/VJoyDropzone/guideline.md +390 -0
  32. package/dist/doc/VJoyDropzone/metadata.json +36 -1
  33. package/dist/doc/VJoyFilterBar/guideline.md +419 -0
  34. package/dist/doc/VJoyFilterBarButton/guideline.md +359 -0
  35. package/dist/doc/VJoyFloatingActionsBar/guideline.md +372 -0
  36. package/dist/doc/VJoyFormError/guideline.md +325 -0
  37. package/dist/doc/VJoyFormError/metadata.json +0 -14
  38. package/dist/doc/VJoyFormFieldSkeleton/guideline.md +159 -0
  39. package/dist/doc/VJoyFormGroup/guideline.md +285 -0
  40. package/dist/doc/VJoyFunnel/guideline.md +349 -0
  41. package/dist/doc/VJoyFunnelFooter/guideline.md +178 -0
  42. package/dist/doc/VJoyFunnelHeader/guideline.md +204 -0
  43. package/dist/doc/VJoyHighlight/guideline.md +169 -0
  44. package/dist/doc/VJoyIcon/JoyIcon.types.ts +1 -1
  45. package/dist/doc/VJoyIcon/guideline.md +168 -0
  46. package/dist/doc/VJoyIcon/metadata.json +0 -1
  47. package/dist/doc/VJoyIconButton/guideline.md +169 -0
  48. package/dist/doc/VJoyIconButton/metadata.json +1 -1
  49. package/dist/doc/VJoyIndicator/guideline.md +168 -0
  50. package/dist/doc/VJoyIndicators/guideline.md +183 -0
  51. package/dist/doc/VJoyInput/JoyInput.types.ts +1 -6
  52. package/dist/doc/VJoyInput/guideline.md +401 -0
  53. package/dist/doc/VJoyInput/metadata.json +5 -14
  54. package/dist/doc/VJoyInputDigit/guideline.md +381 -0
  55. package/dist/doc/VJoyInputNumber/guideline.md +414 -0
  56. package/dist/doc/VJoyInputNumber/metadata.json +22 -17
  57. package/dist/doc/VJoyInputPassword/guideline.md +379 -0
  58. package/dist/doc/VJoyInputPassword/metadata.json +0 -14
  59. package/dist/doc/VJoyLabel/guideline.md +200 -0
  60. package/dist/doc/VJoyLink/guideline.md +360 -0
  61. package/dist/doc/VJoyListItem/guideline.md +335 -0
  62. package/dist/doc/VJoyMenu/guideline.md +304 -0
  63. package/dist/doc/VJoyMenu/metadata.json +21 -21
  64. package/dist/doc/VJoyMenuItem/guideline.md +352 -0
  65. package/dist/doc/VJoyPagination/guideline.md +355 -0
  66. package/dist/doc/VJoyPanel/guideline.md +238 -0
  67. package/dist/doc/VJoyPanelSection/guideline.md +160 -0
  68. package/dist/doc/VJoyPasswordRequirement/guideline.md +336 -0
  69. package/dist/doc/VJoyPasswordRequirement/metadata.json +1 -1
  70. package/dist/doc/VJoyProductTour/JoyProductTour.types.ts +0 -1
  71. package/dist/doc/VJoyProductTour/guideline.md +455 -0
  72. package/dist/doc/VJoyProductTour/metadata.json +1 -1
  73. package/dist/doc/VJoyProductTourTrigger/guideline.md +167 -0
  74. package/dist/doc/VJoyProgressBar/guideline.md +348 -0
  75. package/dist/doc/VJoyRadio/guideline.md +346 -0
  76. package/dist/doc/VJoyRadioGroup/guideline.md +331 -0
  77. package/dist/doc/VJoyRatingStars/guideline.md +306 -0
  78. package/dist/doc/VJoyScreenLoader/guideline.md +172 -0
  79. package/dist/doc/VJoySelect/guideline.md +396 -0
  80. package/dist/doc/VJoySelectableItem/guideline.md +351 -0
  81. package/dist/doc/VJoySelectableItemGroup/guideline.md +370 -0
  82. package/dist/doc/VJoySeparator/guideline.md +161 -0
  83. package/dist/doc/VJoySnackbar/guideline.md +402 -0
  84. package/dist/doc/VJoySpinner/guideline.md +168 -0
  85. package/dist/doc/VJoyStep/guideline.md +182 -0
  86. package/dist/doc/VJoyStepper/guideline.md +390 -0
  87. package/dist/doc/VJoyTab/guideline.md +298 -0
  88. package/dist/doc/VJoyTabs/guideline.md +353 -0
  89. package/dist/doc/VJoyTag/guideline.md +226 -0
  90. package/dist/doc/VJoyTagsInput/guideline.md +387 -0
  91. package/dist/doc/VJoyTagsList/guideline.md +311 -0
  92. package/dist/doc/VJoyTagsList/metadata.json +27 -27
  93. package/dist/doc/VJoyTemplate/guideline.md +366 -0
  94. package/dist/doc/VJoyText/guideline.md +175 -0
  95. package/dist/doc/VJoyText/metadata.json +1 -1
  96. package/dist/doc/VJoyTextarea/guideline.md +378 -0
  97. package/dist/doc/VJoyTitleBrand/guideline.md +155 -0
  98. package/dist/doc/VJoyToggle/guideline.md +327 -0
  99. package/dist/doc/VJoyTooltip/guideline.md +322 -0
  100. package/dist/doc/VJoyUserCard/guideline.md +268 -0
  101. package/dist/doc/VJoyUserCard/metadata.json +3 -17
  102. package/dist/doc/VJoyWalkthrough/guideline.md +351 -0
  103. package/dist/doc/VJoyWalkthroughTrigger/guideline.md +171 -0
  104. package/dist/doc/VJoyWrapper/guideline.md +173 -0
  105. package/dist/doc/css-classes.json +1 -15
  106. package/dist/doc/design-tokens.json +1 -0
  107. package/dist/doc/index.json +20 -30
  108. package/dist/index.js +22 -2
  109. package/dist/index.js.map +1 -1
  110. package/dist/tools/get-component-guideline.d.ts +30 -0
  111. package/dist/tools/get-component-guideline.d.ts.map +1 -0
  112. package/dist/tools/get-component-guideline.js +51 -0
  113. package/dist/tools/get-component-guideline.js.map +1 -0
  114. package/dist/tools/get-documentation-page.d.ts +22 -0
  115. package/dist/tools/get-documentation-page.d.ts.map +1 -0
  116. package/dist/tools/get-documentation-page.js +21 -0
  117. package/dist/tools/get-documentation-page.js.map +1 -0
  118. package/dist/tools/list-documentation-pages.d.ts +22 -0
  119. package/dist/tools/list-documentation-pages.d.ts.map +1 -0
  120. package/dist/tools/list-documentation-pages.js +17 -0
  121. package/dist/tools/list-documentation-pages.js.map +1 -0
  122. package/dist/tools/search-documentation.d.ts +41 -0
  123. package/dist/tools/search-documentation.d.ts.map +1 -0
  124. package/dist/tools/search-documentation.js +25 -0
  125. package/dist/tools/search-documentation.js.map +1 -0
  126. package/dist/zeroheight-parser.d.ts +27 -0
  127. package/dist/zeroheight-parser.d.ts.map +1 -0
  128. package/dist/zeroheight-parser.js +77 -0
  129. package/dist/zeroheight-parser.js.map +1 -0
  130. package/package.json +10 -10
  131. package/dist/doc/VJoyMultiCheckbox/metadata.json +0 -332
@@ -0,0 +1,402 @@
1
+ ---
2
+ component: VJoySnackbar
3
+ status: draft
4
+ language: en
5
+ dsds: JoySnackbar.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Snackbar
10
+
11
+ Use **`pushVJoySnackbar`** when you need a brief, non-blocking status update after a user or system action — saved changes, a failed request, or a reversible outcome.
12
+
13
+ Do **not** mount `<VJoySnackbar>` in templates. The service creates the overlay, wires the event bus, and handles stacking and teardown.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `pushVJoySnackbar`, `createAllSnackbarsContainer` from `@maltjoy/core-vue` |
18
+ | **Design** | [ZeroHeight — Snackbar](https://zeroheight.com/6bf479565/p/72e810-snackbar) |
19
+ | **Storybook** | [VJoySnackbar docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-overlays-vjoysnackbar--docs) |
20
+ | **DSDS (machine)** | [`JoySnackbar.dsds.json`](./JoySnackbar.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoySnackbar")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Show a snackbar for **short, contextual feedback** that does not block the current task — for example confirming that settings were saved, reporting a recoverable API error, or offering a single follow-up action (undo, retry, view details).
28
+
29
+ Snackbars auto-dismiss after a timeout (default 5 seconds) unless you set `duration` to `"forever"` or the user closes them.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Inline, persistent helper or field-level error | `VJoyHighlight` |
36
+ | Blocking confirmation or multi-step decision | `VJoyDialog` |
37
+ | Page-level error that must stay until resolved | `VJoyHighlight` (error level) or page-level messaging — not a transient snackbar |
38
+ | Long-form or multi-paragraph content | A dialog, drawer, or dedicated page section |
39
+ | Custom fixed toast / banner markup | Still use `pushVJoySnackbar` + Joy tokens — don't reinvent overlay chrome |
40
+
41
+ > **Not in the API:** `level="neutral"` is not supported (`TJoySnackbarLevels` excludes `neutral`). Use `info` for neutral informational feedback.
42
+
43
+ ---
44
+
45
+ ## How it works
46
+
47
+ ### Service flow
48
+
49
+ 1. **`SnackbarPlugin`** (installed with the app) calls `createAllSnackbarsContainer()` on the client, injecting a fixed host (`#joy-snackbar__container`) at the bottom of the viewport.
50
+ 2. **`pushVJoySnackbar({ props })`** creates a dedicated Vue app per instance, attaches a `mitt` event bus, mounts into a shell inside the host, and returns `{ snackbarEventBus, snackbarApp }`.
51
+ 3. On hide (timeout, close click, or manual destroy), the bus emits `snackbar:hide`, which unmounts the app and removes the shell.
52
+
53
+ ### Anatomy
54
+
55
+ | Part | Required? | What it is |
56
+ | --- | --- | --- |
57
+ | Level icon | Yes | `VJoyIcon` in a colored badge — mapped from `level` (`check-circle`, `warning-triangle`, `info-circle`) |
58
+ | Header | No | Bold title (`header` prop) |
59
+ | Message | Yes | Main copy (`message` prop); rendered with `v-html` so inline links are supported |
60
+ | Action CTA | No | Text link styled CTA (`actionText`); emits `snackbar:action-click` on the bus |
61
+ | Close control | No* | Icon button (cross) when `closable` is true — *default is true |
62
+ | Level underline | Yes | 4px bottom bar colored by `level` token |
63
+
64
+ ### Default values
65
+
66
+ - `level` defaults to **`success`**
67
+ - `duration` defaults to **`5000`** (milliseconds)
68
+ - `closable` defaults to **`true`**
69
+ - `frozen` defaults to **`false`** (Storybook / docs only — skips enter/leave animation timing)
70
+
71
+ Props, events, and exposed members: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
72
+
73
+ ### Event bus
74
+
75
+ Each instance gets its own bus (you do not pass `bus` yourself):
76
+
77
+ | Event | When |
78
+ | --- | --- |
79
+ | `snackbar:show` | Emitted on mount |
80
+ | `snackbar:hide` | Emitted after close animation (~300ms after `isOpen` becomes false) |
81
+ | `snackbar:action-click` | Emitted when the user clicks `actionText` |
82
+
83
+ Unregister listeners when the calling component unmounts.
84
+
85
+ ---
86
+
87
+ ## Variants
88
+
89
+ In the API, visual emphasis is controlled by **`level`** (semantic severity), not a separate `variant` prop.
90
+
91
+ ### Success (`level="success"`)
92
+
93
+ Use when an action completed as expected — saved profile, sent message, file uploaded.
94
+
95
+ Avoid for errors or warnings; the green accent misleads.
96
+
97
+ ### Error (`level="error"`)
98
+
99
+ Use for failed actions, API errors, or critical issues the user should notice.
100
+
101
+ Avoid long explanations — keep the header/message short; link out for details if needed.
102
+
103
+ ### Warning (`level="warning"`)
104
+
105
+ Use for non-blocking conditions the user should be aware of (quota almost reached, partial save).
106
+
107
+ Avoid when the user must stop and fix something before continuing — prefer `VJoyHighlight` or inline validation.
108
+
109
+ ### Information (`level="info"`)
110
+
111
+ Use for neutral contextual updates (background sync, tips).
112
+
113
+ Avoid overusing info snackbars where static page content would suffice.
114
+
115
+ ### Not in the API: `neutral`
116
+
117
+ Design docs sometimes reference a neutral tone. The code type is `Exclude<TLevels, 'neutral'>`. Do not emit `level="neutral"` — use `info`.
118
+
119
+ ---
120
+
121
+ ## Sizes
122
+
123
+ The snackbar has a **single layout** — no `size` prop.
124
+
125
+ | Constraint | Value (implementation) |
126
+ | --- | --- |
127
+ | Desktop width | **580px** fixed (`width: 580px` on `.joy-snackbar` and host container) |
128
+ | Responsive max | `max-width: calc(100% - var(--joy-core-spacing-6) * 2)` |
129
+ | Padding | `var(--joy-core-spacing-4)` (16px) |
130
+ | Position | Fixed, bottom center of viewport (`bottom: 0`, horizontal center via `left`/`right`/`margin: auto`) |
131
+ | Z-index | `var(--joy-core-z-index-snackbar)` |
132
+
133
+ **Do not** hardcode `580px` or pixel heights in product CSS — the component owns layout. ZeroHeight mentions 375px mobile width; the implementation uses fluid max-width instead.
134
+
135
+ ---
136
+
137
+ ## States
138
+
139
+ | State | What the user sees / can do |
140
+ | --- | --- |
141
+ | Entering | Slides up from below (`showAnimation`, `--joy-transition-duration-long`) |
142
+ | Visible | Message (and optional header, action, close) readable; auto-dismiss timer running when `duration` is a number |
143
+ | Action hover | `actionText` underlines on hover |
144
+ | Closing (user) | Close icon click → `isOpen` false → hide animation → `snackbar:hide` |
145
+ | Closing (timeout) | After `duration` ms → hide animation → `snackbar:hide` ~300ms later |
146
+ | Persistent | `duration: "forever"` — no auto-dismiss; user must close if `closable` |
147
+ | Non-closable | `closable: false` — no close icon; rely on timeout or programmatic teardown |
148
+ | Frozen (docs) | `frozen: true` — near-instant transitions for Storybook screenshots |
149
+
150
+ ---
151
+
152
+ ## Best practices
153
+
154
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
155
+
156
+ ### When to show
157
+
158
+ - **SHOULD** use snackbars for **reversible or low-stakes** outcomes (saved, copied, added to favorites).
159
+ - **SHOULD** prefer a snackbar over a dialog when no explicit user decision is required.
160
+ - **MUST NOT** use snackbars for errors that block further progress on the page — use `VJoyHighlight` or inline errors instead.
161
+
162
+ ### Implementation
163
+
164
+ - **MUST** call `pushVJoySnackbar` — never place `<VJoySnackbar>` in app templates.
165
+ - **MUST** pass at least `props.message`.
166
+ - **MUST** use only documented `level` values from MCP / `JoySnackbar.types.ts` (`success`, `error`, `warning`, `info`).
167
+ - **MUST** use Joy tokens — no custom snackbar chrome CSS.
168
+ - **MUST NOT** invent props (`variant`, `neutral`, `position`, …).
169
+ - **MUST** unregister `snackbarEventBus` listeners on component destroy.
170
+ - **SHOULD** set `closable: true` when `actionText` or inline links are present (design guideline for accessibility).
171
+
172
+ ### Stacking
173
+
174
+ - Multiple calls to `pushVJoySnackbar` stack in the host container (newest shell appended).
175
+ - `disableStacking` exists on `IJoynackbarServiceOptions` but is **not implemented** in the service — do not rely on it.
176
+
177
+ ### Do / don't
178
+
179
+ | Do | Don't |
180
+ | --- | --- |
181
+ | `pushVJoySnackbar({ props: { message: '…' } })` | `<VJoySnackbar message="…" />` in templates |
182
+ | Match `level` to message meaning | Default everything to `success` |
183
+ | Short header + optional detail in `message` | Paragraphs of text |
184
+ | `actionText` + bus listener for undo/retry | Multiple CTAs (only one `actionText` slot) |
185
+ | `duration: "forever"` only when truly needed | Infinite snackbars for routine success toasts |
186
+
187
+ ---
188
+
189
+ ## Content
190
+
191
+ Snackbars carry user-facing copy. Keep tone neutral and didactic.
192
+
193
+ ### Structure
194
+
195
+ | Field | Role | Guidance |
196
+ | --- | --- | --- |
197
+ | `header` | Title (most visible) | Up to **5 words**; specific status, not generic labels |
198
+ | `message` | Body / subtitle | 1–2 short sentences (~90 characters); supports HTML links |
199
+ | `actionText` | Single CTA | Short, action-oriented verb phrase |
200
+
201
+ ### Header formula
202
+
203
+ **Subject + status** — let the `level` icon and color carry severity:
204
+
205
+ - ✅ Profile updated
206
+ - ✅ Payment failed
207
+ - ❌ Error
208
+ - ❌ Something went wrong
209
+ - ❌ Success
210
+ - ❌ Congrats
211
+
212
+ ### Writing rules
213
+
214
+ | Strength | Rule |
215
+ | --- | --- |
216
+ | **MUST** | Be specific — name what happened (`Invoice sent`, not `Done`) |
217
+ | **MUST** | Keep headers ≤ 5 words |
218
+ | **MUST NOT** | Generic titles (`Error`, `Success`, `Info`, `Congrats`) |
219
+ | **MUST NOT** | Redundant adverbs (`successfully saved`) — the success level already signals outcome |
220
+ | **SHOULD** | Use `message` only when it adds guidance or next steps |
221
+ | **SHOULD** | For error feedback, title alone is often enough (per design guidelines) |
222
+ | **SHOULD** | Sentence case for header and message |
223
+ | **SHOULD NOT** | Repeat the header verbatim in `message` |
224
+
225
+ ### Action CTA (`actionText`)
226
+
227
+ - **MUST** use a single, contextual verb (`Undo`, `Retry`, `View invoice`).
228
+ - **MUST NOT** use vague dismissals (`Got it`, `Ok`, `I understand`) — use the close icon instead.
229
+ - **SHOULD** use `actionText` for undo, retry, or navigation — not for merely acknowledging the toast.
230
+
231
+ ### Links in `message`
232
+
233
+ Inline `<a>` tags are supported (`v-html`). Use `VJoyLink` styling patterns (bold, neutral-0 color in the snackbar context).
234
+
235
+ - **SHOULD** pair links with `closable: true`.
236
+ - **SHOULD** link to FAQ, help, or an in-app destination — not duplicate the `actionText` job.
237
+
238
+ ### Patterns
239
+
240
+ **Save feedback** — `level: 'success'`, header names the object: `Brief saved`. Optional `message` only if you guide next steps.
241
+
242
+ **Recoverable error** — `level: 'error'`, specific header: `Upload failed`. `actionText: 'Retry'` with `snackbar:action-click` handler.
243
+
244
+ **Background info** — `level: 'info'`, short header; skip `actionText` unless there is a real follow-up.
245
+
246
+ ---
247
+
248
+ ## Accessibility
249
+
250
+ Behaviors (what must happen), not slogans.
251
+
252
+ | Behavior | Expectation |
253
+ | --- | --- |
254
+ | Role | Transient notification region — not a modal dialog |
255
+ | Name | Meaning comes from `header` + `message` text; avoid icon-only meaning |
256
+ | Keyboard | Close button is a native `<button>` and focusable when `closable` |
257
+ | Focus | Transient auto-dismiss snackbars should not trap focus; when `actionText` or links are present, **SHOULD** keep `closable: true` so users can dismiss explicitly |
258
+ | Motion | Enter/leave animations use `--joy-transition-duration-long`; respect `prefers-reduced-motion` at app level where implemented |
259
+ | Links | Inline links in `message` must have discernible text; don't rely on color alone |
260
+ | Timing | Default 5s may be short for long messages — increase `duration` proportionally to copy length |
261
+
262
+ Target: **WCAG 2.2 AA** where the product requires it.
263
+
264
+ ---
265
+
266
+ ## Examples
267
+
268
+ ```vue
269
+ <script setup lang="ts">
270
+ import { pushVJoySnackbar } from '@maltjoy/core-vue'
271
+
272
+ function notifySaved() {
273
+ pushVJoySnackbar({
274
+ props: {
275
+ level: 'success',
276
+ header: 'Profile updated',
277
+ message: 'Your changes are live.',
278
+ },
279
+ })
280
+ }
281
+
282
+ function retryUpload() {
283
+ // your retry logic
284
+ }
285
+
286
+ function notifyErrorWithRetry() {
287
+ const { snackbarEventBus } = pushVJoySnackbar({
288
+ props: {
289
+ level: 'error',
290
+ header: 'Upload failed',
291
+ message: 'Check your connection and try again.',
292
+ actionText: 'Retry',
293
+ },
294
+ })
295
+
296
+ snackbarEventBus.on('snackbar:action-click', retryUpload)
297
+ snackbarEventBus.on('snackbar:hide', () => {
298
+ snackbarEventBus.off('snackbar:action-click', retryUpload)
299
+ })
300
+ }
301
+
302
+ function persistentInfo() {
303
+ pushVJoySnackbar({
304
+ props: {
305
+ level: 'info',
306
+ header: 'Sync in progress',
307
+ message: 'Large files may take a few minutes.',
308
+ duration: 'forever',
309
+ closable: true,
310
+ },
311
+ })
312
+ }
313
+ </script>
314
+ ```
315
+
316
+ Custom mount (tests / Storybook only):
317
+
318
+ ```typescript
319
+ import { createAllSnackbarsContainer, pushVJoySnackbar } from '@maltjoy/core-vue'
320
+
321
+ createAllSnackbarsContainer({
322
+ appendTo: '#my-canvas',
323
+ defaultMountSelector: 'snackbar-host',
324
+ })
325
+
326
+ pushVJoySnackbar({
327
+ snackbarHostId: 'snackbar-host',
328
+ props: { message: 'Hello from a custom host' },
329
+ })
330
+ ```
331
+
332
+ In Nuxt (or apps with auto-import), omit the import if `pushVJoySnackbar` is already global.
333
+
334
+ ---
335
+
336
+ ## Related
337
+
338
+ | Component | Use when |
339
+ | --- | --- |
340
+ | `VJoyHighlight` | Inline, persistent contextual messages or field-level errors |
341
+ | `VJoyDialog` | Blocking confirmations or multi-field input |
342
+ | `VJoyLink` | Inline navigation patterns referenced inside `message` HTML |
343
+ | `VJoyButton` | Primary actions on the page — not inside the snackbar API |
344
+
345
+ ---
346
+
347
+ ## For agents
348
+
349
+ Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoySnackbar.dsds.json`](./JoySnackbar.dsds.json).
350
+
351
+ ### Pick the right component
352
+
353
+ | If the intent is… | Emit… | Do not emit… |
354
+ | --- | --- | --- |
355
+ | Brief post-action feedback | `pushVJoySnackbar({ props: { message } })` | `<VJoySnackbar>` in template |
356
+ | Field / section error blocking work | `VJoyHighlight` | Snackbar with `level="error"` |
357
+ | Confirm destructive action | `VJoyDialog` | Snackbar with only `actionText` |
358
+ | Neutral info tone | `level: 'info'` | `level: 'neutral'` (invalid) |
359
+ | Undo / retry | `actionText` + `snackbar:action-click` listener | Second button or invented slot |
360
+
361
+ ### Hard rules
362
+
363
+ 1. **MUST** call MCP (or read types) before generating props.
364
+ 2. **MUST** use `pushVJoySnackbar` — never render `<VJoySnackbar>` directly in product code.
365
+ 3. **MUST** include `props.message` (required).
366
+ 4. **MUST NOT** invent props (`variant`, `size`, `neutral`, `disableStacking` behavior, …).
367
+ 5. **MUST NOT** pass `bus` or `instanceNb` — the service sets them.
368
+ 6. **MUST NOT** override `.joy-snackbar` layout, colors, or animation in app CSS.
369
+ 7. **SHOULD** default `level` to `success` only for genuine success; match severity otherwise.
370
+ 8. **SHOULD** set `closable: true` when `actionText` or links are present.
371
+ 9. **SHOULD** clean up `snackbarEventBus.on` handlers in `onUnmounted`.
372
+
373
+ ### Checklist
374
+
375
+ - [ ] `pushVJoySnackbar` (not template component)
376
+ - [ ] `props.message` present
377
+ - [ ] `level` from `success` \| `error` \| `warning` \| `info` only
378
+ - [ ] No invented props / no custom snackbar chrome CSS
379
+ - [ ] Event bus listeners removed on destroy
380
+ - [ ] Header ≤ 5 words, not generic (`Error`, `Success`, …)
381
+ - [ ] `actionText` is action-oriented (not `Ok` / `Got it`)
382
+ - [ ] Inline errors that block the flow → `VJoyHighlight`, not snackbar
383
+
384
+ ---
385
+
386
+ ## DSDS mapping
387
+
388
+ This Markdown mirrors [`JoySnackbar.dsds.json`](./JoySnackbar.dsds.json) (DSDS **0.15.2**).
389
+
390
+ | Section here | DSDS block |
391
+ | --- | --- |
392
+ | When to use / not | `use-cases` |
393
+ | Anatomy / service flow | `anatomy` |
394
+ | Levels (`level`) | `variants` |
395
+ | Layout constraints | `design-specifications` |
396
+ | States | `states` |
397
+ | Best practices / content rules | `guidelines` (+ `content`) |
398
+ | Accessibility | `accessibility` |
399
+ | Examples | `sections` |
400
+ | For agents | `agentDocumentBlocks` |
401
+ | Props detail | `api` in JSON + Joy MCP |
402
+ | Service imports | `imports` in JSON (`pushVJoySnackbar`, `createAllSnackbarsContainer`) |
@@ -0,0 +1,168 @@
1
+ ---
2
+ component: VJoySpinner
3
+ status: draft
4
+ language: en
5
+ dsds: JoySpinner.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Spinner
10
+
11
+ Use **`VJoySpinner`** when you need a compact, animated loading indicator — for example inside a button, a form field area, or a localized content block while data is being fetched or an action is in progress.
12
+
13
+ It is a lightweight CSS-only spinner with two color presets (`teal`, `white`) for light and dark backgrounds. For full-page or section-blocking loaders with overlay and optional message, prefer **`VJoyScreenLoader`** or built-in **`loading`** on **`VJoyButton`** / **`VJoyIconButton`**.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoySpinner` from `@maltjoy/core-vue` |
18
+ | **Design** | [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
19
+ | **Storybook** | [VJoySpinner docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-loader-vjoyspinner--docs) |
20
+ | **DSDS (machine)** | [`JoySpinner.dsds.json`](./JoySpinner.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoySpinner")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a spinner when the user should see that work is **in progress** in a small, inline context — a button submitting a form, a card body waiting for API data, or a custom loader slot inside **`VJoyScreenLoader`**.
28
+
29
+ Pick **`color="white"`** on filled or dark surfaces (primary/danger buttons, dark overlays). Use **`color="teal"`** (or omit `color`) on light backgrounds.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Button or icon button in a loading state | `VJoyButton` / `VJoyIconButton` with `loading` — spinner color is chosen automatically |
36
+ | Full-page or section overlay with optional message | `VJoyScreenLoader` |
37
+ | Placeholder layout while content shape is unknown | `VJoyBlockSkeleton`, `VJoyFormFieldSkeleton` |
38
+ | Determinate progress (percentage or steps) | `VJoyProgressBar` |
39
+ | Custom animated GIF / ad-hoc CSS spinner | Still use `VJoySpinner` + Joy tokens — don’t reinvent loader chrome |
40
+
41
+ ---
42
+
43
+ ## How it works
44
+
45
+ ### Anatomy
46
+
47
+ | Part | Required? | What it is |
48
+ | --- | --- | --- |
49
+ | Root `.joy-spinner` | Yes | Single `div`; circular border animation |
50
+ | Color modifier | No | `joy-spinner_white` when `color="white"`; default tokens when `color` is omitted or `teal` |
51
+
52
+ No slots, events, or child elements. The component is purely presentational.
53
+
54
+ ### Default values
55
+
56
+ - `color` is **optional** — when omitted, default Joy tokens apply (`secondary-50` / `secondary-10`, ~25px size)
57
+ - Storybook defaults `color` to **`teal`**
58
+
59
+ Sizing and fine-tuning use CSS custom properties on the root (or a positioned parent), not props:
60
+
61
+ - `--spinner-size` — diameter (default **25px**)
62
+ - `--spinner-default-color` — active arc color
63
+ - `--spinner-default-bg-color` — track color
64
+
65
+ Props and types: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
66
+
67
+ ### Positioning
68
+
69
+ The spinner uses **`position: absolute`** in its base styles. Place it inside a **relatively positioned** container with enough space for the spinner size, or override positioning in a parent wrapper (as **`VJoyScreenLoader`** does with `position: relative` on `.joy-spinner`).
70
+
71
+ ---
72
+
73
+ ## Best practices
74
+
75
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
76
+
77
+ ### When to show a spinner
78
+
79
+ - **SHOULD** use **`VJoyButton` `loading`** instead of embedding `VJoySpinner` manually in buttons.
80
+ - **SHOULD** pair inline spinners with **visible context** (button label, section heading, or `VJoyScreenLoader` content slot) so the wait state is understandable.
81
+ - **MUST NOT** use a spinner as the only loading feedback on a full view — use **`VJoyScreenLoader`** or skeletons when the whole region is empty.
82
+
83
+ ### Implementation
84
+
85
+ - **MUST** use only documented `color` values: **`teal`**, **`white`** (from MCP / `TJoySpinnerColors`).
86
+ - **MUST** tune size via **`--spinner-size`** (and token-based color vars if needed) — not hardcoded width/height in product CSS.
87
+ - **MUST NOT** invent props (`size`, `variant`, `label`, …).
88
+ - **MUST NOT** override `.joy-spinner` border animation or colors with one-off app CSS — use `color` or documented CSS variables.
89
+ - **SHOULD** set `color="white"` on dark or filled primary/danger surfaces; `teal` on light backgrounds.
90
+
91
+ ### Do / don’t
92
+
93
+ | Do | Don’t |
94
+ | --- | --- |
95
+ | `VJoyButton` `loading` for submit actions | Manual spinner + disabled button without `loading` |
96
+ | `color="white"` on dark overlays / filled buttons | Teal spinner on dark blue button fill |
97
+ | `--spinner-size` in a sized container | Fixed `px` spinner div with custom `@keyframes` |
98
+ | `VJoyScreenLoader` for blocking section load | Lone spinner centered on an empty page with no message |
99
+
100
+ ---
101
+
102
+ ## Accessibility
103
+
104
+ Behaviors (what must happen), not slogans.
105
+
106
+ | Behavior | Expectation |
107
+ | --- | --- |
108
+ | Role | Decorative motion only — **no** implicit `role="status"` or live region on `VJoySpinner` itself |
109
+ | Name | **MUST** expose progress in a **parent** control or region: e.g. `aria-busy="true"` on the loading button, `aria-live` on `VJoyScreenLoader` content, or visible “Loading…” copy |
110
+ | Keyboard | Spinner does not receive focus; ensure the trigger stays focusable or clearly disabled while loading |
111
+ | Motion | Continuous rotation (~1.1s linear); respect `prefers-reduced-motion` at app level if you add global animation overrides |
112
+ | Color | Don’t rely on motion alone — pair with text or `aria-busy` on the relevant widget |
113
+
114
+ Target: **WCAG 2.2 AA** where the product requires it.
115
+
116
+ ---
117
+
118
+ ## Examples
119
+
120
+ ```vue
121
+ <script setup lang="ts">
122
+ import { VJoySpinner } from '@maltjoy/core-vue'
123
+ </script>
124
+
125
+ <template>
126
+ <!-- Default — light background -->
127
+ <div style="position: relative; min-height: 48px">
128
+ <VJoySpinner />
129
+ </div>
130
+
131
+ <!-- Explicit teal on light surface -->
132
+ <div style="position: relative; min-height: 48px">
133
+ <VJoySpinner color="teal" />
134
+ </div>
135
+
136
+ <!-- White on dark / filled surface -->
137
+ <div
138
+ style="position: relative; min-height: 48px; background: var(--joy-color-secondary-50)"
139
+ >
140
+ <VJoySpinner color="white" />
141
+ </div>
142
+
143
+ <!-- Custom size via token (parent or :style on wrapper) -->
144
+ <div style="position: relative; min-height: 64px; --spinner-size: 40px">
145
+ <VJoySpinner color="teal" />
146
+ </div>
147
+ </template>
148
+ ```
149
+
150
+ Prefer **`VJoyButton loading`** over manual spinner placement in buttons:
151
+
152
+ ```vue
153
+ <VJoyButton variant="main" label="Save" :loading="isSaving" />
154
+ ```
155
+
156
+ In Nuxt (or apps with auto-import), omit the import if `VJoySpinner` is already global.
157
+
158
+ ---
159
+
160
+ ## Related
161
+
162
+ | Component | Use when |
163
+ | --- | --- |
164
+ | `VJoyButton` / `VJoyIconButton` | Async action with built-in spinner via `loading` |
165
+ | `VJoyScreenLoader` | Overlay or centered section loader with optional message |
166
+ | `VJoyDropzone` | File upload processing (embeds spinner internally) |
167
+ | `VJoyBlockSkeleton` / `VJoyFormFieldSkeleton` | Content-shaped placeholders instead of spinners |
168
+ | `VJoyProgressBar` | Known or estimable completion progress |