bitboss-ui 3.0.0-beta.21 → 3.0.0-beta.22

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 (183) hide show
  1. package/README.md +55 -3
  2. package/bin/bitboss-ui-mcp.mjs +419 -9
  3. package/bin/bitboss-ui.mjs +293 -22
  4. package/dist/ai/BbAccordion.md +21 -23
  5. package/dist/ai/BbAlert.md +6 -12
  6. package/dist/ai/BbAvatar.md +60 -24
  7. package/dist/ai/BbBadge.md +12 -20
  8. package/dist/ai/BbBaseButton.md +14 -16
  9. package/dist/ai/BbBaseCheckboxIcon.md +6 -10
  10. package/dist/ai/BbBaseRadioIcon.md +6 -10
  11. package/dist/ai/BbBaseSwitchIcon.md +5 -9
  12. package/dist/ai/BbBreadcrumbs.md +23 -34
  13. package/dist/ai/BbButton.md +35 -47
  14. package/dist/ai/BbCheckbox.md +21 -37
  15. package/dist/ai/BbCheckboxGroup.md +13 -13
  16. package/dist/ai/BbCollapsible.md +14 -26
  17. package/dist/ai/BbColorInput.md +13 -23
  18. package/dist/ai/BbColorPalette.md +23 -35
  19. package/dist/ai/BbConfirm.md +17 -32
  20. package/dist/ai/BbDatePickerInput.md +15 -21
  21. package/dist/ai/BbDialog.md +159 -62
  22. package/dist/ai/BbDropdown.md +21 -25
  23. package/dist/ai/BbDropdownButton.md +13 -30
  24. package/dist/ai/BbDropzone.md +9 -11
  25. package/dist/ai/BbIcon.md +44 -33
  26. package/dist/ai/BbIndicator.md +2 -2
  27. package/dist/ai/BbNumberInput.md +18 -20
  28. package/dist/ai/BbOffCanvas.md +42 -42
  29. package/dist/ai/BbPagination.md +21 -23
  30. package/dist/ai/BbPopover.md +15 -17
  31. package/dist/ai/BbProgress.md +40 -51
  32. package/dist/ai/BbRadio.md +9 -9
  33. package/dist/ai/BbRadioGroup.md +7 -11
  34. package/dist/ai/BbRating.md +8 -12
  35. package/dist/ai/BbSelect.md +32 -36
  36. package/dist/ai/BbSelectPopover.md +19 -31
  37. package/dist/ai/BbSlider.md +15 -19
  38. package/dist/ai/BbSmoothHeight.md +16 -31
  39. package/dist/ai/BbSpinner.md +118 -74
  40. package/dist/ai/BbSwitch.md +25 -18
  41. package/dist/ai/BbSwitchGroup.md +9 -14
  42. package/dist/ai/BbTable.md +102 -55
  43. package/dist/ai/BbTabs.md +59 -82
  44. package/dist/ai/BbTag.md +12 -23
  45. package/dist/ai/BbTextInput.md +20 -26
  46. package/dist/ai/BbTextarea.md +28 -34
  47. package/dist/ai/BbToast.md +14 -34
  48. package/dist/ai/BbTooltip.md +22 -20
  49. package/dist/ai/BbTree.md +46 -72
  50. package/dist/ai/changelog.json +169 -62
  51. package/dist/ai/components.json +1467 -9
  52. package/dist/ai/composables/useConfirm.md +5 -2
  53. package/dist/ai/composables/useToast.md +4 -2
  54. package/dist/ai/guides/agent-contract.md +7 -4
  55. package/dist/ai/guides/ai-router.md +37 -22
  56. package/dist/ai/guides/component-picker.md +23 -18
  57. package/dist/ai/guides/design-language.md +141 -13
  58. package/dist/ai/guides/design-tokens.md +29 -4
  59. package/dist/ai/guides/icons-policy.md +46 -21
  60. package/dist/ai/guides/installation-and-plugin-setup.md +253 -17
  61. package/dist/ai/guides/migration/components/bb-rating.md +26 -1
  62. package/dist/ai/guides/migration/components/bb-tooltip.md +18 -0
  63. package/dist/ai/guides/migration/v2-to-v3.md +50 -23
  64. package/dist/ai/index.md +1 -1
  65. package/dist/ai/recipes/inertia/approvals-inbox.md +3 -3
  66. package/dist/ai/recipes/inertia/inline-edit-workspace.md +2 -2
  67. package/dist/ai/recipes/inertia/onboarding.md +2 -2
  68. package/dist/ai/recipes/inertia/ownership-atlas.md +6 -6
  69. package/dist/ai/recipes/inertia/record-form.md +5 -5
  70. package/dist/ai/recipes/inertia/records-workspace.md +4 -4
  71. package/dist/ai/recipes/inertia/upload-center.md +6 -6
  72. package/dist/ai/recipes/inertia/wizard-form.md +5 -5
  73. package/dist/ai/recipes/nuxt/approvals-inbox.md +3 -3
  74. package/dist/ai/recipes/nuxt/inline-edit-workspace.md +2 -2
  75. package/dist/ai/recipes/nuxt/onboarding.md +2 -2
  76. package/dist/ai/recipes/nuxt/record-form.md +5 -5
  77. package/dist/ai/recipes/nuxt/records-workspace.md +4 -4
  78. package/dist/ai/recipes/nuxt/upload-center.md +6 -6
  79. package/dist/ai/recipes/nuxt/wizard-form.md +5 -5
  80. package/dist/ai/recipes/vue/approvals-inbox.md +3 -3
  81. package/dist/ai/recipes/vue/inline-edit-workspace.md +2 -2
  82. package/dist/ai/recipes/vue/onboarding.md +2 -2
  83. package/dist/ai/recipes/vue/record-form.md +10 -10
  84. package/dist/ai/recipes/vue/records-workspace.md +4 -4
  85. package/dist/ai/recipes/vue/upload-center.md +6 -6
  86. package/dist/ai/recipes/vue/wizard-form.md +5 -5
  87. package/dist/ai/source/BbBadge.md +1 -1
  88. package/dist/ai/source/BbBaseCheckbox.md +1 -1
  89. package/dist/ai/source/BbBaseDatePicker.md +5 -5
  90. package/dist/ai/source/BbBaseDatePickerInput.md +2 -2
  91. package/dist/ai/source/BbBaseRadio.md +1 -1
  92. package/dist/ai/source/BbBaseRating.md +5 -5
  93. package/dist/ai/source/BbBaseSelect.md +1 -1
  94. package/dist/ai/source/BbBaseSlider.md +5 -5
  95. package/dist/ai/source/BbBaseSwitch.md +1 -1
  96. package/dist/ai/source/BbBaseTimePickerInput.md +1 -1
  97. package/dist/ai/source/BbBreadcrumbs.md +20 -1
  98. package/dist/ai/source/BbButton.md +2 -2
  99. package/dist/ai/source/BbDialog.md +39 -8
  100. package/dist/ai/source/BbDropdown.md +1 -1
  101. package/dist/ai/source/BbDropzone.md +5 -5
  102. package/dist/ai/source/BbIcon.md +23 -10
  103. package/dist/ai/source/BbOffCanvas.md +50 -28
  104. package/dist/ai/source/BbPagination.md +2 -2
  105. package/dist/ai/source/BbPopover.md +12 -1
  106. package/dist/ai/source/BbSelectPopover.md +45 -7
  107. package/dist/ai/source/BbSpinner.md +2 -2
  108. package/dist/ai/source/BbTable.md +11 -11
  109. package/dist/ai/source/BbTooltip.md +57 -10
  110. package/dist/ai/source/ChipsBox.md +1 -1
  111. package/dist/ai/source/CommaBox.md +1 -1
  112. package/dist/ai/source/GuidesComponent.md +14 -7
  113. package/dist/ai/source/OptionsContainer.md +1 -1
  114. package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +1 -1
  115. package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +1 -1
  116. package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
  117. package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +4 -4
  118. package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +1 -1
  119. package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
  120. package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +1 -1
  121. package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +1 -1
  122. package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +3 -3
  123. package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  124. package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +3 -3
  125. package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
  126. package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
  127. package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +71 -71
  128. package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +2 -2
  129. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +102 -97
  130. package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +1 -1
  131. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +58 -58
  132. package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +2 -2
  133. package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +33 -35
  134. package/dist/components/BbIcon/iconCache.d.ts +30 -0
  135. package/dist/components/BbIcon/iconCache.js +21 -3
  136. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +57 -56
  137. package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +2 -2
  138. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +138 -138
  139. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +43 -43
  140. package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +1 -1
  141. package/dist/components/BbSpinner/types.d.ts +1 -1
  142. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +8 -8
  143. package/dist/components/BbTable/BbTableDataRow.js +2 -2
  144. package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +62 -62
  145. package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +81 -78
  146. package/dist/components/BbTooltip/types.d.ts +8 -1
  147. package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +1 -1
  148. package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +1 -1
  149. package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  150. package/dist/composables/useConfirm.d.ts +22 -0
  151. package/dist/composables/useConfirm.js +43 -40
  152. package/dist/composables/useSafeAreaInternal.js +1 -1
  153. package/dist/composables/useToast.d.ts +16 -0
  154. package/dist/composables/useToast.js +20 -16
  155. package/dist/composables/useViewportWidth.d.ts +20 -6
  156. package/dist/composables/useViewportWidth.js +8 -7
  157. package/dist/icon-scan.d.ts +276 -0
  158. package/dist/icon-scan.js +485 -0
  159. package/dist/icons-whitelist.d.ts +5 -4
  160. package/dist/llms-full.txt +1979 -1527
  161. package/dist/llms-medium.txt +461 -74
  162. package/dist/llms.txt +1 -1
  163. package/dist/nuxt.js +10 -7
  164. package/dist/plugin.js +1 -0
  165. package/dist/runtime/nuxt-plugin.js +2 -1
  166. package/dist/styles.css +1 -1
  167. package/dist/types/Config.d.ts +18 -0
  168. package/dist/utils/cssLayers.d.ts +91 -0
  169. package/dist/utils/cssLayers.js +43 -0
  170. package/dist/utils/injectLibraryStyles.d.ts +3 -1
  171. package/dist/utils/injectLibraryStyles.js +24 -23
  172. package/dist/vite-plugin.d.ts +74 -11
  173. package/dist/vite.js +292 -253
  174. package/llms.txt +1 -1
  175. package/package.json +7 -5
  176. package/scripts/lib/ensure-manifest.mjs +28 -5
  177. package/scripts/lib/eslint-plugin.d.ts +35 -0
  178. package/scripts/lib/eslint-plugin.mjs +299 -3
  179. package/scripts/lib/hand-roll-hints.mjs +50 -6
  180. package/scripts/lib/html-attributes.mjs +42 -0
  181. package/scripts/lib/mcp-config.mjs +38 -34
  182. package/scripts/lib/public-type-exports.mjs +246 -0
  183. package/scripts/lib/validate-bb-markup.mjs +205 -12
