@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.
- package/LICENSE +21 -0
- package/README.md +30 -3
- package/dist/doc/VJoyAdminBanner/guideline.md +284 -0
- package/dist/doc/VJoyAvailability/guideline.md +352 -0
- package/dist/doc/VJoyAvatar/guideline.md +192 -0
- package/dist/doc/VJoyAvatarsList/guideline.md +328 -0
- package/dist/doc/VJoyBadge/guideline.md +169 -0
- package/dist/doc/VJoyBadgeLevel/guideline.md +166 -0
- package/dist/doc/VJoyBlockSkeleton/guideline.md +150 -0
- package/dist/doc/VJoyBottomSheet/guideline.md +345 -0
- package/dist/doc/VJoyBottomSheetTrigger/guideline.md +156 -0
- package/dist/doc/VJoyButton/guideline.md +371 -0
- package/dist/doc/VJoyButton/metadata.json +0 -16
- package/dist/doc/VJoyButtonCard/guideline.md +384 -0
- package/dist/doc/VJoyCheckbox/guideline.md +317 -0
- package/dist/doc/VJoyCheckboxCard/guideline.md +381 -0
- package/dist/doc/VJoyCheckboxGroup/guideline.md +363 -0
- package/dist/doc/VJoyCheckboxGroup/metadata.json +7 -7
- package/dist/doc/VJoyCollapse/guideline.md +362 -0
- package/dist/doc/VJoyCollapseItem/guideline.md +394 -0
- package/dist/doc/VJoyCompanyAvatar/guideline.md +162 -0
- package/dist/doc/VJoyDialog/guideline.md +425 -0
- package/dist/doc/VJoyDialogTrigger/guideline.md +186 -0
- package/dist/doc/VJoyDot/guideline.md +177 -0
- package/dist/doc/VJoyDrawer/guideline.md +392 -0
- package/dist/doc/VJoyDrawerTrigger/guideline.md +145 -0
- package/dist/doc/VJoyDropdown/guideline.md +393 -0
- package/dist/doc/VJoyDropdown/metadata.json +6 -6
- package/dist/doc/VJoyDropdownList/guideline.md +396 -0
- package/dist/doc/VJoyDropdownList/metadata.json +7 -7
- package/dist/doc/VJoyDropzone/guideline.md +390 -0
- package/dist/doc/VJoyDropzone/metadata.json +36 -1
- package/dist/doc/VJoyFilterBar/guideline.md +419 -0
- package/dist/doc/VJoyFilterBarButton/guideline.md +359 -0
- package/dist/doc/VJoyFloatingActionsBar/guideline.md +372 -0
- package/dist/doc/VJoyFormError/guideline.md +325 -0
- package/dist/doc/VJoyFormError/metadata.json +0 -14
- package/dist/doc/VJoyFormFieldSkeleton/guideline.md +159 -0
- package/dist/doc/VJoyFormGroup/guideline.md +285 -0
- package/dist/doc/VJoyFunnel/guideline.md +349 -0
- package/dist/doc/VJoyFunnelFooter/guideline.md +178 -0
- package/dist/doc/VJoyFunnelHeader/guideline.md +204 -0
- package/dist/doc/VJoyHighlight/guideline.md +169 -0
- package/dist/doc/VJoyIcon/JoyIcon.types.ts +1 -1
- package/dist/doc/VJoyIcon/guideline.md +168 -0
- package/dist/doc/VJoyIcon/metadata.json +0 -1
- package/dist/doc/VJoyIconButton/guideline.md +169 -0
- package/dist/doc/VJoyIconButton/metadata.json +1 -1
- package/dist/doc/VJoyIndicator/guideline.md +168 -0
- package/dist/doc/VJoyIndicators/guideline.md +183 -0
- package/dist/doc/VJoyInput/JoyInput.types.ts +1 -6
- package/dist/doc/VJoyInput/guideline.md +401 -0
- package/dist/doc/VJoyInput/metadata.json +5 -14
- package/dist/doc/VJoyInputDigit/guideline.md +381 -0
- package/dist/doc/VJoyInputNumber/guideline.md +414 -0
- package/dist/doc/VJoyInputNumber/metadata.json +22 -17
- package/dist/doc/VJoyInputPassword/guideline.md +379 -0
- package/dist/doc/VJoyInputPassword/metadata.json +0 -14
- package/dist/doc/VJoyLabel/guideline.md +200 -0
- package/dist/doc/VJoyLink/guideline.md +360 -0
- package/dist/doc/VJoyListItem/guideline.md +335 -0
- package/dist/doc/VJoyMenu/guideline.md +304 -0
- package/dist/doc/VJoyMenu/metadata.json +21 -21
- package/dist/doc/VJoyMenuItem/guideline.md +352 -0
- package/dist/doc/VJoyPagination/guideline.md +355 -0
- package/dist/doc/VJoyPanel/guideline.md +238 -0
- package/dist/doc/VJoyPanelSection/guideline.md +160 -0
- package/dist/doc/VJoyPasswordRequirement/guideline.md +336 -0
- package/dist/doc/VJoyPasswordRequirement/metadata.json +1 -1
- package/dist/doc/VJoyProductTour/JoyProductTour.types.ts +0 -1
- package/dist/doc/VJoyProductTour/guideline.md +455 -0
- package/dist/doc/VJoyProductTour/metadata.json +1 -1
- package/dist/doc/VJoyProductTourTrigger/guideline.md +167 -0
- package/dist/doc/VJoyProgressBar/guideline.md +348 -0
- package/dist/doc/VJoyRadio/guideline.md +346 -0
- package/dist/doc/VJoyRadioGroup/guideline.md +331 -0
- package/dist/doc/VJoyRatingStars/guideline.md +306 -0
- package/dist/doc/VJoyScreenLoader/guideline.md +172 -0
- package/dist/doc/VJoySelect/guideline.md +396 -0
- package/dist/doc/VJoySelectableItem/guideline.md +351 -0
- package/dist/doc/VJoySelectableItemGroup/guideline.md +370 -0
- package/dist/doc/VJoySeparator/guideline.md +161 -0
- package/dist/doc/VJoySnackbar/guideline.md +402 -0
- package/dist/doc/VJoySpinner/guideline.md +168 -0
- package/dist/doc/VJoyStep/guideline.md +182 -0
- package/dist/doc/VJoyStepper/guideline.md +390 -0
- package/dist/doc/VJoyTab/guideline.md +298 -0
- package/dist/doc/VJoyTabs/guideline.md +353 -0
- package/dist/doc/VJoyTag/guideline.md +226 -0
- package/dist/doc/VJoyTagsInput/guideline.md +387 -0
- package/dist/doc/VJoyTagsList/guideline.md +311 -0
- package/dist/doc/VJoyTagsList/metadata.json +27 -27
- package/dist/doc/VJoyTemplate/guideline.md +366 -0
- package/dist/doc/VJoyText/guideline.md +175 -0
- package/dist/doc/VJoyText/metadata.json +1 -1
- package/dist/doc/VJoyTextarea/guideline.md +378 -0
- package/dist/doc/VJoyTitleBrand/guideline.md +155 -0
- package/dist/doc/VJoyToggle/guideline.md +327 -0
- package/dist/doc/VJoyTooltip/guideline.md +322 -0
- package/dist/doc/VJoyUserCard/guideline.md +268 -0
- package/dist/doc/VJoyUserCard/metadata.json +3 -17
- package/dist/doc/VJoyWalkthrough/guideline.md +351 -0
- package/dist/doc/VJoyWalkthroughTrigger/guideline.md +171 -0
- package/dist/doc/VJoyWrapper/guideline.md +173 -0
- package/dist/doc/css-classes.json +1 -15
- package/dist/doc/design-tokens.json +1 -0
- package/dist/doc/index.json +20 -30
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/get-component-guideline.d.ts +30 -0
- package/dist/tools/get-component-guideline.d.ts.map +1 -0
- package/dist/tools/get-component-guideline.js +51 -0
- package/dist/tools/get-component-guideline.js.map +1 -0
- package/dist/tools/get-documentation-page.d.ts +22 -0
- package/dist/tools/get-documentation-page.d.ts.map +1 -0
- package/dist/tools/get-documentation-page.js +21 -0
- package/dist/tools/get-documentation-page.js.map +1 -0
- package/dist/tools/list-documentation-pages.d.ts +22 -0
- package/dist/tools/list-documentation-pages.d.ts.map +1 -0
- package/dist/tools/list-documentation-pages.js +17 -0
- package/dist/tools/list-documentation-pages.js.map +1 -0
- package/dist/tools/search-documentation.d.ts +41 -0
- package/dist/tools/search-documentation.d.ts.map +1 -0
- package/dist/tools/search-documentation.js +25 -0
- package/dist/tools/search-documentation.js.map +1 -0
- package/dist/zeroheight-parser.d.ts +27 -0
- package/dist/zeroheight-parser.d.ts.map +1 -0
- package/dist/zeroheight-parser.js +77 -0
- package/dist/zeroheight-parser.js.map +1 -0
- package/package.json +10 -10
- 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 |
|