@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,384 @@
1
+ ---
2
+ component: VJoyButtonCard
3
+ status: draft
4
+ language: en
5
+ dsds: JoyButtonCard.dsds.json
6
+ dsdsVersion: "0.15.2"
7
+ ---
8
+
9
+ # Button card
10
+
11
+ Use **`VJoyButtonCard`** when the user needs a **rich, card-shaped call to action** — a title, subtitle, illustration, or other composed content that would not fit in a standard `VJoyButton`.
12
+
13
+ The component supplies bordered card chrome, hover and focus feedback, and disabled handling. **You compose and style the inner content** via the default slot. It can act as an in-page action (`@click`), a native link (`href`), or a router link (`type` + attrs such as `to`).
14
+
15
+ | | |
16
+ | --- | --- |
17
+ | **Code** | `VJoyButtonCard` from `@maltjoy/core-vue` |
18
+ | **Design** | [ZeroHeight — ButtonCard](https://zeroheight.com/6bf479565/) (page id `3079912`) |
19
+ | **Storybook** | [VJoyButtonCard docs](https://doc.malt.tech/joy-storybook/?path=/docs/components-buttons-vjoybuttoncard--docs) |
20
+ | **DSDS (machine)** | [`JoyButtonCard.dsds.json`](./JoyButtonCard.dsds.json) |
21
+ | **Live API** | `joy_get_component_info("VJoyButtonCard")` |
22
+
23
+ ---
24
+
25
+ ## When to use this component
26
+
27
+ Use a button card when the choice or action needs **more than a short label** — for example branching onboarding paths, profile-type pickers, or navigation tiles with an icon, title, and supporting line.
28
+
29
+ Typical jobs: “choose your account type”, “complete your freelance profile”, or opening a destination that benefits from a scannable card layout. The whole card surface is the click target.
30
+
31
+ Compose several cards in a group when each option represents a **distinct path or action**. Add a group label (`VJoyLabel` or a heading) when the set needs framing context.
32
+
33
+ ### When not to use this component
34
+
35
+ | Need | Use instead |
36
+ | --- | --- |
37
+ | A short text label only (save, cancel, continue) | `VJoyButton` |
38
+ | Icon-only action | `VJoyIconButton` |
39
+ | Inline text navigation in body copy | `VJoyLink` |
40
+ | Picking one option from a list (form data, shipping method) | `VJoyRadio` (`theme="outline"`) inside `VJoyRadioGroup` |
41
+ | Multi-select with card layout | `VJoyCheckboxCard` inside `VJoyCheckboxGroup` |
42
+ | Toggle a single on/off setting | `VJoyToggle` |
43
+ | Custom bordered div + click handler | Still use `VJoyButtonCard` + Joy tokens — don’t reinvent chrome |
44
+
45
+ > **Deprecated:** `VJoySelectableItem` / `VJoySelectableItemGroup` remain deprecated. Use `VJoyButtonCard` when the interaction is a **CTA or link**, `VJoyCheckboxCard` for boolean multi-select cards, and `VJoyRadio` for exclusive choice.
46
+
47
+ ---
48
+
49
+ ## How it works
50
+
51
+ ### Anatomy
52
+
53
+ | Part | Required? | What it is |
54
+ | --- | --- | --- |
55
+ | Card container | Yes | Root `.joy-button-card` — border, padding, hover, focus, disabled styling |
56
+ | Default slot content | Yes | **Your** title, subtitle, icon, image, or layout — not provided by the component |
57
+ | Focus ring | Yes | `outline` on `:focus-visible` — don’t remove it in product CSS |
58
+
59
+ There is no built-in title, subtitle, or icon API. Structure copy and imagery in the slot (for example `<strong>` + `<span>`, or `VJoyText` / `VJoyIcon`).
60
+
61
+ ### Rendering modes
62
+
63
+ The `type` prop chooses the root element (default **`"button"`** — a naming legacy, not always a native `<button>`):
64
+
65
+ | Configuration | Root element | Typical use |
66
+ | --- | --- | --- |
67
+ | Default (`type="button"`, no `href`) | `<div role="button" tabindex="0">` | In-page action via `@click` |
68
+ | `href` set (default `type`) | `<a href="…">` | External or same-origin navigation |
69
+ | `type="a"` | `<a>` | Explicit anchor; pass `href` via attrs or prop |
70
+ | `type={RouterLink}` (or other component) | That component | SPA routing — pass `to` and other attrs |
71
+
72
+ Native attributes (`target`, `rel`, `to`, `aria-*`, `@click`, …) pass through via `useAttrs` (`inheritAttrs: false` on the component).
73
+
74
+ ### Default values
75
+
76
+ - `disabled` defaults to **`false`**
77
+ - `type` defaults to **`"button"`** (renders pseudo-button `<div>` or `<a>` when `href` is set — see table above)
78
+ - `align` defaults to **`"left"`**
79
+
80
+ Props, events, and slots: use Joy MCP or Storybook — this page focuses on **when** and **how**, not a full API dump.
81
+
82
+ ### Visual treatment
83
+
84
+ There is **no `variant` or `size` prop**. Border, radius, padding, and colors come from Joy tokens inside the component (`--joy-core-spacing-3`, `--joy-core-radius-4`, `--joy-color-neutral-*`, state tokens) — **don’t hardcode card dimensions or border colors** in product CSS. Card width is **`fit-content`** by default.
85
+
86
+ ---
87
+
88
+ ## Variants
89
+
90
+ `VJoyButtonCard` has **no visual emphasis variants** (no primary/secondary). Meaning comes from **content** in the slot and from **state** (`disabled`, hover, focus).
91
+
92
+ ### Content alignment (`align`)
93
+
94
+ | Value | Use when |
95
+ | --- | --- |
96
+ | `left` | Default — text-heavy cards, LTR product UI |
97
+ | `center` | Icon-above-title tiles, symmetric marketing pickers |
98
+ | `right` | Rare; RTL-like layout or deliberate visual balance |
99
+
100
+ `align` only sets `text-align` on the card — it does not change padding or border.
101
+
102
+ ### Not in the API: selected / checked state
103
+
104
+ ZeroHeight usage text describes a **“Selected”** border for card pickers. `VJoyButtonCard` has **no `selected`, `checked`, or `active` prop**. For exclusive or multi selection, use `VJoyRadio` (`theme="outline"`) or `VJoyCheckboxCard`. Use `VJoyButtonCard` when the card **navigates or triggers an action**, not when it toggles form value.
105
+
106
+ ### Not in the API: loading
107
+
108
+ There is no `loading` prop. Keep layout stable with surrounding UI or disable the card while async work runs.
109
+
110
+ ---
111
+
112
+ ## Sizes
113
+
114
+ `VJoyButtonCard` exposes **no size API**. Padding and border width are fixed in component styles.
115
+
116
+ | Aspect | Guidance |
117
+ | --- | --- |
118
+ | Card width | `fit-content` — wrap in a grid or `VJoyWrapper` for equal-width groups |
119
+ | Inner typography | Use `VJoyText`, headings, or Joy utility classes in the slot — don’t invent `size` on the card |
120
+ | Density | Prefer shorter copy over shrinking padding via custom CSS |
121
+
122
+ ---
123
+
124
+ ## States
125
+
126
+ | State | What the user sees / can do |
127
+ | --- | --- |
128
+ | Default | Neutral border; pointer cursor; entire card clickable |
129
+ | Hover | Border uses `--joy-color-state-hover` (pointer devices, when not disabled) |
130
+ | Focus | Visible `outline` + hover border token on `:focus-visible` |
131
+ | Disabled | Muted border, background, and text; `aria-disabled="true"`; `tabindex="-1"`; clicks prevented — **explain why** nearby when possible |
132
+ | Pseudo-button keyboard | **Enter** and **Space** synthesize a click on the `<div role="button">` root |
133
+ | Anchor / router | Native link or router keyboard behavior; disabled still sets `aria-disabled` and blocks interaction |
134
+
135
+ ---
136
+
137
+ ## Best practices
138
+
139
+ Rules use RFC 2119 strength (**MUST** / **SHOULD** / **MUST NOT** / **SHOULD NOT**).
140
+
141
+ ### When to choose this over a plain button
142
+
143
+ - **SHOULD** use button cards for **branching** choices that change the user’s path or open a rich destination.
144
+ - **MUST NOT** use button cards for simple form fields where `VJoyRadio` or `VJoySelect` is enough.
145
+ - **SHOULD** use `VJoyButton` when the label fits in one short line.
146
+
147
+ ### Groups and hierarchy
148
+
149
+ - **SHOULD** make each card in a group **meaningfully distinct** — if two labels could be swapped, rewrite them.
150
+ - **SHOULD** add a group label (`VJoyLabel` or heading) when options aren’t self-evident.
151
+ - **SHOULD** avoid a competing principal `VJoyButton` (`variant="main"`) directly beside the card group — subordinate “Continue” below the choice.
152
+ - **MUST NOT** nest secondary links, tooltips, or buttons inside the card — the whole surface is one target.
153
+
154
+ ### Implementation
155
+
156
+ - **MUST** use only documented props from MCP or `JoyButtonCard.types.ts` (`disabled`, `href`, `type`, `align`).
157
+ - **MUST** style inner content with Joy typography / spacing utilities — no custom `.joy-button-card` border, padding, radius, or background overrides.
158
+ - **MUST NOT** invent props (`variant`, `size`, `selected`, `loading`, …).
159
+ - **MUST** pass `href` (or router `to`) when the card navigates; use `@click` on the pseudo-button for in-page actions.
160
+ - **SHOULD** set `target` and `rel="noopener"` on external links.
161
+ - **SHOULD** lay out groups with `VJoyWrapper` (grid / flex + gap) rather than margin hacks between cards.
162
+
163
+ ### Do / don’t
164
+
165
+ | Do | Don’t |
166
+ | --- | --- |
167
+ | Rich slot content (title + subtitle + icon) | `VJoyButton` with multi-line slot hacks |
168
+ | `VJoyRadio` outline for exclusive form choice | `VJoyButtonCard` pretending to be a radio |
169
+ | One clear action per card surface | Submit button inside the card |
170
+ | `disabled` + visible explanation | Disabled card with no reason |
171
+ | Joy tokens in slot typography | Hardcoded card border / padding in apps |
172
+
173
+ ---
174
+
175
+ ## Content
176
+
177
+ Slot copy should help users **self-select** the right path. Follow the same patterns as ZeroHeight ButtonCard content guidelines.
178
+
179
+ ### Group label (optional)
180
+
181
+ Use when the card set needs a framing question or context:
182
+
183
+ - ✅ What type of account are you creating?
184
+ - ✅ Account type
185
+ - ❌ Please select one of the following options.
186
+
187
+ Use sentence case. End questions with `?`.
188
+
189
+ ### Label (title in slot)
190
+
191
+ **Short noun phrase** naming the path or option — the first thing users read:
192
+
193
+ - ✅ Freelance profile
194
+ - ✅ Company account
195
+
196
+ Keep labels **parallel** across siblings (same grammar). Target **two to four words**, one line, sentence case, no trailing punctuation.
197
+
198
+ ### Subtitle (supporting line in slot)
199
+
200
+ Clarifies the label — answers “is this the right option for me?”:
201
+
202
+ - ✅ Complete your skills and availability.
203
+ - ✅ Manage team members and billing.
204
+
205
+ One or two lines. Don’t restate the label. Use a full sentence with a period, or a tight noun phrase without one — stay consistent within the group.
206
+
207
+ ### Icon / illustration
208
+
209
+ - **SHOULD** reinforce the label; omit if decorative only.
210
+ - **MUST NOT** use an icon as the only accessible name — pair with visible text.
211
+
212
+ ### Writing rules
213
+
214
+ | Strength | Rule |
215
+ | --- | --- |
216
+ | **MUST** | Every card has a visible primary label in the slot |
217
+ | **MUST** | Options in a group feel distinct — rewrite if labels are interchangeable |
218
+ | **MUST NOT** | Imply a “right answer” in copy unless one genuinely exists |
219
+ | **MUST NOT** | Overlap subtitles across cards — options may be too similar |
220
+ | **SHOULD** | Include a catch-all option (e.g. Other) when the set must feel exhaustive |
221
+ | **SHOULD** | Keep subtitles to one or two lines — split or redesign if more is needed |
222
+
223
+ ---
224
+
225
+ ## Accessibility
226
+
227
+ Behaviors (what must happen), not slogans.
228
+
229
+ | Behavior | Expectation |
230
+ | --- | --- |
231
+ | Role | `<a>` for links; `<div role="button">` for default in-page actions; router component defines its own semantics |
232
+ | Name | From visible text in the default slot — ensure a concise title is present |
233
+ | Keyboard | Pseudo-button: **Tab** to focus, **Enter** / **Space** activate; links: native link keys |
234
+ | Focus | Visible focus ring on `:focus-visible` — don’t remove `outline` in product CSS |
235
+ | Disabled | `aria-disabled="true"`, `tabindex="-1"`, click/key activation blocked |
236
+ | Large target | Entire card is the hit area — don’t nest separate interactive controls inside |
237
+ | External links | Meaningful link text in the slot; `rel="noopener"` when opening new tabs |
238
+
239
+ Target: **WCAG 2.2 AA** where the product requires it.
240
+
241
+ ---
242
+
243
+ ## Examples
244
+
245
+ ```vue
246
+ <script setup lang="ts">
247
+ import { RouterLink } from 'vue-router'
248
+ import { VJoyButtonCard, VJoyWrapper, VJoyIcon, VJoyLabel } from '@maltjoy/core-vue'
249
+
250
+ function openWizard() {
251
+ // navigate or open flow
252
+ }
253
+ </script>
254
+
255
+ <template>
256
+ <!-- In-page action (pseudo-button) -->
257
+ <VJoyButtonCard @click="openWizard">
258
+ <strong>Freelance profile</strong>
259
+ <span class="joy-d-block">Complete your profile</span>
260
+ </VJoyButtonCard>
261
+
262
+ <!-- External link -->
263
+ <VJoyButtonCard
264
+ href="https://www.malt.fr/help"
265
+ target="_blank"
266
+ rel="noopener"
267
+ >
268
+ <strong>Help center</strong>
269
+ <span class="joy-d-block">Browse guides and FAQs</span>
270
+ </VJoyButtonCard>
271
+
272
+ <!-- Router link -->
273
+ <VJoyButtonCard :type="RouterLink" to="/onboarding/company">
274
+ <VJoyIcon name="building" />
275
+ <strong>Company account</strong>
276
+ <span class="joy-d-block">Invite your team</span>
277
+ </VJoyButtonCard>
278
+
279
+ <!-- Centered tile -->
280
+ <VJoyButtonCard align="center" href="/onboarding/freelance">
281
+ <img src="/illustrations/freelance.svg" width="80" alt="" />
282
+ <strong class="joy-mt-2">Freelance profile</strong>
283
+ <span class="joy-d-block">Complete your profile</span>
284
+ </VJoyButtonCard>
285
+
286
+ <!-- Group layout -->
287
+ <VJoyWrapper direction="column" row-gap="4">
288
+ <VJoyLabel>What type of account are you creating?</VJoyLabel>
289
+ <VJoyWrapper direction="row" column-gap="4" wrap="wrap">
290
+ <VJoyButtonCard href="/onboarding/freelance">
291
+ <strong>Freelance</strong>
292
+ <span class="joy-d-block">Work as an independent.</span>
293
+ </VJoyButtonCard>
294
+ <VJoyButtonCard href="/onboarding/company">
295
+ <strong>Company</strong>
296
+ <span class="joy-d-block">Hire and manage projects.</span>
297
+ </VJoyButtonCard>
298
+ </VJoyWrapper>
299
+ </VJoyWrapper>
300
+
301
+ <!-- Disabled -->
302
+ <VJoyButtonCard disabled href="/unavailable">
303
+ <strong>Enterprise</strong>
304
+ <span class="joy-d-block">Contact sales to enable.</span>
305
+ </VJoyButtonCard>
306
+ </template>
307
+ ```
308
+
309
+ In Nuxt (or apps with auto-import), omit imports if components are already global. Decorative images should use `alt=""`; meaningful images need descriptive `alt` text.
310
+
311
+ ---
312
+
313
+ ## Related
314
+
315
+ | Component | Use when |
316
+ | --- | --- |
317
+ | `VJoyButton` | Short labeled actions with variants and loading |
318
+ | `VJoyIconButton` | Icon-only actions |
319
+ | `VJoyLink` | Inline text navigation |
320
+ | `VJoyRadio` / `VJoyRadioGroup` | Exactly one choice in a form (`theme="outline"` for card-like radios) |
321
+ | `VJoyCheckboxCard` / `VJoyCheckboxGroup` | Multi-select card options |
322
+ | `VJoyWrapper` | Grid or flex layout for card groups |
323
+ | `VJoyLabel` | Group question or legend above cards |
324
+ | `VJoyText` / `VJoyIcon` | Typography and icons inside the slot |
325
+
326
+ ---
327
+
328
+ ## For agents
329
+
330
+ Maps to `agentDocumentBlocks` in [`JoyButtonCard.dsds.json`](./JoyButtonCard.dsds.json). Humans can skip this section.
331
+
332
+ ### Pick the right component
333
+
334
+ | If the intent is… | Emit… | Do not emit… |
335
+ | --- | --- | --- |
336
+ | Rich CTA / navigation tile | `VJoyButtonCard` | Custom bordered `div` + `@click` |
337
+ | Short verb label (Save, Cancel) | `VJoyButton` | `VJoyButtonCard` with one word |
338
+ | Exclusive form choice | `VJoyRadio` `theme="outline"` | `VJoyButtonCard` + fake selected state |
339
+ | Multi-select cards | `VJoyCheckboxCard` | `VJoyButtonCard` |
340
+ | Icon-only | `VJoyIconButton` | Card with only an icon |
341
+ | SPA route | `VJoyButtonCard` `:type="RouterLink"` + `to` | `div` + manual router push only |
342
+
343
+ ### Hard rules
344
+
345
+ 1. **MUST** call `joy_get_component_info("VJoyButtonCard")` or read `JoyButtonCard.types.ts` before generating props.
346
+ 2. **MUST** put title and supporting copy in the **default slot** — there is no `label` prop.
347
+ 3. **MUST** use `href` or router `to` for navigation; `@click` for pseudo-button actions.
348
+ 4. **MUST NOT** invent props — no `variant`, `size`, `selected`, `loading`, or `label`.
349
+ 5. **MUST NOT** override `.joy-button-card` border, padding, radius, or background.
350
+ 6. **MUST NOT** nest buttons or links inside the card.
351
+ 7. **SHOULD** use `align="center"` for icon-above-title tiles.
352
+ 8. **SHOULD** explain `disabled` in visible nearby text.
353
+
354
+ ### Checklist
355
+
356
+ - [ ] Correct component (`VJoyButtonCard` or named alternative)
357
+ - [ ] Props from MCP / types only (`disabled`, `href`, `type`, `align`)
358
+ - [ ] Default slot has accessible primary label text
359
+ - [ ] Navigation uses `href` or `RouterLink`; actions use `@click` on pseudo-button
360
+ - [ ] No invented selected/loading/variant API
361
+ - [ ] No custom `.joy-button-card` chrome CSS
362
+ - [ ] No nested interactive controls inside the card
363
+ - [ ] Group cards are distinct; optional `VJoyLabel` for context
364
+ - [ ] Disabled state explained when user-visible
365
+
366
+ ---
367
+
368
+ ## DSDS mapping
369
+
370
+ This Markdown mirrors [`JoyButtonCard.dsds.json`](./JoyButtonCard.dsds.json) (DSDS **0.15.2**).
371
+
372
+ | Section here | DSDS block |
373
+ | --- | --- |
374
+ | When to use / not | `use-cases` |
375
+ | Anatomy / rendering modes | `anatomy` |
376
+ | Alignment / no variants | `variants` |
377
+ | Sizes (no size API) | `design-specifications` |
378
+ | States | `states` |
379
+ | Best practices / content rules | `guidelines` (+ `content`) |
380
+ | Accessibility | `accessibility` |
381
+ | Examples | `sections` |
382
+ | For agents | `agentDocumentBlocks` |
383
+ | Props detail | `api` in JSON + Joy MCP |
384
+ | Imports | `imports` in JSON |