@@ -68,19 +68,19 @@ route paths or `to`.
68
68
  ```vue
69
69
  <template>
70
70
  <div
71
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
71
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
72
72
  >
73
- <div class="space-y-2 border-b border-[color:var(--bb-border)] p-3">
73
+ <div class="space-y-2 border-b border-(--bb-border) p-3">
74
74
  <BbBreadcrumbs :items="trail" :offset-width="54" />
75
75
  <div class="flex flex-col gap-0.5">
76
76
  <p class="text-sm font-medium">Members</p>
77
- <p class="text-xs text-[color:var(--bb-text-muted)]">
77
+ <p class="text-xs text-(--bb-text-muted)">
78
78
  Northwind Billing · invite and manage workspace access.
79
79
  </p>
80
80
  </div>
81
81
  </div>
82
82
 
83
- <ul class="divide-y divide-[color:var(--bb-border)] px-3">
83
+ <ul class="divide-y divide-(--bb-border) px-3">
84
84
  <li
85
85
  v-for="member in members"
86
86
  :key="member.email"
@@ -88,11 +88,11 @@ route paths or `to`.
88
88
  >
89
89
  <span class="min-w-0 truncate">
90
90
  <span class="font-medium">{{ member.name }}</span>
91
- <span class="text-xs text-[color:var(--bb-text-muted)]">
91
+ <span class="text-xs text-(--bb-text-muted)">
92
92
  · {{ member.email }}</span
93
93
  >
94
94
  </span>
95
- <span class="shrink-0 text-xs text-[color:var(--bb-text-muted)]">{{
95
+ <span class="shrink-0 text-xs text-(--bb-text-muted)">{{
96
96
  member.role
97
97
  }}</span>
98
98
  </li>
@@ -228,7 +228,7 @@ When slot (or icon) content adds width the estimator cannot see, set
228
228
  ]"
229
229
  >
230
230
  <template #prepend>
231
- <span class="text-xs text-[color:var(--bb-text-muted)]">You are here</span>
231
+ <span class="text-xs text-(--bb-text-muted)">You are here</span>
232
232
  </template>
233
233
  <template #item:prepend="{ item }">
234
234
  <BbIcon
@@ -249,19 +249,15 @@ at. Keep them `size="xs"` and account for them with `offset-width`:
249
249
  ```vue
