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
@@ -42,7 +42,7 @@ control instead.
42
42
  ```vue
43
43
  <div class="space-y-2">
44
44
  <BbPagination v-model="state.page" :total-pages="24" />
45
- <p class="text-xs text-[color:var(--bb-text-muted)]">On page {{ state.page ?? 1 }} of 24.</p>
45
+ <p class="text-xs text-(--bb-text-muted)">On page {{ state.page ?? 1 }} of 24.</p>
46
46
  </div>
47
47
  ```
48
48
 
@@ -96,29 +96,27 @@ list or table it drives.
96
96
  <!-- Manual wire-up: the pager reports the page; you slice (or fetch) from it.
97
97
  Prefer this over `table-id` when the list is not a BbTable. -->
98
98
  <div
99
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
99
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
100
100
  >
101
- <ul class="divide-y divide-[color:var(--bb-border)]">
101
+ <ul class="divide-y divide-(--bb-border)">
102
102
  <li
103
103
  v-for="invoice in pageItems"
104
104
  :key="invoice.id"
105
105
  class="flex items-center justify-between gap-2 px-3 py-2 text-sm"
106
106
  >
107
107
  <span class="font-medium tabular-nums">{{ invoice.number }}</span>
108
- <span
109
- class="min-w-0 flex-1 truncate text-[color:var(--bb-text-muted)]"
110
- >{{ invoice.customer }}</span
111
- >
112
- <span
113
- class="shrink-0 text-xs tabular-nums text-[color:var(--bb-text-muted)]"
114
- >{{ invoice.amount }}</span
115
- >
108
+ <span class="min-w-0 flex-1 truncate text-(--bb-text-muted)">{{
109
+ invoice.customer
110
+ }}</span>
111
+ <span class="shrink-0 text-xs text-(--bb-text-muted) tabular-nums">{{
112
+ invoice.amount
113
+ }}</span>
116
114
  </li>
117
115
  </ul>
118
116
  <div
119
- class="flex flex-wrap items-center justify-between gap-2 border-t border-[color:var(--bb-border)] px-3 py-2"
117
+ class="flex flex-wrap items-center justify-between gap-2 border-t border-(--bb-border) px-3 py-2"
120
118
  >
121
- <p class="text-xs text-[color:var(--bb-text-muted)]" data-test="range">
119
+ <p class="text-xs text-(--bb-text-muted)" data-test="range">
122
120
  {{ rangeStart }}–{{ rangeEnd }} of {{ invoices.length }}
123
121
  </p>
124
122
  <BbPagination
