@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,327 @@
1
+ ---
2
+ component: VJoyToggle
3
+ status: draft
4
+ language: en
5
+ dsds: JoyToggle.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Toggle
10
+
11
+ Use **`VJoyToggle`** when the user needs to switch a single setting **on or off** with immediate effect — especially in settings rows, preferences, or compact forms.
12
+
13
+ It supports checked and disabled states, a label via prop or slot (left or right of the switch), native form attributes (`name`, `value`, `required`), and two visual variants: **`default`** (customer product) and **`admin`** (internal tooling).
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyToggle` from `@maltjoy/core-vue` |
18
+ | **Design** | [Joy — Toggle](https://zeroheight.com/6bf479565/p/67497f) (page id `1423634`) |
19
+ | **Storybook** | [VJoyToggle docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoytoggle--docs) |
20
+ | **DSDS (machine)** | [`JoyToggle.dsds.json`](./JoyToggle.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyToggle")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a toggle when there are **exactly two mutually exclusive states** (on / off) that the user may change often and see take effect right away — for example enabling notifications, showing optional UI, or turning a feature on.
28
+
29
+ Toggles work well when space is tight and the two states are clearly related. Place the label beside the switch so the user knows what flips when they interact with the control.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | More than two options | `VJoyRadio` / `VJoyRadioGroup` or `VJoySelect` |
36
+ | Multiple independent selections | `VJoyCheckbox` / `VJoyCheckboxGroup` |
37
+ | Consent, agreement, or “I accept…” copy | `VJoyCheckbox` |
38
+ | Options that are not mutually exclusive | `VJoyCheckbox` |
39
+ | A change that only applies after Save / Submit | Checkbox or explicit submit — not an instant toggle |
40
+ | Customer-facing product UI with admin chrome | `variant="default"` — never `admin` |
41
+ | Custom-styled switch markup | Still use `VJoyToggle` + Joy tokens — don’t reinvent chrome |
42
+
43
+ ---
44
+
45
+ ## How it works
46
+
47
+ ### Anatomy
48
+
49
+ | Part | Required? | What it is |
50
+ | --- | --- | --- |
51
+ | Wrapper | Yes | Root container (`.joy-toggle__wrapper`) |
52
+ | Label | Yes | Clickable `<label>` wrapping the input and content row |
53
+ | Input | Yes | Visually hidden `<input type="checkbox">` with `data-joy-form-field` and `role="checkbox"` |
54
+ | Switch track | Yes | `.joy-toggle__symbol` — rounded track behind the bubble |
55
+ | Bubble | Yes | `.joy-toggle__symbol-bubble` — slides horizontally when checked |
56
+ | Variant icon | No | Smile SVG (`default`) or `VJoyIcon` `eye` (`admin`) inside the bubble |
57
+ | Label text | Yes* | Default slot or `label` prop in `.joy-toggle__content-label` (*one must provide an accessible name) |
58
+ | Focus ring | Yes | Outline on the track when the input is focused (`joy-toggle__focusing`) |
59
+
60
+ The component `provide`s `JoyFormElementContext` so it integrates with Joy form field patterns (e.g. labels, validation groups).
61
+
62
+ ### Default values
63
+
64
+ - `modelValue` defaults to **`false`**
65
+ - `variant` defaults to **`default`**
66
+ - `labelPosition` defaults to **`right`**
67
+ - `disabled` defaults to **`false`**
68
+ - `invalid` defaults to **`false`** (declared on props; no dedicated invalid styling in component CSS yet — pair with `VJoyFormError` or group messaging)
69
+ - `required` defaults to **`false`**
70
+ - `value` defaults to **`"on"`** (submitted value when checked in native forms)
71
+
72
+ Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
73
+
74
+ ### Visual treatment
75
+
76
+ There is **no `size` prop**. Track and bubble dimensions come from component CSS variables (e.g. `--toggle-width`, `--toggle-height`) and Joy color tokens — **don’t hardcode switch width, height, or track colors** in product CSS.
77
+
78
+ ---
79
+
80
+ ## Variants
81
+
82
+ Pick variant by **surface audience**, not by color preference.
83
+
84
+ ### `default`
85
+
86
+ Use on **customer-facing** product UI. The bubble shows the Joy smile motif when checked.
87
+
88
+ Avoid `admin` styling on public or client surfaces.
89
+
90
+ ### `admin`
91
+
92
+ Use **only** in internal / admin tooling. Checked state uses quaternary tokens; the bubble shows an **eye** icon instead of the smile.
93
+
94
+ Avoid `variant="admin"` outside admin contexts — it is documented separately in Storybook under Admin.
95
+
96
+ ---
97
+
98
+ ## States
99
+
100
+ | State | What the user sees / can do |
101
+ | --- | --- |
102
+ | Off (`modelValue: false`) | Bubble on the left; neutral track; label in normal weight |
103
+ | On (`modelValue: true`) | Bubble slides right; track uses secondary (or quaternary for `admin`); smile or eye visible in bubble |
104
+ | Hover | Track and bubble border lighten (pointer devices); checked hover uses `--toggle-bg-color-hover-checked` |
105
+ | Focus | Visible focus outline on the track — don’t remove in product CSS |
106
+ | Disabled | No interaction; muted track, bubble, label, and icon — **explain why** nearby when possible |
107
+ | Disabled + on | Same as disabled with bubble in the “on” position |
108
+
109
+ Bind state with `v-model` (or `:model-value` + `@update:modelValue`). Changes emit immediately on `change` — there is no separate “Apply” step inside the component.
110
+
111
+ ---
112
+
113
+ ## Best practices
114
+
115
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
116
+
117
+ ### Selection model
118
+
119
+ - **SHOULD** use a toggle only for **two** mutually exclusive, immediately applied states.
120
+ - **MUST NOT** use a toggle when the user can select multiple options — use checkboxes.
121
+ - **MUST NOT** use a toggle when more than two choices exist — use radio or select.
122
+ - **SHOULD** prefer `VJoyCheckbox` when the label is a consent statement (“I agree to…”) rather than a setting name.
123
+
124
+ ### Implementation
125
+
126
+ - **MUST** bind state with `v-model` (or `:model-value` + `@update:modelValue`).
127
+ - **MUST** use only documented props from MCP or `VJoyToggle.vue` props.
128
+ - **MUST** use Joy tokens — no custom `.joy-toggle` track, bubble, or focus CSS.
129
+ - **MUST NOT** invent props (`size`, `color`, `checked` as v-model name, …).
130
+ - **MUST** use `variant="default"` on customer surfaces; reserve `admin` for internal tools.
131
+ - **SHOULD** set `name` and `value` when multiple toggles participate in the same native form submit.
132
+ - **SHOULD** surface validation errors with `VJoyFormError` or `VJoyFormGroup` — don’t rely on `invalid` alone until invalid styling is wired.
133
+
134
+ ### Interaction
135
+
136
+ - **MUST** keep the label clickable (component wraps input in `<label>` — don’t break that association).
137
+ - **SHOULD** use `labelPosition="left"` when the switch must align with a column of controls on the right (e.g. settings tables).
138
+ - **SHOULD** disable rather than hide a setting the user cannot change, when the setting must remain visible.
139
+
140
+ ### Do / don’t
141
+
142
+ | Do | Don’t |
143
+ | --- | --- |
144
+ | `v-model` for on/off state | Separate Save button for a single instant setting (unless product requires it) |
145
+ | Short setting names as labels | Full sentences that read like checkboxes (“I agree…”) |
146
+ | `default` on product UI | `admin` variant on customer flows |
147
+ | `VJoyCheckbox` for consent | Toggle for legal acceptance |
148
+ | Radio / select for 3+ options | Toggle as a multi-option picker |
149
+
150
+ ---
151
+
152
+ ## Content
153
+
154
+ Toggle labels name **what is enabled when the switch is on**. They are scannable, parallel in lists, and use the **infinitive** mood in English (per Joy conjugation guidelines).
155
+
156
+ ### Label formula
157
+
158
+ **Infinitive verb + optional object** (English):
159
+
160
+ - ✅ Enable notifications
161
+ - ✅ Show completed projects
162
+ - ✅ Use dark mode
163
+
164
+ The label describes the **on** state. Off is implied.
165
+
166
+ ### Writing rules
167
+
168
+ | Strength | Rule |
169
+ | --- | --- |
170
+ | **MUST** | Every toggle has a visible label (prop or default slot) — no unlabeled switches |
171
+ | **MUST** | Label states what is on when checked — ✅ `Enable email alerts` · ❌ `Email alerts are enabled` |
172
+ | **MUST** | Use infinitive mood (EN) — ✅ `Show profile` · ❌ `Showing profile` |
173
+ | **MUST NOT** | Use Yes / No as the only label — name the setting |
174
+ | **MUST NOT** | Use checkbox-style agreement copy — ✅ move to `VJoyCheckbox` |
175
+ | **SHOULD** | ≤ 40 characters; wrap rather than truncate |
176
+ | **SHOULD** | Sentence case; parallel grammar in stacked settings |
177
+ | **SHOULD** | Avoid “Turn on/off” padding when the infinitive alone is clear — ✅ `Enable notifications` |
178
+ | **SHOULD** (FR) | Follow product locale rules; infinitive is still preferred for toggle labels |
179
+
180
+ ### Patterns
181
+
182
+ **Settings row** — One toggle per row; label on the right by default. Group related toggles under a `VJoyLabel` or section heading.
183
+
184
+ **Dialog / panel** — Use when the choice is binary and applies immediately (e.g. “Show archived items”). Prefer explicit labels over “On / Off” alone.
185
+
186
+ **Forms** — If the form is submitted as a whole, still bind `v-model`; set `name` / `value` so unchecked toggles behave as expected in native submit (checkbox semantics).
187
+
188
+ **Validation** — When a toggle must be on to proceed, show error copy next to the control (`VJoyFormError`) rather than only relying on border color.
189
+
190
+ ---
191
+
192
+ ## Accessibility
193
+
194
+ Behaviors (what must happen), not slogans.
195
+
196
+ | Behavior | Expectation |
197
+ | --- | --- |
198
+ | Role | Native checkbox (`<input type="checkbox">`) with `role="checkbox"` |
199
+ | Name | From default slot or `label` prop; associated via wrapping `<label>` |
200
+ | State | `aria-checked` reflects `modelValue` |
201
+ | Keyboard | **Space** toggles when focused; **Tab** moves focus |
202
+ | Focus | Visible focus ring on the track (`joy-toggle__focusing`); don’t remove in product CSS |
203
+ | Disabled | `disabled` on input; explain restriction in visible text when it matters |
204
+ | Motion | Bubble animates with `--joy-transition-duration-default`; respect `prefers-reduced-motion` at app level if you add wrappers |
205
+
206
+ Target: **WCAG 2.2 AA** where the product requires it.
207
+
208
+ ---
209
+
210
+ ## Examples
211
+
212
+ ```vue
213
+ <script setup lang="ts">
214
+ import { ref } from 'vue'
215
+ import { VJoyToggle } from '@maltjoy/core-vue'
216
+
217
+ const notifications = ref(true)
218
+ const showArchived = ref(false)
219
+ </script>
220
+
221
+ <template>
222
+ <!-- Basic setting (label right) -->
223
+ <VJoyToggle v-model="notifications" label="Enable notifications" />
224
+
225
+ <!-- Label on the left (settings table alignment) -->
226
+ <VJoyToggle
227
+ v-model="showArchived"
228
+ label="Show archived projects"
229
+ label-position="left"
230
+ />
231
+
232
+ <!-- Custom label slot -->
233
+ <VJoyToggle v-model="showArchived">
234
+ Show <strong>archived</strong> projects
235
+ </VJoyToggle>
236
+
237
+ <!-- Disabled -->
238
+ <VJoyToggle disabled :model-value="true" label="Managed by your organization" />
239
+
240
+ <!-- Native form field -->
241
+ <VJoyToggle
242
+ v-model="notifications"
243
+ name="email_alerts"
244
+ value="enabled"
245
+ label="Enable email alerts"
246
+ />
247
+
248
+ <!-- Admin tooling only -->
249
+ <VJoyToggle variant="admin" v-model="showArchived" label="Admin view" />
250
+ </template>
251
+ ```
252
+
253
+ In Nuxt (or apps with auto-import), omit the import if `VJoyToggle` is already global.
254
+
255
+ ---
256
+
257
+ ## Related
258
+
259
+ | Component | Use when |
260
+ | --- | --- |
261
+ | `VJoyCheckbox` | Consent, multi-select, or non-immediate binary confirmation |
262
+ | `VJoyCheckboxGroup` | Several independent checkboxes with shared legend |
263
+ | `VJoyRadio` / `VJoyRadioGroup` | Exactly one of three or more visible options |
264
+ | `VJoySelect` | Many options or searchable lists |
265
+ | `VJoyLabel` / `VJoyFormGroup` | Section headings, required marks, and form-level validation messaging |
266
+ | `VJoyFormError` | Explicit error text when validation fails |
267
+
268
+ ---
269
+
270
+ ## For agents
271
+
272
+ Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyToggle.dsds.json`](./JoyToggle.dsds.json).
273
+
274
+ ### Pick the right component
275
+
276
+ | If the intent is… | Emit… | Do not emit… |
277
+ | --- | --- | --- |
278
+ | Single on/off setting, immediate | `VJoyToggle` | `VJoyCheckbox` for a settings row |
279
+ | “I agree to terms” | `VJoyCheckbox` | `VJoyToggle` |
280
+ | Pick one of 3+ options | `VJoyRadio` / `VJoySelect` | `VJoyToggle` |
281
+ | Multi-select filters | `VJoyCheckbox` / `VJoyCheckboxGroup` | `VJoyToggle` |
282
+ | Customer product UI | `VJoyToggle` `variant="default"` | `variant="admin"` |
283
+ | Internal admin surface | `VJoyToggle` `variant="admin"` | `admin` on public pages |
284
+
285
+ ### Hard rules
286
+
287
+ 1. **MUST** read `VJoyToggle.vue` props (or MCP when available) before generating props — `joy_get_component_info("VJoyToggle")` may not be registered yet.
288
+ 2. **MUST** use `v-model` / `:model-value` + `@update:modelValue` — not `v-model:checked`.
289
+ 3. **MUST NOT** invent props — there is no `size`, `checked` v-model alias, or `color`.
290
+ 4. **MUST NOT** override `.joy-toggle` track, bubble, or focus styles with custom CSS.
291
+ 5. **MUST NOT** use `variant="admin"` outside admin / internal tooling.
292
+ 6. **MUST** provide a visible label (prop or default slot) on every instance.
293
+ 7. **SHOULD** use infinitive labels (EN): `Enable notifications`, not `Notifications enabled`.
294
+ 8. **SHOULD** use `label-position="left"` only when layout requires label-before-switch alignment.
295
+
296
+ ### Checklist
297
+
298
+ - [ ] Correct component (`VJoyToggle` or named alternative)
299
+ - [ ] Props from source / MCP only — no invented API
300
+ - [ ] `v-model` wired for boolean on/off state
301
+ - [ ] Visible label on every instance
302
+ - [ ] `variant="default"` on customer surfaces
303
+ - [ ] No custom `.joy-toggle` chrome CSS
304
+ - [ ] 3+ options → radio or select, not toggle
305
+ - [ ] Consent copy → checkbox, not toggle
306
+ - [ ] Disabled / validation explained in visible text when user-facing
307
+ - [ ] `name` / `value` set when needed for native form submit
308
+
309
+ ---
310
+
311
+ ## DSDS mapping
312
+
313
+ This Markdown mirrors [`JoyToggle.dsds.json`](./JoyToggle.dsds.json) (DSDS **0.15.2**).
314
+
315
+ | Section here | DSDS block |
316
+ | --- | --- |
317
+ | When to use / not | `use-cases` |
318
+ | Anatomy | `anatomy` |
319
+ | Variants | `variants` |
320
+ | States | `states` |
321
+ | Best practices / content rules | `guidelines` (+ `content`) |
322
+ | Accessibility | `accessibility` |
323
+ | Examples | `sections` |
324
+ | For agents | `agentDocumentBlocks` |
325
+ | Props detail | `api` in JSON + Joy MCP |
326
+ | Imports | `imports` in JSON |
327
+ | Token sizing | `design-specifications` in JSON (token names, not raw px dumps) |
@@ -0,0 +1,322 @@
1
+ ---
2
+ component: VJoyTooltip
3
+ status: draft
4
+ language: en
5
+ dsds: JoyTooltip.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Tooltip
10
+
11
+ Use **`VJoyTooltip`** when you need brief, contextual help on hover or click — for example naming an icon-only control, explaining why something is disabled, or clarifying a field label.
12
+
13
+ The tooltip wraps a trigger (default slot), teleports its panel to `body`, positions itself with Floating UI (auto-flip when space is tight), and exposes `role="tooltip"` with `aria-describedby` on the trigger.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyTooltip` from `@maltjoy/core-vue` |
18
+ | **Design** | [ZeroHeight — Tooltip](https://zeroheight.com/6bf479565/p/137c09-tooltip) |
19
+ | **Storybook** | [VJoyTooltip docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-overlays-vjoytooltip--docs) |
20
+ | **DSDS (machine)** | [`JoyTooltip.dsds.json`](./JoyTooltip.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyTooltip")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a tooltip to reveal **short, supplementary** information tied to a single trigger — without pulling the user out of their current task. Typical jobs: label an icon button, explain a disabled control, or add optional detail to a form label (`VJoyLabel` uses this pattern).
28
+
29
+ Default interaction is **hover** (`event="mouseenter"`). Switch to **click** when hover is unreliable (touch-first surfaces) or when the tooltip must stay open while the user reads or interacts with its content.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Information the user must read or act on | `VJoyDialog`, `VJoyDrawer`, or inline helper text |
36
+ | A list of actions or navigation | `VJoyMenu`, `VJoyDropdown` |
37
+ | Persistent page guidance or onboarding | Product tour / coachmark patterns (see Information helper guidelines) |
38
+ | Toast-style feedback after an action | `pushVJoySnackbar` |
39
+ | A custom floating panel with ad-hoc CSS | Still use `VJoyTooltip` + Joy tokens — don’t reinvent overlay chrome |
40
+
41
+ > **Deprecated:** `enableClickInContent` — behavior is already the default (`true`) and the prop will be removed. Omit it in new code unless you explicitly need the legacy `false` behavior.
42
+
43
+ > **Removed from API:** `variant` (`primary` / `secondary`) existed on the legacy Stencil tooltip. **`VJoyTooltip` has no `variant` prop.** Do not emit `variant="primary"` or `variant="secondary"`.
44
+
45
+ ---
46
+
47
+ ## How it works
48
+
49
+ ### Anatomy
50
+
51
+ | Part | Required? | What it is |
52
+ | --- | --- | --- |
53
+ | Click-outside wrapper | Yes | `OnClickOutside` root — dismisses click-triggered tooltips |
54
+ | Trigger wrapper | Yes | Wraps the default slot; holds pointer/keyboard listeners and `aria-describedby` |
55
+ | Trigger (default slot) | Yes | The control or element the user hovers/clicks (e.g. `VJoyIconButton`) |
56
+ | Tooltip panel | Yes* | Teleported to `body`; `role="tooltip"`; shown when visible |
57
+ | Arrow | Yes | Positioned by Floating UI middleware |
58
+ | Content inner | Yes* | `tooltip-content` slot or `text` prop output |
59
+
60
+ \*The panel is not rendered unless `text` or the `tooltip-content` slot is provided.
61
+
62
+ ### Default values
63
+
64
+ - `placement` defaults to **`bottom`**
65
+ - `event` defaults to **`mouseenter`**
66
+ - `delay` defaults to **`100`** (ms, show/hide)
67
+ - `tooltipWidth` defaults to **`250`** (px `max-width` on the panel)
68
+ - `enableClickInContent` defaults to **`true`**
69
+ - `show` defaults to **`false`** (set `true` mainly in Storybook / visual tests)
70
+
71
+ Props, slots, and exposed members: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
72
+
73
+ ### Positioning and triggers
74
+
75
+ - **Placement** — any [Floating UI placement](https://floating-ui.com/docs/tutorial#placements) (`top`, `bottom`, `left`, `right`, and `*-start` / `*-end`). The component **flips** to `bottom`, `top`, `left`, or `right` when there isn’t enough room.
76
+ - **Hover (default)** — show on `mouseenter`, hide on `mouseleave`. Clicks alone do not open the tooltip.
77
+ - **Click** — set `event="click"`. Opens on trigger click; closes on outside click or **Tab** away from the trigger. With `enableClickInContent`, clicks inside the tooltip content do not dismiss it.
78
+
79
+ ---
80
+
81
+ ## Variants
82
+
83
+ `VJoyTooltip` has **no visual variant prop**. Styling is fixed: neutral background (`--joy-color-neutral-10`), body text (`--joy-color-neutral-60`), `primary-300` typography, `--joy-core-radius-3`, and `--joy-core-elevation-3`.
84
+
85
+ ### Not in the API: `primary` / `secondary` (legacy Stencil)
86
+
87
+ ZeroHeight and the legacy `@maltjoy/core` tooltip documented `variant="primary"` (information blue) and `variant="secondary"` (neutral). These values are **not** on `VJoyTooltip`. Do not generate them. If product needs an information-colored tooltip, confirm with design before adding a new API value.
88
+
89
+ ---
90
+
91
+ ## Sizes
92
+
93
+ Single visual size — no `size` prop. Typography and padding come from Joy tokens inside the component (`primary-300`, `--joy-core-spacing-4`).
94
+
95
+ | Control | Use when |
96
+ | --- | --- |
97
+ | Default width | Short labels and icon explanations |
98
+ | `tooltipWidth` | Longer copy that should wrap within a narrower or wider column (value is `max-width` in px) |
99
+
100
+ **Don’t** hardcode font sizes or padding on `.joy-tooltip__content` in product CSS.
101
+
102
+ ---
103
+
104
+ ## States
105
+
106
+ | State | What the user sees / can do |
107
+ | --- | --- |
108
+ | Hidden (default) | Only the trigger is visible |
109
+ | Delayed show | After `delay` ms, panel fades in (hover or click path) |
110
+ | Visible | Tooltip panel + arrow; describes the trigger for assistive tech |
111
+ | Hiding | Opacity animation when dismissed (`mouseleave`, outside click, or Tab) |
112
+ | Click + open | With `event="click"`, panel stays until outside click or Tab; content may be clickable when `enableClickInContent` is true |
113
+ | `show` (dev) | Forces visible on mount — for Storybook/tests, not production UX |
114
+
115
+ ---
116
+
117
+ ## Best practices
118
+
119
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
120
+
121
+ ### When to show a tooltip
122
+
123
+ - **SHOULD** add a tooltip on **icon-only** controls when the icon alone is not universally understood (see Button content guidelines).
124
+ - **SHOULD** explain **why** a nearby control is disabled (forms pattern: disabled submit + tooltip).
125
+ - **SHOULD** keep tooltips **supplementary** — the interface must remain usable if the tooltip never appears.
126
+ - **MUST NOT** put essential instructions, links, or decisions only inside a tooltip.
127
+
128
+ ### Implementation
129
+
130
+ - **MUST** provide content via `text` **or** the `tooltip-content` slot — omitting both means no tooltip is rendered.
131
+ - **MUST** use only documented props and enums from MCP / component types.
132
+ - **MUST** use Joy tokens — no custom `.joy-tooltip__content` chrome (background, shadow, radius).
133
+ - **MUST NOT** invent props (`variant`, `size`, `color`, …).
134
+ - **SHOULD** prefer the **`tooltip-content` slot** over `text` when content includes markup (`<strong>`, `<small>`). Use `text` only for plain, trusted strings (XSS risk if fed user input).
135
+ - **SHOULD** set a stable **`id`** when multiple tooltips on one view need unambiguous `aria-describedby` targeting.
136
+ - **SHOULD** use `event="click"` on touch-heavy layouts or when users must interact with tooltip content.
137
+
138
+ ### Interaction
139
+
140
+ - **MUST NOT** cover the trigger or adjacent primary actions with the tooltip panel.
141
+ - **SHOULD** use default hover on desktop pointer UIs; evaluate click for mobile/tablet.
142
+ - **SHOULD** keep `delay` at default unless UX research shows a different timing need.
143
+
144
+ ### Do / don’t
145
+
146
+ | Do | Don’t |
147
+ | --- | --- |
148
+ | Wrap `VJoyIconButton` + short label in tooltip | Duplicate the button’s visible label in the tooltip |
149
+ | `tooltip-content` slot for formatted copy | `text` with HTML strings from the API |
150
+ | `event="click"` when content is tappable | Hover-only tooltips with links or buttons inside |
151
+ | `tooltipWidth` for readable line length | Fixed `width` overrides in app CSS |
152
+ | Dialog/drawer for required reading | Long paragraphs in a tooltip |
153
+
154
+ ---
155
+
156
+ ## Content
157
+
158
+ Tooltips carry **short, contextual** copy. They enhance the UI; they don’t replace labels, helper text, or dialog body content.
159
+
160
+ ### Copy formula
161
+
162
+ **Noun phrase** or **short imperative** that names the control or states the constraint:
163
+
164
+ - ✅ Put to trash
165
+ - ✅ Notifications
166
+ - ✅ Complete all required fields to submit
167
+ - ✅ Annual billing only
168
+
169
+ Avoid full sentences unless explaining a disabled state.
170
+
171
+ ### Writing rules
172
+
173
+ | Strength | Rule |
174
+ | --- | --- |
175
+ | **MUST** | One idea per tooltip — no paragraphs |
176
+ | **MUST** | Match the trigger’s language (icon `label`, button text, field name) |
177
+ | **MUST NOT** | Repeat visible text already on the trigger |
178
+ | **MUST NOT** | Put links, CTAs, or decisions only in a tooltip |
179
+ | **MUST NOT** | Use tooltips for error validation (use inline field errors) |
180
+ | **SHOULD** | ≤ ~60 characters; wrap naturally within `tooltipWidth` |
181
+ | **SHOULD** | Sentence case; no trailing period for labels |
182
+ | **SHOULD** | Name the **reason** when explaining disabled controls |
183
+ | **SHOULD** | Use `<strong>` sparingly for one emphasized word (slot content) |
184
+ | **SHOULD** (FR) | Non-breaking spaces before `;`, `?`, `!`, and `:` |
185
+
186
+ ### Patterns
187
+
188
+ **Icon buttons** — Tooltip text should mirror or shorten the control’s accessible name (`label` on `VJoyIconButton`). If the icon is universal (e.g. trash → delete), tooltip still helps but label remains mandatory.
189
+
190
+ **Disabled submit** — Pair with the disabled control: e.g. “Complete all required fields to submit”. Same message as inline form guidance is acceptable.
191
+
192
+ **Form labels** — `VJoyLabel` exposes a `label-tooltip` slot pattern; keep copy factual (format, eligibility), not marketing.
193
+
194
+ **Information helper** — For the 16px `(?)` pattern, tooltips are **text on hover**; richer patterns (badge, dialog on click) use other components — don’t overload `VJoyTooltip`.
195
+
196
+ ---
197
+
198
+ ## Accessibility
199
+
200
+ Behaviors (what must happen), not slogans.
201
+
202
+ | Behavior | Expectation |
203
+ | --- | --- |
204
+ | Role | Tooltip panel uses `role="tooltip"` |
205
+ | Relationship | Trigger wrapper sets `aria-describedby` to the tooltip `id` when open |
206
+ | Name | Trigger must have its own accessible name (`label` on icon buttons, visible text, `aria-label`) — the tooltip **describes**, it doesn’t replace the name |
207
+ | Keyboard | **Tab** away from the trigger closes an open tooltip; tooltips are not a separate tab stop |
208
+ | Hover | Default `mouseenter` — ensure equivalent info is not hover-only on keyboard-only flows when content is essential (use visible text or click mode) |
209
+ | Click mode | `event="click"` improves discoverability on touch; still provide a clear trigger name |
210
+ | Motion | Show/hide uses `--joy-transition-duration-default`; respect `prefers-reduced-motion` at app level if you add global overrides |
211
+ | Content | Don’t rely on color alone inside tooltip copy; inherited tokens meet contrast for default neutral theme |
212
+
213
+ Target: **WCAG 2.2 AA** where the product requires it.
214
+
215
+ ---
216
+
217
+ ## Examples
218
+
219
+ ```vue
220
+ <script setup lang="ts">
221
+ import { VJoyTooltip, VJoyIconButton, VJoyButton } from '@maltjoy/core-vue'
222
+ </script>
223
+
224
+ <template>
225
+ <!-- Icon button — default hover, plain text -->
226
+ <VJoyTooltip text="Put to trash">
227
+ <VJoyIconButton icon="trash" variant="ghost" label="Put to trash" />
228
+ </VJoyTooltip>
229
+
230
+ <!-- Rich content via slot -->
231
+ <VJoyTooltip placement="top">
232
+ <VJoyIconButton icon="bell" variant="ghost" label="Notifications" />
233
+ <template #tooltip-content>
234
+ You have <strong>3</strong> unread messages
235
+ </template>
236
+ </VJoyTooltip>
237
+
238
+ <!-- Click trigger (touch / interactive content) -->
239
+ <VJoyTooltip event="click" text="More details about this setting">
240
+ <VJoyIconButton icon="info" variant="ghost" label="Setting information" />
241
+ </VJoyTooltip>
242
+
243
+ <!-- Disabled control explanation -->
244
+ <VJoyTooltip text="Complete all required fields to submit">
245
+ <VJoyButton variant="main" label="Submit" disabled />
246
+ </VJoyTooltip>
247
+ </template>
248
+ ```
249
+
250
+ In Nuxt (or apps with auto-import), omit the import if `VJoyTooltip` is already global.
251
+
252
+ ---
253
+
254
+ ## Related
255
+
256
+ | Component | Use when |
257
+ | --- | --- |
258
+ | `VJoyIconButton` | Icon-only trigger — pair with tooltip when meaning isn’t obvious |
259
+ | `VJoyLabel` | Field label + optional `label-tooltip` slot |
260
+ | `VJoyButton` | Disabled actions that need a “why” on hover |
261
+ | `VJoyDialog` | User must read content or confirm a decision |
262
+ | `VJoyDrawer` | Supplementary panel with header actions (may embed tooltips in chrome) |
263
+ | `VJoyMenu` / `VJoyDropdown` | Action or navigation lists |
264
+ | `pushVJoySnackbar` | Transient feedback after an action |
265
+
266
+ ---
267
+
268
+ ## For agents
269
+
270
+ Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyTooltip.dsds.json`](./JoyTooltip.dsds.json).
271
+
272
+ ### Pick the right component
273
+
274
+ | If the intent is… | Emit… | Do not emit… |
275
+ | --- | --- | --- |
276
+ | Short hint on hover/click for one trigger | `VJoyTooltip` | Custom `position: absolute` div + title attr |
277
+ | Icon-only control | `VJoyIconButton` inside `VJoyTooltip` | `VJoyButton` with only an icon |
278
+ | Required reading or confirm/cancel | `VJoyDialog` | Long tooltip copy |
279
+ | Menu of actions | `VJoyMenu` / `VJoyDropdown` | Tooltip with fake buttons |
280
+ | Post-action feedback | `pushVJoySnackbar` | Tooltip that stays after click |
281
+ | Legacy colored variant | Neutral `VJoyTooltip` only | `variant="primary"` / `variant="secondary"` |
282
+
283
+ ### Hard rules
284
+
285
+ 1. **MUST** call `joy_get_component_info("VJoyTooltip")` (or read `VJoyTooltip.vue` types) before generating props.
286
+ 2. **MUST** wrap exactly one trigger in the default slot.
287
+ 3. **MUST** supply `text` or `#tooltip-content` — never an empty tooltip.
288
+ 4. **MUST NOT** invent props (`variant`, `size`, `open`, `theme`, …).
289
+ 5. **MUST NOT** pass HTML through `text` from API/user data — use the slot or sanitize upstream.
290
+ 6. **MUST NOT** override `.joy-tooltip__content` / `__arrow` colors, shadow, or radius in app CSS.
291
+ 7. **SHOULD** set `event="click"` when the tooltip contains interactive elements or on mobile-first screens.
292
+ 8. **SHOULD** reuse the icon button’s `label` text in the tooltip when clarifying the same action.
293
+ 9. **SHOULD NOT** use `show` in production pages (Storybook/tests only).
294
+
295
+ ### Checklist
296
+
297
+ - [ ] Correct component (or named alternative from table above)
298
+ - [ ] Props / enums from MCP only (`placement`, `event`, `delay`, `tooltipWidth`, `id`, `text`)
299
+ - [ ] Content via `text` (plain) or `#tooltip-content` (markup)
300
+ - [ ] No `variant` / no invented props / no custom tooltip chrome CSS
301
+ - [ ] Trigger has its own accessible name (`label`, visible text, or `aria-label`)
302
+ - [ ] Copy is short and non-essential; disabled/menu/dialog cases handled elsewhere if needed
303
+ - [ ] `event="click"` when content is interactive or hover is insufficient
304
+
305
+ ---
306
+
307
+ ## DSDS mapping
308
+
309
+ This Markdown mirrors [`JoyTooltip.dsds.json`](./JoyTooltip.dsds.json) (DSDS **0.15.2**).
310
+
311
+ | Section here | DSDS block |
312
+ | --- | --- |
313
+ | When to use / not | `use-cases` |
314
+ | Anatomy / placement | `anatomy` |
315
+ | Placements / triggers | `variants` (behavioral) |
316
+ | States | `states` |
317
+ | Best practices / content rules | `guidelines` (+ `content`) |
318
+ | Accessibility | `accessibility` |
319
+ | Examples | `sections` |
320
+ | For agents | `agentDocumentBlocks` |
321
+ | Props detail | `api` in JSON + Joy MCP |
322
+ | Token sizing | `design-specifications` in JSON |