250
250
  <template>
251
251
  <div
252
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
252
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
253
253
  >
254
254
  <div
255
- class="flex items-center gap-2 border-b border-[color:var(--bb-border)] px-2.5 py-1.5"
255
+ class="flex items-center gap-2 border-b border-(--bb-border) px-2.5 py-1.5"
256
256
  >
257
- <span
258
- class="min-w-0 flex-auto truncate text-xs text-[color:var(--bb-text-muted)]"
259
- >
257
+ <span class="min-w-0 flex-auto truncate text-xs text-(--bb-text-muted)">
260
258
  acme-ui / src / components
261
259
  </span>
262
- <span class="shrink-0 text-xs text-[color:var(--bb-text-muted)]"
263
- >12.4 KB · TSX</span
264
- >
260
+ <span class="shrink-0 text-xs text-(--bb-text-muted)">12.4 KB · TSX</span>
265
261
  </div>
266
262
 
267
263
  <div class="p-3">
@@ -326,7 +322,7 @@ keys that collide with the reserved slots (`prepend`, `append`, `divider`,
326
322
  <template #release="{ text }">
327
323
  <span class="inline-flex items-center gap-1.5">
328
324
  <span
329
- class="size-2 rounded-full bg-[color:var(--bb-primary)]"
325
+ class="size-2 rounded-full bg-(--bb-primary)"
330
326
  ></span>
331
327
  {{ text }}
332
328
  </span>
@@ -357,7 +353,7 @@ wider than `…`, pair it with `ellipsis-width`.
357
353
  ]"
358
354
  >
359
355
  <template #divider>
360
- <span class="text-[color:var(--bb-text-muted)]">/</span>
356
+ <span class="text-(--bb-text-muted)">/</span>
361
357
  </template>
362
358
  <template #ellipsis="{ overflowCount }">
363
359
  <span class="text-xs font-medium">+{{ overflowCount }}</span>