@@ -178,10 +176,10 @@ their wiring; the slots only swap what is painted inside them.
178
176
  ```vue
179
177
  <template>
180
178
  <div
181
- class="max-w-sm space-y-2 rounded-[var(--bb-radius)] border border-[color:var(--bb-border)] p-3"
179
+ class="max-w-sm space-y-2 rounded-(--bb-radius) border border-(--bb-border) p-3"
182
180
  >
183
181
  <div
184
- class="rounded-[var(--bb-radius)] bg-[color:var(--bb-muted)] px-3 py-8 text-center text-sm text-[color:var(--bb-text-muted)]"
182
+ class="rounded-(--bb-radius) bg-(--bb-muted) px-3 py-8 text-center text-sm text-(--bb-text-muted)"
185
183
  >
186
184
  Sheet {{ sheet }} of 18 · Q3 board pack
187
185
  </div>
@@ -248,7 +246,7 @@ your own. Sit the pager in the table footer:
248
246
  `dependencies` wiring is needed — the provider just reads `page` from the
249
247
  same context the pager drives. -->
250
248
  <div
251
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
249
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
252
250
  >
253
251
  <BbTable
254
252
  id="audit-log"
@@ -259,9 +257,9 @@ your own. Sit the pager in the table footer:
259
257
  :items="fetchEvents"
260
258
  />
261
259
  <div
262
- class="flex flex-wrap items-center justify-between gap-2 border-t border-[color:var(--bb-border)] px-3 py-2"
260
+ class="flex flex-wrap items-center justify-between gap-2 border-t border-(--bb-border) px-3 py-2"
263
261
  >
264
- <p class="text-xs text-[color:var(--bb-text-muted)]">
262
+ <p class="text-xs text-(--bb-text-muted)">
265
263
  {{ rangeStart }}–{{ rangeEnd }} of {{ totalItems }}
266
264
  </p>
267
265
  <BbPagination label="Audit log pagination" table-id="audit-log" />
@@ -357,24 +355,24 @@ them in sync from then on:
357
355
  instead of walking every page click. Seed v-model from the route once —
358
356
  the control writes the URL, it does not read it at mount. -->
359
357
  <div
360
- class="max-w-md overflow-hidden rounded-[var(--bb-radius)] border border-[color:var(--bb-border)]"
358
+ class="max-w-md overflow-hidden rounded-(--bb-radius) border border-(--bb-border)"
361
359
  >
362
- <ul class="divide-y divide-[color:var(--bb-border)]">
360
+ <ul class="divide-y divide-(--bb-border)">
363
361
  <li
364
362
  v-for="row in pageItems"
365
363
  :key="row.id"
366
364
  class="flex items-center justify-between gap-2 px-3 py-2 text-sm"
367
365
  >
368
366
  <span class="font-medium">{{ row.label }}</span>
369
- <span class="text-xs tabular-nums text-[color:var(--bb-text-muted)]">{{
367
+ <span class="text-xs text-(--bb-text-muted) tabular-nums">{{
370
368
  row.when
371
369
  }}</span>
372
370
  </li>
373
371
  </ul>
374
372
  <div
375
- class="flex flex-wrap items-center justify-between gap-2 border-t border-[color:var(--bb-border)] px-3 py-2"
373
+ class="flex flex-wrap items-center justify-between gap-2 border-t border-(--bb-border) px-3 py-2"
376
374
  >
377
- <p class="text-xs text-[color:var(--bb-text-muted)]">
375
+ <p class="text-xs text-(--bb-text-muted)">
378
376
  Page {{ page }} of {{ totalPages }}
379
377
  </p>
380
378
  <BbPagination
@@ -53,11 +53,11 @@ dismiss the panel.
53
53
  ```vue
54
54
  <template>
55
55
  <div
56
- class="flex max-w-sm items-center justify-between gap-3 rounded-[var(--bb-radius)] border p-3"
56
+ class="flex max-w-sm items-center justify-between gap-3 rounded-(--bb-radius) border p-3"
57
57
  >
58
58
  <div class="min-w-0">
59
59
  <p class="truncate text-sm font-medium">Invoices</p>
60
- <p class="truncate text-xs text-[color:var(--bb-text-muted)]">
60
+ <p class="truncate text-xs text-(--bb-text-muted)">
61
61
  24 open · last synced 2m ago
62
62
  </p>
63
63
  </div>
@@ -102,11 +102,11 @@ a record and closes itself on save via the `close` callback.
102
102
  ```vue
103
103
  <template>
104
104
  <div
105
- class="flex max-w-sm items-center justify-between gap-2 rounded-[var(--bb-radius)] border p-3"
105
+ class="flex max-w-sm items-center justify-between gap-2 rounded-(--bb-radius) border p-3"
106
106
  >
107
107
  <div class="min-w-0">
108
108
  <p class="truncate text-sm font-medium">{{ name }}</p>
109
- <p class="truncate text-xs text-[color:var(--bb-text-muted)]">
109
+ <p class="truncate text-xs text-(--bb-text-muted)">
110
110
  Shared report · edited today
111
111
  </p>
112
112
  </div>
@@ -173,10 +173,10 @@ the name, and the panel is pure content: any navigation ("View profile") is an
173
173
  ```vue
