@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,378 @@
1
+ ---
2
+ component: VJoyTextarea
3
+ status: draft
4
+ language: en
5
+ dsds: JoyTextarea.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Textarea
10
+
11
+ Use **`VJoyTextarea`** when the user needs to enter multi-line text — descriptions, comments, bios, feedback, or any free-form content that does not fit on a single line.
12
+
13
+ It supports labels, optional autogrow height, min/max length helpers with a live character counter, validation styling, and binds to `v-model`. Native attributes such as `placeholder`, `rows`, `readonly`, and `aria-*` pass through to the underlying `<textarea>`.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyTextarea` from `@maltjoy/core-vue` |
18
+ | **Design** | [ZeroHeight — Textarea](https://zeroheight.com/6bf479565/p/80b38d) · [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
19
+ | **Storybook** | [VJoyTextarea docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-forms-vjoytextarea--docs) |
20
+ | **DSDS (machine)** | [`JoyTextarea.dsds.json`](./JoyTextarea.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyTextarea")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a textarea when someone must type or paste content that spans multiple lines — for example a project description, support message, rejection reason, or profile bio.
28
+
29
+ Pair it with a visible label, validation feedback when needed, and (inside forms) `VJoyWrapper` for consistent spacing. Use `autogrow` when the field should expand with content instead of showing a fixed scrollable box.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Single-line text (name, email, search, short code) | `VJoyInput` |
36
+ | Password with show/hide toggle | `VJoyInputPassword` |
37
+ | Numeric value with a unit suffix (€, %, days…) | `VJoyInputNumber` |
38
+ | One-time / verification code (OTP) | `VJoyInputDigit` |
39
+ | Choosing from a known set of options | `VJoySelect`, `VJoyRadio`, or `VJoySelectableItemGroup` |
40
+ | Rich text with formatting (bold, lists, links) | A dedicated rich-text editor — not a plain textarea |
41
+ | Read-only display of existing long text | `VJoyText` or formatted content — not a disabled textarea masquerading as content |
42
+ | Custom-styled `<textarea>` / ad-hoc field chrome | Still use `VJoyTextarea` + Joy tokens |
43
+
44
+ > **Deprecated:** do not use the default slot for labels on new work — prefer the `label` prop or a separate `VJoyLabel`.
45
+
46
+ ---
47
+
48
+ ## How it works
49
+
50
+ ### Anatomy
51
+
52
+ | Part | Required? | What it is |
53
+ | --- | --- | --- |
54
+ | Root container | Yes | Wrapper (`joy-textarea`) with state classes |
55
+ | Label | No | `VJoyLabel` from the `label` prop (or deprecated default slot) |
56
+ | Field wrapper | Yes | Positions the native `<textarea>` |
57
+ | Native `<textarea>` | Yes | The actual form control; receives `$attrs` (placeholder, rows, readonly, etc.) |
58
+ | Min-length helper | No | Shown when `minlength` is set — text + check icon when the minimum is met |
59
+ | Max-length counter | No | Shown when `maxlength` is set — `current/max` format (e.g. `42/300`) |
60
+ | Focus ring | Yes | Applied on focus via Joy form-field tokens — do not remove in product CSS |
61
+
62
+ ### Helper row visibility
63
+
64
+ The helper row appears only when **`minlength` and/or `maxlength`** is set. With neither limit, no counter or minimum text is shown below the field.
65
+
66
+ ### Default values
67
+
68
+ - `modelValue` defaults to **`''`**
69
+ - `labelSize` defaults to **`medium`**
70
+ - `modelModifiers` defaults to **`{ lazy: false }`** (updates on `input`)
71
+ - `autogrow` defaults to **`false`**
72
+ - `disabled`, `invalid`, `required`, `requiredMark`, and `autofocus` default to **`false`**
73
+ - `minlengthLabel` falls back to **`Minimum {minlength} characters`** when `minlength` is set and no custom label is provided
74
+
75
+ Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
76
+
77
+ ---
78
+
79
+ ## Variants
80
+
81
+ `VJoyTextarea` has no visual “variant” prop (no primary/secondary). Visual meaning comes from **state** (`invalid`, `disabled`) and **behavior** (`autogrow`).
82
+
83
+ ### Default (valid)
84
+
85
+ Use for standard editable multi-line fields. Border uses neutral tokens; focus uses `--joy-color-state-focus`.
86
+
87
+ ### Invalid
88
+
89
+ Set `invalid` when server-side or client-side validation failed. The component also applies invalid styling on **blur** when the value violates `minlength` or `maxlength` (internal `isInvalid`). Pair with `VJoyFormError` (or equivalent) so the error is announced — the red border and counter alone are not enough.
90
+
91
+ Avoid toggling `invalid` while the user is still typing unless the value is clearly wrong.
92
+
93
+ ### Disabled
94
+
95
+ Use when the field cannot be edited and the reason is external (permissions, workflow step). **Always explain why** nearby.
96
+
97
+ ### Autogrow
98
+
99
+ Set `autogrow` when the field should grow vertically with content (no manual resize handle). The component uses `useTextareaAutosize` and disables native resize. Prefer autogrow for open-ended feedback; use native `rows` (via attrs) for a predictable initial height when autogrow is off.
100
+
101
+ ### Not in the API: floating label
102
+
103
+ ZeroHeight and legacy patterns sometimes describe a floating label. `VJoyTextarea` uses a **top label** (`label` / `VJoyLabel`) — do not simulate floating labels with custom CSS.
104
+
105
+ ### Not in the API: `size`
106
+
107
+ `VJoyInput` exposes a `size` prop; **`VJoyTextarea` does not**. Field typography and padding come from Joy textarea tokens — do not hardcode heights to mimic input sizes.
108
+
109
+ ---
110
+
111
+ ## States
112
+
113
+ | State | What the user sees / can do |
114
+ | --- | --- |
115
+ | Default | Empty or filled; editable; neutral border |
116
+ | Hover | Border shifts on pointer devices (`formElementColorHover`) |
117
+ | Focus | Focus border `--joy-color-state-focus`; root gets `joy-textarea_focusing` |
118
+ | Invalid | Error border; min helper and/or max counter turn red when limits are violated; respects `invalid` prop |
119
+ | Disabled | Not interactive; muted background and border; helpers muted; explain why nearby |
120
+ | Min length (helper) | Helper text below; check icon appears when `modelValue.length >= minlength` |
121
+ | Max length (counter) | `length/maxlength` counter; turns red when over `maxlength` |
122
+ | Autogrow | Height expands with content; vertical resize disabled |
123
+
124
+ On blur, internal `isInvalid` becomes true when the value is under `minlength` or over `maxlength` (even without the `invalid` prop).
125
+
126
+ ---
127
+
128
+ ## Best practices
129
+
130
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
131
+
132
+ ### Labels and forms
133
+
134
+ - **MUST** provide a visible label for every textarea (via `label`, `VJoyLabel`, or an associated `<label for="…">`).
135
+ - **MUST** set `name` (and `id` when multiple fields share a form) so labels, autofill, and tests bind correctly.
136
+ - **SHOULD** use `requiredMark` together with `required` when the asterisk pattern is used in the product.
137
+ - **SHOULD** use `optionalLabel` on non-required fields when the form mixes required and optional inputs.
138
+ - **SHOULD** wrap related fields in `VJoyWrapper` for spacing; margin adjusts automatically when inside a wrapper.
139
+
140
+ ### Length limits
141
+
142
+ - **SHOULD** set `maxlength` when the backend or UX has a hard character cap — the built-in counter replaces ad-hoc counters.
143
+ - **SHOULD** set `minlength` only when a minimum is a real product rule (e.g. review text); localize `minlengthLabel` for non-English UIs.
144
+ - **MUST** show `VJoyFormError` (or equivalent) when blocking submit — not only red helper text.
145
+ - **MUST NOT** show a character counter when neither `minlength` nor `maxlength` is set — the component hides helpers by design.
146
+
147
+ ### Implementation
148
+
149
+ - **MUST** use only documented prop values from MCP or `VJoyTextarea.vue`.
150
+ - **MUST** bind value with `v-model` (`modelValue` / `update:modelValue`).
151
+ - **MUST** style with Joy tokens — no custom `.joy-textarea` chrome (border, padding, radius).
152
+ - **MUST NOT** invent props (`size`, `variant`, `errorMessage`, `clearable`, …).
153
+ - **MUST NOT** use the default slot for new labels — use `label` or `VJoyLabel`.
154
+ - **SHOULD** use `v-model.lazy` (via `modelModifiers: { lazy: true }`) when updates on every keystroke are expensive.
155
+ - **SHOULD** use `autogrow` for variable-length content; set `rows` via native attrs when a fixed initial height is enough.
156
+
157
+ ### Do / don’t
158
+
159
+ | Do | Don’t |
160
+ | --- | --- |
161
+ | `label` + `name` on every field | Placeholder as the only label |
162
+ | `maxlength` + built-in counter | Custom duplicate counter below the field |
163
+ | `minlengthLabel` localized | Hard-coded English minimum text in FR UI |
164
+ | `VJoyFormError` with `invalid` | Red border with no message |
165
+ | `autogrow` for open-ended text | Tiny fixed box with hidden overflow |
166
+ | Joy textarea tokens | Hardcoded min-heights in px |
167
+ | `VJoyInput` for one-line values | Tall textarea for a single email |
168
+
169
+ ---
170
+
171
+ ## Content
172
+
173
+ Textareas expose label, placeholder, and optional helper copy below the field (min text / counter). All must help users enter the **right** content the **first** time.
174
+
175
+ ### Label formula
176
+
177
+ **Noun or short noun phrase** describing the data (not the action):
178
+
179
+ - ✅ Project description
180
+ - ✅ Message to the client
181
+ - ✅ Rejection reason
182
+
183
+ Use sentence case. For free-text fields, the label names **what** is being collected, not **how** to write it.
184
+
185
+ ### Placeholder formula
186
+
187
+ **Short hint or example** — not a repeat of the label. ZeroHeight allows slightly longer placeholders for free text than single-line inputs, but keep them concise (roughly 3–5 words when possible, more only when the format truly needs it).
188
+
189
+ - ✅ `Briefly describe your experience on this mission` (under label “Feedback”)
190
+ - ✅ `Ex: We need a senior React developer for a 3-month mission` (under label “Project description”)
191
+
192
+ - ❌ `Description` as placeholder when the label already says “Description”
193
+
194
+ Do not put required instructions only in the placeholder — they disappear when typing. Use helper text (`VJoyFormError`, legend on `VJoyLabel`, or `minlengthLabel`) for rules that must stay visible.
195
+
196
+ ### Optional / required markers
197
+
198
+ | Pattern | When |
199
+ | --- | --- |
200
+ | `required` + `requiredMark` | Required field in a form that uses asterisks |
201
+ | `optionalLabel` (e.g. “optional”) | Field is not required among mostly required fields |
202
+
203
+ ### Writing rules
204
+
205
+ | Strength | Rule |
206
+ | --- | --- |
207
+ | **MUST** | Every field has a visible label — never placeholder-only |
208
+ | **MUST** | Error messages state what went wrong and how to fix it — ✅ `Enter at least 50 characters` · ❌ `Invalid` |
209
+ | **MUST NOT** | Repeat the label verbatim in the placeholder |
210
+ | **MUST NOT** | Use placeholders that look like prefilled data without `Ex:` or similar |
211
+ | **SHOULD** | Localize `minlengthLabel` and error copy for non-English products |
212
+ | **SHOULD** | Use placeholders for format examples or tone hints, not requirements |
213
+ | **SHOULD** | Match terminology with page headings and adjacent fields |
214
+ | **SHOULD** (FR) | Follow French grammar for labels and optional markers when the UI is localized |
215
+
216
+ ### Patterns
217
+
218
+ **Feedback / reviews** — Set `minlength` when quality matters; customize `minlengthLabel`. Show errors on blur or submit, not on every keystroke.
219
+
220
+ **Character limits** — Set `maxlength` to match API limits. Let the built-in counter communicate progress; add `VJoyFormError` when submit is blocked.
221
+
222
+ **Long forms** — Prefer `autogrow` so users are not scrolling inside a small box. Split unrelated long content into separate fields when they serve different purposes.
223
+
224
+ ---
225
+
226
+ ## Accessibility
227
+
228
+ Behaviors (what must happen), not slogans.
229
+
230
+ | Behavior | Expectation |
231
+ | --- | --- |
232
+ | Role | Native `<textarea>` |
233
+ | Name | From associated `VJoyLabel` / `<label for>`; `label` prop wires `for` to `id` |
234
+ | Value | Controlled via `v-model` |
235
+ | Keyboard | Standard multi-line editing; Tab moves focus; Enter inserts newline |
236
+ | Focus | Visible focus ring on the textarea — do not remove `outline` / focus tokens |
237
+ | Invalid | `invalid` prop and/or internal length validation styles the field; pair with `VJoyFormError` (`role="alert"`) for the message |
238
+ | Disabled | Native `disabled` on textarea; root sets `aria-disabled`; explain why in visible text |
239
+ | Min / max helpers | Visible text below the field; do not rely on color alone for errors |
240
+ | Icons | Check icon on min-length helper is supplementary — the text carries the meaning |
241
+ | Native attrs | Pass `aria-describedby`, `aria-invalid`, `readonly`, etc. via attrs when needed |
242
+
243
+ Target: **WCAG 2.2 AA** where the product requires it.
244
+
245
+ ---
246
+
247
+ ## Examples
248
+
249
+ ```vue
250
+ <script setup lang="ts">
251
+ import { ref } from 'vue'
252
+ import { VJoyTextarea, VJoyLabel, VJoyFormError, VJoyWrapper } from '@maltjoy/core-vue'
253
+
254
+ const description = ref('')
255
+ const feedback = ref('')
256
+ const showError = ref(false)
257
+ </script>
258
+
259
+ <template>
260
+ <!-- Standard field with max length -->
261
+ <VJoyWrapper direction="column" row-gap="4">
262
+ <VJoyTextarea
263
+ v-model="description"
264
+ name="project-description"
265
+ label="Project description"
266
+ placeholder="Ex: Mission goals, stack, and duration"
267
+ :maxlength="500"
268
+ required
269
+ required-mark
270
+ :invalid="showError"
271
+ />
272
+ <VJoyFormError v-if="showError" message="Enter a project description" />
273
+ </VJoyWrapper>
274
+
275
+ <!-- Autogrow + minimum length -->
276
+ <VJoyTextarea
277
+ v-model="feedback"
278
+ name="mission-feedback"
279
+ label="Feedback"
280
+ autogrow
281
+ :minlength="50"
282
+ minlength-label="At least 50 characters"
283
+ placeholder="Share what went well and what could improve"
284
+ />
285
+
286
+ <!-- Custom label composition -->
287
+ <VJoyLabel for="rejection-reason" required>
288
+ Rejection reason
289
+ <template #label-legend>Visible to the freelancer</template>
290
+ </VJoyLabel>
291
+ <VJoyTextarea
292
+ id="rejection-reason"
293
+ v-model="description"
294
+ name="rejection-reason"
295
+ :maxlength="300"
296
+ rows="4"
297
+ placeholder="Explain your decision clearly and respectfully"
298
+ />
299
+ </template>
300
+ ```
301
+
302
+ In Nuxt (or apps with auto-import), omit the import if `VJoyTextarea` is already global.
303
+
304
+ ---
305
+
306
+ ## Related
307
+
308
+ | Component | Use when |
309
+ | --- | --- |
310
+ | `VJoyInput` | Single-line text |
311
+ | `VJoyLabel` | Rich labels (legend, tooltip) separate from the field |
312
+ | `VJoyFormError` | Inline validation message below the field |
313
+ | `VJoyWrapper` | Form layout and spacing between fields |
314
+ | `VJoyFormGroup` | Label + field + error grouped with reset margins |
315
+ | `VJoyText` | Read-only display of long text |
316
+
317
+ ---
318
+
319
+ ## For agents
320
+
321
+ Optional for readers; **required** for vibe-coding quality. Maps to `agentDocumentBlocks` in [`JoyTextarea.dsds.json`](./JoyTextarea.dsds.json).
322
+
323
+ ### Pick the right component
324
+
325
+ | If the intent is… | Emit… | Do not emit… |
326
+ | --- | --- | --- |
327
+ | Multi-line free text | `VJoyTextarea` | Tall `VJoyInput` or raw `<textarea>` |
328
+ | Single-line text | `VJoyInput` | `VJoyTextarea` with `rows="1"` |
329
+ | Rich formatted text | Dedicated editor component | `VJoyTextarea` + custom WYSIWYG CSS |
330
+ | Display-only long text | `VJoyText` | `disabled` textarea without reason |
331
+ | Character limit UI | `VJoyTextarea` + `maxlength` | Separate counter div |
332
+ | Expanding height | `VJoyTextarea` + `autogrow` | Fixed height + `overflow: hidden` hack |
333
+
334
+ ### Hard rules
335
+
336
+ 1. **MUST** call `joy_get_component_info("VJoyTextarea")` before generating props.
337
+ 2. **MUST** bind `v-model` for editable values.
338
+ 3. **MUST** set `name` (and `id` when needed for label association).
339
+ 4. **MUST** pass native attrs (`placeholder`, `rows`, `readonly`, `aria-*`) on the component — they flow to `<textarea>`.
340
+ 5. **MUST** set `invalid` when showing validation errors and render `VJoyFormError` with a message.
341
+ 6. **MUST** set `maxlength` and/or `minlength` when using length rules — helpers render automatically.
342
+ 7. **MUST NOT** emit `size` — textarea has no size prop.
343
+ 8. **MUST NOT** use the default slot for new labels — use `label` or `VJoyLabel`.
344
+ 9. **MUST NOT** override `.joy-textarea` border, padding, radius, or typography.
345
+ 10. **SHOULD** set `autogrow` for open-ended user content.
346
+ 11. **SHOULD** localize `minlengthLabel` when `minlength` is set.
347
+
348
+ ### Checklist
349
+
350
+ - [ ] Correct component (or named alternative from table above)
351
+ - [ ] Props from MCP only
352
+ - [ ] `v-model` + `name` present
353
+ - [ ] Visible label (not placeholder-only)
354
+ - [ ] `invalid` + `VJoyFormError` when validation fails
355
+ - [ ] `maxlength` / `minlength` set when product rules require them
356
+ - [ ] No invented props / no custom textarea chrome CSS
357
+ - [ ] Single-line text → `VJoyInput`, not textarea
358
+ - [ ] Disabled explained if user-visible
359
+
360
+ ---
361
+
362
+ ## DSDS mapping
363
+
364
+ This Markdown mirrors [`JoyTextarea.dsds.json`](./JoyTextarea.dsds.json) (DSDS **0.15.2**).
365
+
366
+ | Section here | DSDS block |
367
+ | --- | --- |
368
+ | When to use / not | `use-cases` |
369
+ | Anatomy | `anatomy` |
370
+ | Variants (state / autogrow) | `variants` |
371
+ | States | `states` |
372
+ | Best practices / content rules | `guidelines` (+ `content`) |
373
+ | Accessibility | `accessibility` |
374
+ | Examples | `sections` |
375
+ | For agents | `agentDocumentBlocks` |
376
+ | Props detail | `api` in JSON + Joy MCP |
377
+ | Imports | `imports` in JSON |
378
+ | Token sizing | `design-specifications` in JSON (`--joy-core-radius-*`, form border tokens) |
@@ -0,0 +1,155 @@
1
+ ---
2
+ component: VJoyTitleBrand
3
+ status: draft
4
+ language: en
5
+ dsds: JoyTitleBrand.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Title brand
10
+
11
+ Use **`VJoyTitleBrand`** when a page, dialog, or marketing block needs a striking title with one or more **brand-highlighted** word segments — a rotated primary background behind selected text.
12
+
13
+ The root element is configurable (`tag`, default `p`). Wrap highlighted words in a **`<span>`** in the default slot; the component applies highlight styling automatically. Typical placements include `VJoyTemplate` headers and `VJoyDialog` titles.
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyTitleBrand` from `@maltjoy/core-vue` |
18
+ | **Design** | [Figma — Joy Core Design System](https://www.figma.com/design/jfndp2QBJGS9yeuWbeGuvD/) |
19
+ | **Storybook** | [VJoyTitleBrand docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-view-vjoytitlebrand--docs) |
20
+ | **DSDS (machine)** | [`JoyTitleBrand.dsds.json`](./JoyTitleBrand.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyTitleBrand")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a title brand when the heading should feel **on-brand and energetic** — onboarding heroes, funnel steps, dialog headers, or landing sections where one or two words deserve extra emphasis.
28
+
29
+ The highlight is purely presentational: surrounding copy stays in the default title color; `<span>` children get inverted text on a tilted primary swatch. Multiple `<span>` segments are supported in one title.
30
+
31
+ ### When not to use this component
32
+
33
+ | Need | Use instead |
34
+ | --- | --- |
35
+ | Standard body or UI typography | `VJoyText` |
36
+ | A plain semantic heading without brand highlight | Native `h1`–`h6` with Joy font utility classes |
37
+ | Panel or card title chrome (actions, subtitle row) | `VJoyPanel` header slots |
38
+ | Inline emphasis inside a paragraph | `VJoyText` or semantic `<strong>` — not a full title component |
39
+ | Custom highlight markup / ad-hoc CSS for the same job | Still use `VJoyTitleBrand` + `<span>` — don’t reinvent the highlight |
40
+
41
+ ---
42
+
43
+ ## How it works
44
+
45
+ ### Anatomy
46
+
47
+ | Part | Required? | What it is |
48
+ | --- | --- | --- |
49
+ | Root element | Yes | Dynamic tag (`tag` prop) with `.joy-title-brand` and `.joy-font-secondary-200` |
50
+ | Default slot | Yes | Title text; mix plain text and `<span>` for highlights |
51
+ | Highlight (`span`) | No | One or more segments with rotated `--joy-color-primary-50` background and `--joy-color-neutral-0` text |
52
+
53
+ ### Default values
54
+
55
+ - `tag` defaults to **`p`**
56
+ - Typography: **`joy-font-secondary-200`** on the root (secondary font family, weight 200)
57
+ - Title color: **`--joy-color-text-title`**
58
+ - Highlight padding/gap: **`--joy-core-spacing-1`** / **`--joy-core-spacing-2`**; highlight radius **`--joy-core-radius-5`**
59
+
60
+ Props and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
61
+
62
+ ### Highlight behavior
63
+
64
+ Only **direct `<span>` children** in the default slot receive the brand highlight (`:deep(span)`). Plain text nodes render without a background. Each span gets a slight **−1°** rotation on its background pseudo-element.
65
+
66
+ ---
67
+
68
+ ## Best practices
69
+
70
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
71
+
72
+ ### Semantics and hierarchy
73
+
74
+ - **MUST** set `tag` to the correct heading level (`h1`–`h6`) when the title is a real page or section heading — don’t leave the default `p` for primary document titles.
75
+ - **SHOULD** respect heading order (one `h1` per view; don’t skip levels).
76
+ - **SHOULD** keep highlighted segments **short** (roughly one to three words) so the tilted swatch stays readable on narrow viewports.
77
+
78
+ ### Content and layout
79
+
80
+ - **MUST** use **`<span>`** for highlighted words — no extra wrappers, classes, or inline styles on highlights.
81
+ - **SHOULD** limit highlights to **one or two** segments per title; more reads as noisy (see Storybook “Corner case”).
82
+ - **SHOULD** place the component in template or dialog **header** slots where brand tone is intentional — not for dense data tables or settings labels.
83
+
84
+ ### Implementation
85
+
86
+ - **MUST** use only documented props from MCP (`tag` is the sole prop).
87
+ - **MUST** use Joy tokens — **MUST NOT** override `.joy-title-brand` colors, padding, or pseudo-element rotation in product CSS.
88
+ - **MUST NOT** invent props (`variant`, `size`, `highlight`, …).
89
+
90
+ ### Do / don’t
91
+
92
+ | Do | Don’t |
93
+ | --- | --- |
94
+ | `tag="h1"` / `h2` for real headings | Default `p` for the main page title |
95
+ | `<span>brand</span>` for emphasis | Custom `<mark>` or colored `<strong>` for the same effect |
96
+ | Short highlighted phrases | Entire title wrapped in one `<span>` |
97
+ | `VJoyTitleBrand` in template/dialog headers | Brand title styling on every `h2` in the app |
98
+
99
+ ---
100
+
101
+ ## Accessibility
102
+
103
+ `VJoyTitleBrand` is **presentational and non-interactive**. Accessible structure depends on the chosen `tag` and slot content.
104
+
105
+ | Behavior | Expectation |
106
+ | --- | --- |
107
+ | Role | Comes from `tag` — `h1`–`h6` for headings, `p` for non-heading brand copy |
108
+ | Name | Full title text (plain + span content) forms the accessible name |
109
+ | Heading outline | Use semantic heading tags when the title denotes page/section hierarchy |
110
+ | Highlight | Decorative background only; meaning must not rely on color or rotation alone |
111
+ | Focus | Not focusable — no keyboard interaction |
112
+ | Contrast | Highlight uses `--joy-color-neutral-0` on `--joy-color-primary-50`; ensure surrounding title color meets product contrast requirements |
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 { VJoyTitleBrand } from '@maltjoy/core-vue'
123
+ </script>
124
+
125
+ <template>
126
+ <!-- Page hero — semantic heading + one highlight -->
127
+ <VJoyTitleBrand tag="h1">
128
+ Welcome to Malt <span>freelancers</span>
129
+ </VJoyTitleBrand>
130
+
131
+ <!-- Dialog or template header -->
132
+ <VJoyTitleBrand tag="h2">
133
+ Title of the page <span>with brand</span>
134
+ </VJoyTitleBrand>
135
+
136
+ <!-- Multiple highlights (use sparingly) -->
137
+ <VJoyTitleBrand tag="h2">
138
+ Title of the page <span>with brand</span> but not <span>only</span>
139
+ </VJoyTitleBrand>
140
+ </template>
141
+ ```
142
+
143
+ In Nuxt (or apps with auto-import), omit the import if `VJoyTitleBrand` is already global.
144
+
145
+ ---
146
+
147
+ ## Related
148
+
149
+ | Component | Use when |
150
+ | --- | --- |
151
+ | `VJoyTemplate` | Full-page layout with header area for branded titles |
152
+ | `VJoyDialog` | Modal headers that can host a brand title |
153
+ | `VJoyText` | Standard typography without brand highlight |
154
+ | `VJoyPanel` | Card/section titles with actions and structured header |
155
+ | `VJoyFunnel` | Multi-step flows where a brand moment may appear in the header |