@@ -379,40 +375,33 @@ folded into the overflow menu keep their `onClick` too.
379
375
  ```vue
380
376
  <template>
381
377
  <div
382
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
378
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
383
379
  >
384
380
  <div
385
- class="flex items-center gap-1.5 border-b border-[color:var(--bb-border)] px-2.5 py-1.5"
381
+ class="flex items-center gap-1.5 border-b border-(--bb-border) px-2.5 py-1.5"
386
382
  >
387
- <BbIcon
388
- class="text-[color:var(--bb-text-muted)]"
389
- icon="lucide:folder"
390
- size="sm"
391
- />
383
+ <BbIcon class="text-(--bb-text-muted)" icon="lucide:folder" size="sm" />
392
384
  <span class="text-xs font-medium">Files</span>
393
- <span class="ml-auto text-xs text-[color:var(--bb-text-muted)]"
394
- >northwind-docs</span
395
- >
385
+ <span class="ml-auto text-xs text-(--bb-text-muted)">northwind-docs</span>
396
386
  </div>
397
387
 
398
388
  <div class="space-y-2 p-3">
399
389
  <BbBreadcrumbs :items="items" :offset-width="iconOffset" />
400
- <ul class="divide-y divide-[color:var(--bb-border)] text-sm">
390
+ <ul class="divide-y divide-(--bb-border) text-sm">
401
391
  <li
402
392
  v-for="entry in entries"
403
393
  :key="entry.name"
404
394
  class="flex items-center gap-1.5 py-1.5"
405
395
  >
406
396
  <BbIcon
407
- class="shrink-0 text-[color:var(--bb-text-muted)]"
397
+ class="shrink-0 text-(--bb-text-muted)"
408
398
  :icon="entry.kind === 'folder' ? 'lucide:folder' : 'lucide:file'"
409
399
  size="sm"
410
400
  />
411
401
  <span class="min-w-0 truncate">{{ entry.name }}</span>
412
- <span
413
- class="ml-auto shrink-0 text-xs text-[color:var(--bb-text-muted)]"
414
- >{{ entry.meta }}</span
415
- >
402
+ <span class="ml-auto shrink-0 text-xs text-(--bb-text-muted)">{{
403
+ entry.meta
404
+ }}</span>
416
405
  </li>
417
406
  </ul>
418
407
  </div>
@@ -563,7 +552,7 @@ overflow calculation runs after hydration once real widths exist.
563
552
  | `ellipsisWidth` | `number \| undefined` | | | Explicit width used by the estimator for the ellipsis activator. |
564
553
  | `estimationBias` | `number \| undefined` | `1.12` | | Multiplier applied to each estimated item width. A higher value will result in more over-estimation. |
565
554
  | `gapWidth` | `number \| undefined` | `4` | | Horizontal gap between breadcrumb elements in pixels. |
566
- | `items` | `Item[]` | | yes | Array of items that describe how the breadcrumbs should behave. Compatible with navigating through `a` or `router-link`. |
555
+ | `items` | `BbBreadcrumbsItem[]` | | yes | Array of items that describe how the breadcrumbs should behave. Compatible with navigating through `a` or `router-link`. |
567
556
  | `offsetWidth` | `number \| undefined` | `0` | | Extra one-off width in pixels added to the total estimate (e.g. prepend/append/item slot content). |
568
557
 
569
558
  ## Events
@@ -125,16 +125,16 @@ accident; set `type="submit"` for the one that should, and let the form's
125
125
 
126
126
  ```vue
127
127
  <form
128
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
128
+ class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3"
129
129
  @submit.prevent="state.sent = true"
130
130
  >
131
131
  <div class="flex flex-col gap-0.5">
132
132
  <span class="text-sm font-medium">Request workspace access</span>
133
- <span class="text-xs text-[color:var(--bb-text-muted)]">Acme Studio · billing team</span>
133
+ <span class="text-xs text-(--bb-text-muted)">Acme Studio · billing team</span>
134
134
  </div>
135
135
  <div class="flex items-center gap-2">
136
136
  <BbButton type="submit" variant="primary">Request access</BbButton>
137
- <span class="text-xs text-[color:var(--bb-text-muted)]">
137
+ <span class="text-xs text-(--bb-text-muted)">
138
138
  {{ state.sent ? 'Request sent — check your email.' : 'Owner will approve by email.' }}
139
139
  </span>
140
140
  </div>
@@ -201,7 +201,7 @@ padding, icon size and radius stay at the `default` entry's values.
201
201
 
202
202
  Use `prepend:icon` / `append:icon` for labelled buttons, and `icon` for
203
203
  icon-only buttons. Accessibility here is **symmetric**: the label content still
204
- matters, it is just rendered `sr-only` when `icon` is set. So give an icon-only
204
+ matters, it is just rendered visually hidden (`bb-sr-only`) when `icon` is set. So give an icon-only
205
205
  button its accessible name the same way as any other — through the default slot
206
206
  (or `text`): `<BbButton icon="lucide:settings">Settings</BbButton>` renders no
207
207
  visible text but announces "Settings". `aria-label` is plan B — an override for
@@ -214,13 +214,13 @@ the `lucide:*` set (see the icons policy).
214
214
 