174
174
  <template>
175
175
  <div
176
- class="max-w-md rounded-[var(--bb-radius)] border p-3 text-sm text-[color:var(--bb-text-muted)]"
176
+ class="max-w-md rounded-(--bb-radius) border p-3 text-sm text-(--bb-text-muted)"
177
177
  >
178
178
  Deploy of
179
- <span class="font-medium text-[var(--bb-text)]">invoice-export</span>
179
+ <span class="font-medium text-(--bb-text)">invoice-export</span>
180
180
  approved by
181
181
  <BbPopover placement="bottom-start" :width="280">
182
182
  <template #activator="{ props }">
@@ -198,15 +198,15 @@ the name, and the panel is pure content: any navigation ("View profile") is an
198
198
  <span>{{ member.initials }}</span>
199
199
  </BbAvatar>
200
200
  <div class="min-w-0">
201
- <div class="truncate text-sm font-medium text-[var(--bb-text)]">
201
+ <div class="truncate text-sm font-medium text-(--bb-text)">
202
202
  {{ member.name }}
203
203
  </div>
204
- <div class="truncate text-xs text-[color:var(--bb-text-muted)]">
204
+ <div class="truncate text-xs text-(--bb-text-muted)">
205
205
  {{ member.role }}
206
206
  </div>
207
207
  </div>
208
208
  </div>
209
- <div class="grid gap-1 text-xs text-[color:var(--bb-text-muted)]">
209
+ <div class="grid gap-1 text-xs text-(--bb-text-muted)">
210
210
  <div class="flex items-center gap-1.5">
211
211
  <BbIcon icon="lucide:mail" size="xs" />
212
212
  <span class="truncate">{{ member.email }}</span>
@@ -268,10 +268,10 @@ dismissible.
268
268
 
269
269
  ```vue
270
270
  <template>
271
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-3">
271
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-3">
272
272
  <div class="flex flex-col gap-0.5">
273
273
  <p class="text-sm font-medium">Invoice retention</p>
274
- <p class="text-xs text-[color:var(--bb-text-muted)]">
274
+ <p class="text-xs text-(--bb-text-muted)">
275
275
  How long paid invoices stay searchable in the workspace.
276
276
  </p>
277
277
  </div>
@@ -296,7 +296,7 @@ dismissible.
296
296
  </template>
297
297
  <div class="grid gap-2 text-sm">
298
298
  <p class="m-0 font-medium">Retention period</p>
299
- <p class="m-0 text-xs text-[color:var(--bb-text-muted)]">
299
+ <p class="m-0 text-xs text-(--bb-text-muted)">
300
300
  Paid invoices older than this window are archived and drop out of
301
301
  search. Drafts and unpaid invoices are never archived by retention.
302
302
  </p>
@@ -375,13 +375,11 @@ utility (BbButton centers labels by default).
375
375
  ```vue
376
376
  <template>
377
377
  <div
378
- class="flex max-w-sm items-center justify-between gap-3 rounded-[var(--bb-radius)] border p-3"
378
+ class="flex max-w-sm items-center justify-between gap-3 rounded-(--bb-radius) border p-3"
379
379
  >
380
380
  <div class="min-w-0">
381
381
  <p class="truncate text-sm font-medium">Acme Studio</p>
382
- <p class="truncate text-xs text-[color:var(--bb-text-muted)]">
383
- Workspace · Billing
384
- </p>
382
+ <p class="truncate text-xs text-(--bb-text-muted)">Workspace · Billing</p>
385
383
  </div>
386
384
  <BbPopover scrollable :width="300">
387
385
  <template #activator="{ props }">
@@ -466,7 +464,7 @@ actions, reach for `BbDropdown` instead.)
466
464
  <button
467
465
  v-bind="props"
468
466
  type="button"