215
215
  ```vue
216
216
  <div
217
- class="flex max-w-sm items-center justify-between gap-2 rounded-[var(--bb-radius)] border p-3"
217
+ class="flex max-w-sm items-center justify-between gap-2 rounded-(--bb-radius) border p-3"
218
218
  >
219
219
  <span class="text-sm font-medium">Invoices</span>
220
220
  <div class="flex items-center gap-1.5">
221
221
  <BbButton variant="primary" prepend:icon="lucide:plus">New invoice</BbButton>
222
222
  <BbButton variant="outline" append:icon="lucide:download">Export</BbButton>
223
- <!-- Label comes from slot content, rendered sr-only — no aria-label needed. -->
223
+ <!-- Label comes from slot content, rendered visually hidden — no aria-label needed. -->
224
224
  <BbButton variant="ghost" icon="lucide:settings">Settings</BbButton>
225
225
  </div>
226
226
  </div>
@@ -286,12 +286,10 @@ own semantics, and there is no arrow-key roving between them.
286
286
 
287
287
  ```vue
288
288
  <template>
289
- <div
290
- class="flex max-w-md flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
291
- >
289
+ <div class="flex max-w-md flex-col gap-3 rounded-(--bb-radius) border p-3">
292
290
  <div class="flex flex-col gap-0.5">
293
291
  <span class="text-sm font-medium">Issues</span>
294
- <span class="text-xs text-[color:var(--bb-text-muted)]">
292
+ <span class="text-xs text-(--bb-text-muted)">
295
293
  Acme workspace ·
296
294
  {{
297
295
  statusFilter
@@ -303,7 +301,7 @@ own semantics, and there is no arrow-key roving between them.
303
301
  </div>
304
302
 
305
303
  <div class="flex flex-col gap-1.5">
306
- <span class="text-xs text-[color:var(--bb-text-muted)]">Status</span>
304
+ <span class="text-xs text-(--bb-text-muted)">Status</span>
307
305
  <div>
308
306
  <BbButton
309
307
  v-for="status in statuses"
@@ -321,7 +319,7 @@ own semantics, and there is no arrow-key roving between them.
321
319
  </div>
322
320
 
323
321
  <div class="flex flex-col gap-1.5">
324
- <span class="text-xs text-[color:var(--bb-text-muted)]">View</span>
322
+ <span class="text-xs text-(--bb-text-muted)">View</span>
325
323
  <div>
326
324
  <BbButton
327
325
  v-for="view in views"
@@ -377,16 +375,10 @@ stays on as the accessible name:
377
375
 
378
376
  ```vue
379
377
  <template>
380
- <div
381
- class="flex max-w-sm flex-col gap-2 rounded-[var(--bb-radius)] border p-3"
382
- >
378
+ <div class="flex max-w-sm flex-col gap-2 rounded-(--bb-radius) border p-3">
383
379
  <div class="flex items-center justify-between gap-2">
384
- <span class="text-xs font-medium text-[color:var(--bb-text-muted)]"
385
- >Format</span
386
- >
387
- <span class="text-xs text-[color:var(--bb-text-muted)]">{{
388
- summary
389
- }}</span>
380
+ <span class="text-xs font-medium text-(--bb-text-muted)">Format</span>
381
+ <span class="text-xs text-(--bb-text-muted)">{{ summary }}</span>
390
382
  </div>
391
383
  <div>
392
384
  <BbButton
@@ -462,12 +454,10 @@ hand-placed `BbSpinner`.
462
454
 
463
455
  ```vue
464
456
  <template>
465
- <div
466
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
467
- >
457
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
468
458
  <div class="flex flex-col gap-0.5">
469
459
  <span class="text-sm font-medium">Invoice INV-1042</span>
470
- <span class="text-xs text-[color:var(--bb-text-muted)]">
460
+ <span class="text-xs text-(--bb-text-muted)">
471
461
  Acme Studio · draft · due Jul 31, 2026
472
462
  </span>
473
463
  </div>
@@ -475,9 +465,7 @@ hand-placed `BbSpinner`.
475
465
  <BbButton prepend:icon="lucide:save" variant="primary" @click="save">
476
466
  Save changes
477
467
  </BbButton>
478
- <span class="text-xs text-[color:var(--bb-text-muted)]">{{
479
- status
480
- }}</span>
468
+ <span class="text-xs text-(--bb-text-muted)">{{ status }}</span>
481
469
  </div>
482
470
  </div>
483
471
  </template>
@@ -507,7 +495,7 @@ Escape hatches, when the default isn't right:
507
495
  loading state, for controls that must stay interactive (this is how the
508
496
  `BbDropdownButton` caret stays usable while the primary action runs).
509
497
 
510
- The sr-only loading announcement is localized via the plugin's `locale`
498
+ The visually-hidden loading announcement is localized via the plugin's `locale`
511
499
  option. `disabled` and `loading` are independent: `disabled` wins the muted
512
500
  styling and native blocking; don't set both.
513
501
 
@@ -515,11 +503,11 @@ styling and native blocking; don't set both.
515
503
 
516
504
  ```vue
517
505
  <div
518
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
506
+ class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3"
519
507
  >
520
508
  <div class="flex flex-col gap-0.5">
521
509
  <span class="text-sm font-medium">Release notes</span>
522
- <span class="text-xs text-[color:var(--bb-text-muted)]">v2.4.0 · draft in review</span>
510
+ <span class="text-xs text-(--bb-text-muted)">v2.4.0 · draft in review</span>
523
511
  </div>
524
512
  <div class="flex items-center gap-2">
525
513
  <BbButton :loading="state.publishing" variant="primary">Publish</BbButton>
@@ -555,11 +543,11 @@ adapts to what you pass:
555
543
 
556
544
  ```vue
557
545
  <div
558
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
546
+ class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3"
559
547
  >
560
548
  <div class="flex flex-col gap-0.5">
561
549
  <span class="text-sm font-medium">Q2 revenue report</span>
562
- <span class="text-xs text-[color:var(--bb-text-muted)]">Ready · exported Jul 12, 2026</span>
550
+ <span class="text-xs text-(--bb-text-muted)">Ready · exported Jul 12, 2026</span>
563
551
  </div>
564
552
  <div class="flex flex-wrap items-center gap-1.5">
565
553
  <BbButton variant="outline" href="#reports">View report</BbButton>
@@ -656,7 +644,12 @@ concern:
656
644
  - **Lifecycle hooks** — `onBefore`, `onStart`, `onProgress`, `onSuccess`,
657
645
  `onError`, `onCancel`, `onFinish`, plus `onCancelToken` to capture a cancel
658
646
  handle and `onPrefetching`/`onPrefetched` for the prefetch pair. Bind them as
659
- props (`:on-success="..."`), not `@` listeners.
647
+ props (`:on-success="..."`), not `@` listeners. The listener spelling
648
+ (`@success`) does reach the same prop — Vue compiles it to an `onSuccess`
649
+ vnode entry and prop resolution claims it — but it is a trap, not a synonym:
650
+ a modifier (`@success.once`) has nothing to attach to, and a second handler
651
+ for the same name silently becomes an array the prop is not typed for.
652
+ `no-unknown-event` warns and names the prop form.
660
653
 
661
654
  **`onError` here means VALIDATION errors** — the ordinary 422 — and it is the
662
655
  only failure callback `<Link>` accepts. Inertia's other two,
@@ -707,12 +700,10 @@ library's public types are not allowed to import.
707
700
 
708
701
  ```vue
709
702
  <template>
710
- <div
711
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
712
- >
703
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
713
704
  <div class="flex flex-col gap-0.5">
714
705
  <span class="text-sm font-medium">API keys</span>
715
- <span class="text-xs text-[color:var(--bb-text-muted)]">
706
+ <span class="text-xs text-(--bb-text-muted)">
716
707
  <span data-count>{{ keys.length }}</span> active · revoking is immediate
717
708
  </span>
718
709
  </div>
@@ -724,7 +715,7 @@ library's public types are not allowed to import.
724
715
  >
725
716
  <div class="flex min-w-0 flex-1 flex-col">
726
717
  <span class="truncate text-sm font-medium">{{ key.label }}</span>
727
- <span class="font-mono text-xs text-[color:var(--bb-text-muted)]">
718
+ <span class="font-mono text-xs text-(--bb-text-muted)">
728
719
  {{ key.masked }} · created {{ key.created }}
729
720
  </span>
730
721
  </div>
@@ -737,10 +728,7 @@ library's public types are not allowed to import.
737
728
  Revoke
738
729
  </BbButton>
739
730
  </div>
740
- <p
741
- v-if="keys.length === 0"
742
- class="text-xs text-[color:var(--bb-text-muted)]"
743
- >
731
+ <p v-if="keys.length === 0" class="text-xs text-(--bb-text-muted)">
744
732
  No active keys.
745
733
  </p>
746
734
 
@@ -799,12 +787,12 @@ const revoke = async (key: ApiKey) => {
799
787
 
800
788
  ```vue
801
789
  <div
802
- class="flex max-w-sm items-center justify-between gap-2 rounded-[var(--bb-radius)] border p-3"
790
+ class="flex max-w-sm items-center justify-between gap-2 rounded-(--bb-radius) border p-3"
803
791
  >
804
792
  <span class="text-sm font-medium">Acme Studio</span>
805
793
  <div class="flex items-center gap-1.5">
806
794
  <BbIndicator :text="3" variant="destructive">
807
- <!-- Accessible name from slot content, rendered sr-only. -->
795
+ <!-- Accessible name from slot content, rendered visually hidden. -->
808
796
  <BbButton variant="outline" icon="lucide:bell">Notifications</BbButton>
809
797
  </BbIndicator>
810
798
  <BbButton
@@ -856,13 +844,13 @@ Anti-patterns:
856
844
  ### Accessibility
857
845
 
858
846
  - Icon-only buttons still need an accessible name, but it works like any other
859
- button: put it in the default slot (or `text`) and it renders `sr-only` when
847
+ button: put it in the default slot (or `text`) and it renders visually hidden (`bb-sr-only`) when
860
848
  `icon` is set — not removed. Use `aria-label` only to override that name, not
861
849
  as the default mechanism. Never ship an icon-only button with no name at all.
862
850
  - Toggles expose `aria-pressed` (`true`/`false`) automatically — never add your
863
851
  own.
864
852
  - While loading: `aria-disabled="true"`, `tabindex="-1"`, and a localized
865
- sr-only loading announcement.
853
+ visually-hidden loading announcement.
866
854
  - Disabled links render with `role="link"`, `aria-disabled="true"`, no `href`,
867
855
  and leave the tab order.
868
856
  - The exact-active link gets `aria-current` (default `"page"`) on every
@@ -36,7 +36,7 @@ Pick the right sibling instead when the shape changes:
36
36
  **The states you'll actually ship**
37
37
 
38
38
  ```vue
39
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-3">
39
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-3">
40
40
  <BbCheckbox label="Email me product updates" name="s-on" checked />
41
41
  <BbCheckbox
42
42
  label="Send a weekly digest"
@@ -104,12 +104,10 @@ and `false-value` when unchecking.
104
104
 
105
105
  ```vue
106
106
  <template>
107
- <div
108
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
109
- >
107
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
110
108
  <div class="flex flex-col gap-0.5">
111
109
  <p class="text-sm font-medium">Privacy consent</p>
112
- <p class="text-xs text-[color:var(--bb-text-muted)]">
110
+ <p class="text-xs text-(--bb-text-muted)">
113
111
  Posted as a domain value — not a raw boolean.
114
112
  </p>
115
113
  </div>
@@ -121,9 +119,9 @@ and `false-value` when unchecking.
121
119
  submit-when-false
122
120
  true-value="accepted"
123
121
  />
124
- <p class="text-xs text-[color:var(--bb-text-muted)]">
122
+ <p class="text-xs text-(--bb-text-muted)">
125
123
  Submitted value:
126
- <span class="font-medium text-[color:var(--bb-text)]">{{ consent }}</span>
124
+ <span class="font-medium text-(--bb-text)">{{ consent }}</span>
127
125
  </p>
128
126
  </div>
129
127
  </template>
@@ -190,12 +188,10 @@ than toggling. The slot receives `{ text, hasErrors }`.
190
188
 
191
189
  ```vue
192
190
  <template>
193
- <div
194
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
195
- >
191
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
196
192
  <div class="flex flex-col gap-0.5">
197
193
  <p class="text-sm font-medium">Accept terms</p>
198
- <p class="text-xs text-[color:var(--bb-text-muted)]">
194
+ <p class="text-xs text-(--bb-text-muted)">
199
195
  Links stay real anchors — clicks navigate instead of toggling.
200
196
  </p>
201
197
  </div>
@@ -236,12 +232,10 @@ the label `text`.
236
232
 
237
233
  ```vue
238
234
  <template>
239
- <div
240
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
241
- >
235
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
242
236
  <div class="flex flex-col gap-0.5">
243
237
  <p class="text-sm font-medium">Critical alerts</p>
244
- <p class="text-xs text-[color:var(--bb-text-muted)]">
238
+ <p class="text-xs text-(--bb-text-muted)">
245
239
  Custom icon owns both states — checked and unchecked.
246
240
  </p>
247
241
  </div>
@@ -252,14 +246,12 @@ the label `text`.
252
246
  >
253
247
  <template #icon="{ checked, focusVisible }">
254
248
  <span
255
- class="inline-flex h-5 w-5 items-center justify-center rounded-[var(--bb-radius-sm)]"
249
+ class="inline-flex h-5 w-5 items-center justify-center rounded-(--bb-radius-sm)"
256
250
  :class="[
257
251
  checked
258
- ? 'bg-[color:var(--bb-primary)] text-[color:var(--bb-primary-fg)]'
259
- : 'border border-[color:var(--bb-border)] text-[color:var(--bb-text-muted)]',
260
- focusVisible
261
- ? 'ring-2 ring-[color:var(--bb-ring)] ring-offset-1'
262
- : '',
252
+ ? 'bg-(--bb-primary) text-(--bb-primary-fg)'
253
+ : 'border border-(--bb-border) text-(--bb-text-muted)',
254
+ focusVisible ? 'ring-2 ring-(--bb-ring) ring-offset-1' : '',
263
255
  ]"
264
256
  >
265
257
  <BbIcon icon="lucide:bell" size="xs" />
@@ -288,12 +280,10 @@ even though a Save button posts them; the Save button is not what decides it.
288
280
 
289
281
  ```vue
290
282
  <template>
291
- <div
292
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
293
- >
283
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
294
284
  <div class="flex flex-col gap-0.5">
295
285
  <p class="text-sm font-medium">Email notifications</p>
296
- <p class="text-xs text-[color:var(--bb-text-muted)]">
286
+ <p class="text-xs text-(--bb-text-muted)">
297
287
  Saved with your account — nothing changes until you hit Save.
298
288
  </p>
299
289
  </div>
@@ -320,9 +310,7 @@ even though a Save button posts them; the Save button is not what decides it.
320
310
  </div>
321
311
  <div class="flex items-center gap-2">
322
312
  <BbButton variant="primary" @click="save">Save preferences</BbButton>
323
- <span class="text-xs text-[color:var(--bb-text-muted)]">{{
324
- status
325
- }}</span>
313
+ <span class="text-xs text-(--bb-text-muted)">{{ status }}</span>
326
314
  </div>
327
315
  </div>
328
316
  </template>
@@ -358,12 +346,10 @@ the disabled state can simply tell the truth up front.
358
346
 
359
347
  ```vue
360
348
  <template>
361
- <div
362
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
363
- >
349
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
364
350
  <div class="flex flex-col gap-0.5">
365
351
  <p class="text-sm font-medium">Create your account</p>
366
- <p class="text-xs text-[color:var(--bb-text-muted)]">
352
+ <p class="text-xs text-(--bb-text-muted)">
367
353
  Last step — accept the terms to unlock Create account.
368
354
  </p>
369
355
  </div>
@@ -410,12 +396,10 @@ for the children.
410
396
 
411
397
  ```vue
412
398
  <template>
413
- <div
414
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
415
- >
399
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
416
400
  <div class="flex flex-col gap-0.5">
417
401
  <p class="text-sm font-medium">Token permissions</p>
418
- <p class="text-xs text-[color:var(--bb-text-muted)]">
402
+ <p class="text-xs text-(--bb-text-muted)">
419
403
  Scopes this personal access token can use.
420
404
  </p>
421
405
  </div>
@@ -437,7 +421,7 @@ for the children.
437
421
  legend="Scopes"
438
422
  name="scope-list"
439
423
  />
440
- <p class="selection-summary text-xs text-[color:var(--bb-text-muted)]">
424
+ <p class="selection-summary text-xs text-(--bb-text-muted)">
441
425
  {{ granted.length }} of {{ scopes.length }} scopes granted
442
426
  </p>
443
427
  </fieldset>
@@ -39,10 +39,10 @@ mapped (here, channel ids), which is what you send back to the server.
39
39
 
40
40
  ```vue
41
41
  <template>
42
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-3">
42
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-3">
43
43
  <div>
44
44
  <h3 class="text-sm font-medium">Notification preferences</h3>
45
- <p class="text-xs text-[color:var(--bb-text-muted)]">
45
+ <p class="text-xs text-(--bb-text-muted)">
46
46
  We only send what you opt into.
47
47
  </p>
48
48
  </div>
@@ -57,7 +57,7 @@ mapped (here, channel ids), which is what you send back to the server.
57
57
  name="channels"
58
58
  persistent-hint
59
59
  />
60
- <p class="channel-summary text-xs text-[color:var(--bb-text-muted)]">
60
+ <p class="channel-summary text-xs text-(--bb-text-muted)">
61
61
  {{ selected.length }} channel{{ selected.length === 1 ? '' : 's' }} on
62
62
  </p>
63
63
  </div>
@@ -139,10 +139,10 @@ choice out. Pair it with a `hint` that states the limit.
139
139
 
140
140
  ```vue
141
141
  <template>
142
- <div class="grid max-w-md gap-3 rounded-[var(--bb-radius)] border p-3">
142
+ <div class="grid max-w-md gap-3 rounded-(--bb-radius) border p-3">
143
143
  <div>
144
144
  <h3 class="text-sm font-medium">Personalize your feed</h3>
145
- <p class="text-xs text-[color:var(--bb-text-muted)]">
145
+ <p class="text-xs text-(--bb-text-muted)">
146
146
  Pick up to three topics to follow.
147
147
  </p>
148
148
  </div>
@@ -157,7 +157,7 @@ choice out. Pair it with a `hint` that states the limit.
157
157
  name="topics"
158
158
  persistent-hint
159
159
  />
160
- <p class="text-xs text-[color:var(--bb-text-muted)]">
160
+ <p class="text-xs text-(--bb-text-muted)">
161
161
  {{ topics.length }} / 3 selected
162
162
  </p>
163
163
  </div>
@@ -192,10 +192,10 @@ on the row, feed that field to the predicate.
192
192
 
193
193
  ```vue
194
194
  <template>
195
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-3">
195
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-3">
196
196
  <div>
197
197
  <h3 class="text-sm font-medium">Delivery channels</h3>
198
- <p class="text-xs text-[color:var(--bb-text-muted)]">
198
+ <p class="text-xs text-(--bb-text-muted)">
199
199
  Unconfigured channels stay unavailable until setup finishes.
200
200
  </p>
201
201
  </div>
@@ -277,10 +277,10 @@ border) — floating mode is not supported for checkbox groups.
277
277
 