469
- class="grid h-24 w-full place-items-center rounded-[var(--bb-radius)] border border-dashed text-sm text-[color:var(--bb-text-muted)]"
467
+ class="grid h-24 w-full place-items-center rounded-(--bb-radius) border border-dashed text-sm text-(--bb-text-muted)"
470
468
  >
471
469
  Right-click anywhere here
472
470
  </button>
@@ -28,7 +28,7 @@ one-way `:model-value` binding and keep the source of truth in your state.
28
28
  <div class="max-w-sm flex flex-col gap-2">
29
29
  <div class="flex justify-between text-sm">
30
30
  <span>Profile completeness</span>
31
- <span class="text-[color:var(--bb-text-muted)]">60%</span>
31
+ <span class="text-(--bb-text-muted)">60%</span>
32
32
  </div>
33
33
  <BbProgress :model-value="60" />
34
34
  </div>
@@ -46,17 +46,15 @@ hook (a theme token — never a hardcoded hex).
46
46
 
47
47
  ```vue
48
48
  <template>
49
- <div
50
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
51
- >
49
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
52
50
  <div class="flex items-start justify-between gap-3">
53
51
  <div class="flex flex-col gap-0.5">
54
52
  <span class="text-sm font-medium">Storage</span>
55
- <span class="text-xs text-[color:var(--bb-text-muted)]">
53
+ <span class="text-xs text-(--bb-text-muted)">
56
54
  Northwind Billing · Team plan
57
55
  </span>
58
56
  </div>
59
- <span class="text-xs tabular-nums text-[color:var(--bb-text-muted)]">
57
+ <span class="text-xs text-(--bb-text-muted) tabular-nums">
60
58
  {{ usedGb }} / {{ maxGb }} GB
61
59
  </span>
62
60
  </div>
@@ -71,7 +69,7 @@ hook (a theme token — never a hardcoded hex).
71
69
  />
72
70
 
73
71
  <div class="flex items-center justify-between gap-3">
74
- <span class="text-xs text-[color:var(--bb-text-muted)]">
72
+ <span class="text-xs text-(--bb-text-muted)">
75
73
  {{ remainingGb }} GB free
76
74
  <template v-if="nearLimit"> · nearly full</template>
77
75
  </span>
@@ -105,7 +103,7 @@ transition.
105
103
  <div class="max-w-sm flex flex-col gap-2">
106
104
  <div class="flex justify-between text-sm">
107
105
  <span>Q3 sales target</span>
108
- <span class="text-[color:var(--bb-text-muted)]">€310k of the €250–370k band</span>
106
+ <span class="text-(--bb-text-muted)">€310k of the €250–370k band</span>
109
107
  </div>
110
108
  <BbProgress :min="250" :max="370" :model-value="310" />
111
109
  </div>
@@ -120,17 +118,13 @@ percentage label is derived from the same source of truth.
120
118
 
121
119
  ```vue
122
120
  <template>
123
- <div
124
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
125
- >
121
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
126
122
  <div class="flex items-center justify-between gap-3">
127
123
  <span class="flex min-w-0 items-center gap-2 text-sm font-medium">
128
124
  <BbIcon icon="lucide:file-up" size="sm" />
129
125
  <span class="truncate">quarterly-report.pdf</span>
130
126
  </span>
131
- <span
132
- class="shrink-0 text-xs tabular-nums text-[color:var(--bb-text-muted)]"
133
- >
127
+ <span class="shrink-0 text-xs text-(--bb-text-muted) tabular-nums">
134
128
  {{ uploadedMb }} / {{ totalMb }} MB
135
129
  </span>
136
130
  </div>
@@ -143,7 +137,7 @@ percentage label is derived from the same source of truth.
143
137
  />
144
138
 
145
139
  <div class="flex items-center justify-between gap-3">
146
- <span class="text-xs tabular-nums text-[color:var(--bb-text-muted)]">
140
+ <span class="text-xs text-(--bb-text-muted) tabular-nums">
147
141
  {{ percent }}% uploaded
148
142
  </span>