278
278
  ```vue
279
279
  <template>
280
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-3">
280
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-3">
281
281
  <div>
282
282
  <h3 class="text-sm font-medium">Privacy & contact</h3>
283
- <p class="text-xs text-[color:var(--bb-text-muted)]">
283
+ <p class="text-xs text-(--bb-text-muted)">
284
284
  Choose how we may reach you about your account.
285
285
  </p>
286
286
  </div>
@@ -346,10 +346,10 @@ Read-only, with no cleanup code.
346
346
 
347
347
  ```vue
348
348
  <template>
349
- <div class="grid max-w-md gap-3 rounded-[var(--bb-radius)] border p-3">
349
+ <div class="grid max-w-md gap-3 rounded-(--bb-radius) border p-3">
350
350
  <div>
351
351
  <h3 class="text-sm font-medium">Create access token</h3>
352
- <p class="text-xs text-[color:var(--bb-text-muted)]">
352
+ <p class="text-xs text-(--bb-text-muted)">
353
353
  Scopes stay coherent with the selected role.
354
354
  </p>
355
355
  </div>
@@ -378,7 +378,7 @@ Read-only, with no cleanup code.
378
378
  name="token-scopes"
379
379
  persistent-hint
380
380
  />
381
- <p class="scope-summary text-xs text-[color:var(--bb-text-muted)]">
381
+ <p class="scope-summary text-xs text-(--bb-text-muted)">
382
382
  {{ scopes.length }} scope{{ scopes.length === 1 ? '' : 's' }} granted
383
383
  </p>
384
384
  </div>