149
143
  <BbButton
@@ -207,17 +201,13 @@ emits nothing.
207
201
 
208
202
  ```vue
209
203
  <template>
210
- <div
211
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
212
- >
204
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
213
205
  <div class="flex items-center justify-between gap-3">
214
206
  <span class="flex min-w-0 items-center gap-2 text-sm font-medium">
215
207
  <BbIcon icon="lucide:file-spreadsheet" size="sm" />
216
208
  <span class="truncate">contacts.csv</span>
217
209
  </span>
218
- <span
219
- class="shrink-0 text-xs tabular-nums text-[color:var(--bb-text-muted)]"
220
- >
210
+ <span class="shrink-0 text-xs text-(--bb-text-muted) tabular-nums">
221
211
  {{ statusLabel }}
222
212
  </span>
223
213
  </div>
@@ -232,7 +222,7 @@ emits nothing.
232
222
  <!-- Unmeasurable phase: swap to a spinner — never fake a creeping bar. -->
233
223
  <div
234
224
  v-else-if="phase === 'processing'"
235
- class="flex items-center gap-2 text-sm text-[color:var(--bb-text-muted)]"
225
+ class="flex items-center gap-2 text-sm text-(--bb-text-muted)"
236
226
  role="status"
237
227
  >
238
228
  <BbSpinner size="sm" />
@@ -243,7 +233,17 @@ emits nothing.
243
233
  1,284 contacts imported
244
234
  </div>
245
235
 
246
- <div class="flex justify-end">
236
+ <div class="flex justify-end gap-2">
237
+ <!-- Demo control: one click = one chunk sent, then the server's
238
+ "done". In an app the transfer and the job's completion drive
239
+ these; nothing here ticks on its own. -->
240
+ <BbButton
241
+ v-if="phase === 'uploading' || phase === 'processing'"
242
+ prepend:icon="lucide:step-forward"
243
+ :text="phase === 'uploading' ? 'Send next chunk' : 'Processing done'"
244
+ variant="ghost"
245
+ @click="onAdvance"
246
+ />
247
247
  <BbButton
248
248
  :disabled="phase === 'uploading' || phase === 'processing'"
249
249
  prepend:icon="lucide:upload"
@@ -256,25 +256,19 @@ emits nothing.
256
256
  </template>
257
257
 
258
258
  <script setup lang="ts">
259
- import { computed, onUnmounted, ref } from 'vue';
259
+ import { computed, ref } from 'vue';
260
260
  import { BbButton, BbIcon, BbProgress, BbSpinner } from 'bitboss-ui';
261
261
 
262
262
  type Phase = 'idle' | 'uploading' | 'processing' | 'done';
263
263
 
264
- // Simulated transfer: the upload is measurable (bytes sent), the server-side
265
- // row parsing that follows is not — that boundary is where the bar hands off
266
- // to BbSpinner.
264
+ // The upload is measurable (bytes sent), the server-side row parsing that
265
+ // follows is not — that boundary is where the bar hands off to BbSpinner.
267
266
  const totalKb = 640;
268
- const chunkKb = 80;
267
+ const chunkKb = 160;
269
268
 
270
269
  const phase = ref<Phase>('idle');
271
270
  const sentKb = ref(0);
272
271
 
273
- let timer: ReturnType<typeof setInterval> | undefined;
274
- onUnmounted(() => {
275
- if (timer !== undefined) clearInterval(timer);
276
- });
277
-
278
272
  const statusLabel = computed(() => {
279
273
  if (phase.value === 'uploading') return `${sentKb.value} / ${totalKb} KB`;
280
274
  if (phase.value === 'processing') return 'Upload complete';
@@ -282,19 +276,18 @@ const statusLabel = computed(() => {
282
276
  return `${totalKb} KB`;
283
277
  });
284
278
 
285
- /** Run the upload → processing → done sequence with deterministic timers. */
286
279
  const onStartImport = () => {
287
280
  phase.value = 'uploading';
288
281
  sentKb.value = 0;
289
- timer = setInterval(() => {
282
+ };
283
+
284
+ const onAdvance = () => {
285
+ if (phase.value === 'uploading') {
290
286
  sentKb.value = Math.min(totalKb, sentKb.value + chunkKb);
291
- if (sentKb.value < totalKb) return;
292
- if (timer !== undefined) clearInterval(timer);
293
- phase.value = 'processing';
294
- setTimeout(() => {
295
- phase.value = 'done';
296
- }, 500);
297
- }, 70);
287
+ if (sentKb.value === totalKb) phase.value = 'processing';
288
+ return;
289
+ }
290
+ if (phase.value === 'processing') phase.value = 'done';
298
291
  };
299
292
  </script>
300
293
  ```
@@ -309,17 +302,13 @@ fill transition animates each advance for free.
309
302
 
310
303
  ```vue
311
304
  <template>
312
- <div
313
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
314
- >
305
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
315
306
  <div class="flex items-center justify-between gap-3">
316
307
  <div class="flex flex-col gap-0.5">
317
308
  <span class="text-sm font-medium">Workspace setup</span>
318
- <span class="text-xs text-[color:var(--bb-text-muted)]">{{
319
- steps[step]
320
- }}</span>
309
+ <span class="text-xs text-(--bb-text-muted)">{{ steps[step] }}</span>
321
310
  </div>
322
- <span class="text-xs tabular-nums text-[color:var(--bb-text-muted)]">
311
+ <span class="text-xs text-(--bb-text-muted) tabular-nums">
323
312
  Step {{ step + 1 }} of {{ steps.length }}
324
313
  </span>
325
314
  </div>
@@ -336,7 +325,7 @@ fill transition animates each advance for free.
336
325
  v-for="(label, index) in steps"
337
326
  :key="label"
338
327
  class="flex items-center gap-2 text-sm"
339
- :class="index <= step ? '' : 'text-[color:var(--bb-text-muted)]'"
328
+ :class="index <= step ? '' : 'text-(--bb-text-muted)'"
340
329
  >
341
330
  <BbIcon
342
331
  :icon="index < step ? 'lucide:circle-check' : 'lucide:circle'"
@@ -459,7 +448,7 @@ What's still on you:
459
448
  <div class="max-w-sm flex flex-col gap-2">
460
449
  <div class="flex justify-between text-sm">
461
450
  <span>Storage used</span>
462
- <span class="text-[color:var(--bb-text-muted)]">220 / 500 GB</span>
451
+ <span class="text-(--bb-text-muted)">220 / 500 GB</span>
463
452
  </div>
464
453
  <BbProgress
465
454
  aria-label="Storage used"
@@ -50,18 +50,18 @@ name (see Accessibility).
50
50
  >
51
51
  <div class="flex flex-col gap-0.5">
52
52
  <p class="text-sm font-medium">Workspace plan</p>
53
- <p class="text-xs text-[color:var(--bb-text-muted)]">
53
+ <p class="text-xs text-(--bb-text-muted)">
54
54
  Billed monthly · cancel anytime
55
55
  </p>
56
56
  </div>
57
57
  <div
58
58
  v-for="plan in plans"
59
59
  :key="plan.value"
60
- class="flex items-start justify-between gap-3 rounded-[var(--bb-radius)] border p-2.5"
60
+ class="flex items-start justify-between gap-3 rounded-(--bb-radius) border p-2.5"
61
61
  :class="
62
62
  selected === plan.value
63
- ? 'border-[color:var(--bb-primary)]'
64
- : 'border-[color:var(--bb-border)]'
63
+ ? 'border-(--bb-primary)'
64
+ : 'border-(--bb-border)'
65
65
  "
66
66
  >
67
67
  <BbRadio
@@ -75,7 +75,7 @@ name (see Accessibility).
75
75
  {{ plan.price }}
76
76
  </span>
77
77
  </div>
78
- <p class="plan-summary text-xs text-[color:var(--bb-text-muted)]">
78
+ <p class="plan-summary text-xs text-(--bb-text-muted)">
79
79
  Selected plan: {{ selected }}
80
80
  </p>
81
81
  </div>
@@ -147,7 +147,7 @@ amber and red at once.
147
147
  **Readonly, error and disabled**
148
148
 
149
149
  ```vue
150
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-3">
150
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-3">
151
151
  <BbRadio
152
152
  :model-value="'locked'"
153
153
  value="locked"
@@ -207,12 +207,12 @@ individual `BbRadio` buttons.
207
207
  <template>
208
208
  <div
209
209
  aria-label="Merge method"
210
- class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-3"
210
+ class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-3"
211
211
  role="radiogroup"
212
212
  >
213
213
  <div class="flex flex-col gap-0.5">
214
214
  <p class="text-sm font-medium">Merge pull request #482</p>
215
- <p class="text-xs text-[color:var(--bb-text-muted)]">
215
+ <p class="text-xs text-(--bb-text-muted)">
216
216
  feature/invoice-export → main
217
217
  </p>
218
218
  </div>
@@ -244,7 +244,7 @@ individual `BbRadio` buttons.
244
244
  name="merge-method"
245
245
  value="rebase"
246
246
  />
247
- <p class="merge-summary text-xs text-[color:var(--bb-text-muted)]">
247
+ <p class="merge-summary text-xs text-(--bb-text-muted)">
248
248
  Pull request #482 will be merged via {{ labels[strategy] }}.
249
249
  </p>
250
250
  </div>
@@ -40,9 +40,7 @@ wins) — see the
40
40
 
41
41
  ```vue
42
42
  <template>
43
- <div
44
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-4"
45
- >
43
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-4">
46
44
  <BbRadioGroup
47
45
  v-model="plan"
48
46
  compact
@@ -53,7 +51,7 @@ wins) — see the
53
51
  legend="Subscription plan"
54
52
  name="plan"
55
53
  />
56
- <p class="plan-summary text-xs text-[var(--bb-text-muted)]">
54
+ <p class="plan-summary text-xs text-(--bb-text-muted)">
57
55
  Selected: {{ plan }}
58
56
  </p>
59
57
  </div>
@@ -109,7 +107,7 @@ from `v-model` inline.
109
107
 
110
108
  ```vue
111
109
  <template>
112
- <div class="max-w-sm rounded-[var(--bb-radius)] border p-4">
110
+ <div class="max-w-sm rounded-(--bb-radius) border p-4">
113
111
  <BbRadioGroup
114
112
  v-model="speed"
115
113
  compact
@@ -159,7 +157,7 @@ it participates in native form validation. `legend-mode` places the legend
159
157
 
160
158
  ```vue
161
159
  <template>
162
- <div class="max-w-sm rounded-[var(--bb-radius)] border p-4">
160
+ <div class="max-w-sm rounded-(--bb-radius) border p-4">
163
161
  <BbRadioGroup
164
162
  v-model="cycle"
165
163
  compact
@@ -234,9 +232,7 @@ valid yet" and gate the submit on it.
234
232
 
235
233
  ```vue
236
234
  <template>
237
- <div
238
- class="flex max-w-md flex-col gap-3 rounded-[var(--bb-radius)] border p-4"
239
- >
235
+ <div class="flex max-w-md flex-col gap-3 rounded-(--bb-radius) border p-4">
240
236
  <BbRadioGroup
241
237
  v-model="branch"
242
238
  compact
@@ -262,7 +258,7 @@ valid yet" and gate the submit on it.
262
258
  name="merge-strategy"
263
259
  persistent-hint
264
260
  />
265
- <p class="strategy-summary text-xs text-[var(--bb-text-muted)]">
261
+ <p class="strategy-summary text-xs text-(--bb-text-muted)">
266
262
  Strategy: {{ strategy ?? 'none selected' }}
267
263
  </p>
268
264
  </div>
@@ -334,7 +330,7 @@ modifier class when checked — style off that, not a `:has()` selector.
334
330
 
335
331
  ```vue
336
332
  <template>
337
- <div class="max-w-md rounded-[var(--bb-radius)] border p-4">
333
+ <div class="max-w-md rounded-(--bb-radius) border p-4">
338
334
  <BbRadioGroup
339
335
  v-model="theme"
340
336
  class="theme-tiles"
@@ -78,7 +78,7 @@ sample size next to the stars.
78
78
  ```vue
79
79
  <BbRating :model-value="4" readonly legend="Average rating" name="avg">
80
80
  <template #append>
81
- <span class="text-sm text-[color:var(--bb-text-muted)]">4.0 · 128 reviews</span>
81
+ <span class="text-sm text-(--bb-text-muted)">4.0 · 128 reviews</span>
82
82
  </template>
83
83
  </BbRating>
84
84
  ```
@@ -138,12 +138,10 @@ from the `size` prop through the shared scale — pass it straight to `BbIcon`'s
138
138
 
139
139
  ```vue
140
140
  <template>
141
- <div
142
- class="flex max-w-sm flex-col gap-2 rounded-[var(--bb-radius)] border p-3"
143
- >
141
+ <div class="flex max-w-sm flex-col gap-2 rounded-(--bb-radius) border p-3">
144
142
  <div class="flex flex-col gap-0.5">
145
143
  <span class="text-sm font-medium">Pulse · App Store</span>
146
- <span class="text-xs text-[color:var(--bb-text-muted)]"
144
+ <span class="text-xs text-(--bb-text-muted)"
147
145
  >How much do you love it?</span
148
146
  >
149
147
  </div>
@@ -155,9 +153,7 @@ from the `size` prop through the shared scale — pass it straight to `BbIcon`'s
155
153
  >
156
154
  <template #icon="{ checked, size }">
157
155
  <BbIcon
158
- :class="
159
- checked ? 'text-rose-500' : 'text-[color:var(--bb-text-faint)]'
160
- "
156
+ :class="checked ? 'text-rose-500' : 'text-(--bb-text-faint)'"
161
157
  icon="lucide:heart"
162
158
  :size="size"
163
159
  />
@@ -201,13 +197,13 @@ as the user picks a value.
201
197
  ```vue
202
198
  <template>
203
199
  <form
204
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
200
+ class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3"
205
201
  novalidate
206
202
  @submit.prevent
207
203
  >
208
204
  <div class="flex flex-col gap-0.5">
209
205
  <span class="text-sm font-medium">How was your order?</span>
210
- <span class="text-xs text-[color:var(--bb-text-muted)]">
206
+ <span class="text-xs text-(--bb-text-muted)">
211
207
  Order #10482 · delivered Jul 8, 2026
212
208
  </span>
213
209
  </div>
@@ -221,7 +217,7 @@ as the user picks a value.
221
217
  required
222
218
  @update:model-value="onScoreChange"
223
219
  />
224
- <p class="text-sm text-[color:var(--bb-text-muted)]" data-testid="score">
220
+ <p class="text-sm text-(--bb-text-muted)" data-testid="score">
225
221
  {{ score === null ? 'Not rated yet' : `${score}/5 — ${labels[score]}` }}
226
222
  </p>
227
223
  <BbTextarea
@@ -238,7 +234,7 @@ as the user picks a value.
238
234
  <BbButton variant="primary" @click="submit">Submit review</BbButton>
239
235
  <span
240
236
  v-if="sent"
241
- class="text-xs text-[color:var(--bb-text-muted)]"
237
+ class="text-xs text-(--bb-text-muted)"
242
238
  data-testid="sent"
243
239
  >
244
240
  Thanks — review received.