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
@@ -21,7 +21,7 @@ spinner, or vice-versa — `BbProgress` has no indeterminate mode (`null` render
21
21
  an empty bar), so this component **is** the library's indeterminate state.
22
22
 
23
23
  Two props, no slots, no events: `size` (icon scale) and optional `label` (an
24
- sr-only status announcement). Show the spinner with `v-if` while loading, remove
24
+ visually-hidden status announcement). Show the spinner with `v-if` while loading, remove
25
25
  it when done.
26
26
 
27
27
  **An indeterminate wait**
@@ -47,14 +47,27 @@ earlier:
47
47
  <span class="import-card__title">Import contacts</span>
48
48
  <span class="import-card__meta">contacts.csv · 1,284 rows</span>
49
49
  </div>
50
- <BbButton
51
- :disabled="phase === 'validating' || phase === 'uploading'"
52
- prepend:icon="lucide:upload"
53
- variant="outline"
54
- @click="onImport"
55
- >
56
- Import
57
- </BbButton>
50
+ <div class="import-card__actions">
51
+ <BbButton
52
+ :disabled="phase !== 'idle' && phase !== 'done'"
53
+ prepend:icon="lucide:upload"
54
+ variant="outline"
55
+ @click="onImport"
56
+ >
57
+ Import
58
+ </BbButton>
59
+ <!-- Demo control: advances the job one step per click. In an app
60
+ the parser and the upload drive these transitions; nothing
61
+ here ticks on its own. -->
62
+ <BbButton
63
+ v-if="phase === 'validating' || phase === 'uploading'"
64
+ prepend:icon="lucide:step-forward"
65
+ variant="ghost"
66
+ @click="onAdvance"
67
+ >
68
+ {{ phase === 'validating' ? 'Validation done' : 'Next chunk' }}
69
+ </BbButton>
70
+ </div>
58
71
  </div>
59
72
 
60
73
  <div
@@ -89,20 +102,19 @@ const phase = ref<Phase>('idle');
89
102
  const progress = ref(0);
90
103
 
91
104
  const onImport = () => {
105
+ // Parsing + validating the file: duration unknown → spinner.
92
106
  phase.value = 'validating';
93
107
  progress.value = 0;
94
- // Stand-in for parsing + validating the file (duration unknown → spinner).
95
- setTimeout(() => {
108
+ };
109
+
110
+ const onAdvance = () => {
111
+ if (phase.value === 'validating') {
112
+ // Rows counted from here on → the bar takes over.
96
113
  phase.value = 'uploading';
97
- // Stand-in for a chunked upload (rows counted → progress bar).
98
- const timer = setInterval(() => {
99
- progress.value = Math.min(progress.value + 20, 100);
100
- if (progress.value === 100) {
101
- clearInterval(timer);
102
- phase.value = 'done';
103
- }
104
- }, 90);
105
- }, 350);
114
+ return;
115
+ }
116
+ progress.value = Math.min(progress.value + 25, 100);
117
+ if (progress.value === 100) phase.value = 'done';
106
118
  };
107
119
  </script>
108
120
 
@@ -125,6 +137,13 @@ const onImport = () => {
125
137
  justify-content: space-between;
126
138
  }
127
139
 
140
+ .import-card__actions {
141
+ align-items: center;
142
+ display: flex;
143
+ flex-shrink: 0;
144
+ gap: 6px;
145
+ }
146
+
128
147
  .import-card__titles {
129
148
  display: flex;
130
149
  flex-direction: column;
@@ -185,13 +204,13 @@ internals.
185
204
 
186
205
  ```vue
187
206
  <div class="flex items-center gap-4">
188
- <span class="inline-flex items-center gap-2 text-[color:var(--bb-text-muted)]">
207
+ <span class="inline-flex items-center gap-2 text-(--bb-text-muted)">
189
208
  <BbSpinner size="sm" /> Muted
190
209
  </span>
191
- <span class="inline-flex items-center gap-2 text-[color:var(--bb-primary)]">
210
+ <span class="inline-flex items-center gap-2 text-(--bb-primary)">
192
211
  <BbSpinner size="sm" /> Brand
193
212
  </span>
194
- <span class="inline-flex items-center gap-2 text-[color:var(--bb-danger)]">
213
+ <span class="inline-flex items-center gap-2 text-(--bb-danger)">
195
214
  <BbSpinner size="sm" /> Retrying…
196
215
  </span>
197
216
  </div>
@@ -225,15 +244,28 @@ panel body (and let the trigger button use its own `loading` prop):
225
244
  <div class="results-panel">
226
245
  <div class="results-panel__head">
227
246
  <span class="results-panel__title">Invoices</span>
228
- <BbButton
229
- :loading="loading"
230
- prepend:icon="lucide:refresh-cw"
231
- size="sm"
232
- variant="outline"
233
- @click="onRefresh"
234
- >
235
- Refresh
236
- </BbButton>
247
+ <div class="results-panel__actions">
248
+ <BbButton
249
+ :loading="loading"
250
+ prepend:icon="lucide:refresh-cw"
251
+ size="sm"
252
+ variant="outline"
253
+ @click="onRefresh"
254
+ >
255
+ Refresh
256
+ </BbButton>
257
+ <!-- Demo control: plays the response arriving. In an app the
258
+ fetch's own completion does this; nothing here ticks on its own. -->
259
+ <BbButton
260
+ v-if="loading"
261
+ prepend:icon="lucide:check"
262
+ size="sm"
263
+ variant="ghost"
264
+ @click="onResponse"
265
+ >
266
+ Deliver results
267
+ </BbButton>
268
+ </div>
237
269
  </div>
238
270
 
239
271
  <div v-if="loading" class="results-panel__status" role="status">
@@ -276,14 +308,15 @@ const loading = ref(false);
276
308
 
277
309
  const onRefresh = () => {
278
310
  loading.value = true;
279
- setTimeout(() => {
280
- invoices.value = [
281
- { id: 'INV-1045', customer: 'Umbrella', amount: '€5,120.00' },
282
- { id: 'INV-1044', customer: 'Hooli', amount: '€2,340.00' },
283
- { id: 'INV-1043', customer: 'Stark Industries', amount: '€8,900.00' },
284
- ];
285
- loading.value = false;
286
- }, 800);
311
+ };
312
+
313
+ const onResponse = () => {
314
+ invoices.value = [
315
+ { id: 'INV-1045', customer: 'Umbrella', amount: '€5,120.00' },
316
+ { id: 'INV-1044', customer: 'Hooli', amount: '€2,340.00' },
317
+ { id: 'INV-1043', customer: 'Stark Industries', amount: '€8,900.00' },
318
+ ];
319
+ loading.value = false;
287
320
  };
288
321
  </script>
289
322
 
@@ -306,6 +339,12 @@ const onRefresh = () => {
306
339
  justify-content: space-between;
307
340
  }
308
341
 
342
+ .results-panel__actions {
343
+ align-items: center;
344
+ display: flex;
345
+ gap: 6px;
346
+ }
347
+
309
348
  .results-panel__title {
310
349
  font-size: 14px;
311
350
  font-weight: 500;
@@ -385,14 +424,21 @@ on the container makes the state audible to assistive tech, per the
385
424
 
386
425
  <div class="pending-list__trail">
387
426
  <span class="pending-list__role">{{ member.role }}</span>
388
- <span
389
- v-if="pendingId === member.email"
390
- class="pending-list__status"
391
- role="status"
392
- >
393
- <BbSpinner size="xs" />
394
- Saving…
395
- </span>
427
+ <template v-if="pendingId === member.email">
428
+ <span class="pending-list__status" role="status">
429
+ <BbSpinner size="xs" />
430
+ Saving…
431
+ </span>
432
+ <!-- Demo control: plays the save response. In an app the
433
+ request's completion clears `pendingId`; nothing ticks. -->
434
+ <BbButton
435
+ aria-label="Mark as saved"
436
+ icon="lucide:check"
437
+ size="sm"
438
+ variant="ghost"
439
+ @click="onSaved"
440
+ />
441
+ </template>
396
442
  <BbButton
397
443
  v-else
398
444
  size="sm"
@@ -442,9 +488,10 @@ const pendingId = ref<string | null>(null);
442
488
 
443
489
  const onSave = (email: string) => {
444
490
  pendingId.value = email;
445
- setTimeout(() => {
446
- pendingId.value = null;
447
- }, 700);
491
+ };
492
+
493
+ const onSaved = () => {
494
+ pendingId.value = null;
448
495
  };
449
496
  </script>
450
497
 
@@ -535,14 +582,15 @@ const onSave = (email: string) => {
535
582
  <span class="section-shell__title">Revenue overview</span>
536
583
  <span class="section-shell__meta">Last 30 days · Northwind</span>
537
584
  </div>
585
+ <!-- Demo control: stands in for the request settling / being re-run.
586
+ In an app the fetch flips `loading`; nothing here ticks on its own. -->
538
587
  <BbButton
539
- :disabled="loading"
540
- prepend:icon="lucide:refresh-cw"
588
+ :prepend:icon="loading ? 'lucide:check' : 'lucide:refresh-cw'"
541
589
  size="sm"
542
590
  variant="outline"
543
- @click="onReload"
591
+ @click="loading ? deliver() : reload()"
544
592
  >
545
- Reload
593
+ {{ loading ? 'Deliver data' : 'Reload' }}
546
594
  </BbButton>
547
595
  </div>
548
596
 
@@ -560,34 +608,30 @@ const onSave = (email: string) => {
560
608
  </template>
561
609
 
562
610
  <script setup lang="ts">
563
- import { onMounted, ref } from 'vue';
611
+ import { ref } from 'vue';
564
612
  import { BbButton, BbSpinner } from 'bitboss-ui';
565
613
 
566
614
  type Stat = { label: string; value: string };
567
615
 
568
616
  // First paint of an empty section: center a spinner with a line naming what is
569
- // coming. A bare spinner in a blank region tells users nothing.
617
+ // coming. A bare spinner in a blank region tells users nothing. The example
618
+ // mounts IN that state — no timer resolves it; the button plays the response.
570
619
  const stats = ref<Stat[]>([]);
571
620
  const loading = ref(true);
572
621
 
573
- const load = () => {
574
- loading.value = true;
575
- stats.value = [];
576
- setTimeout(() => {
577
- stats.value = [
578
- { label: 'Collected', value: '€48,210' },
579
- { label: 'Outstanding', value: '€12,940' },
580
- { label: 'Overdue', value: '€3,180' },
581
- ];
582
- loading.value = false;
583
- }, 800);
622
+ const deliver = () => {
623
+ stats.value = [
624
+ { label: 'Collected', value: '€48,210' },
625
+ { label: 'Outstanding', value: '€12,940' },
626
+ { label: 'Overdue', value: '€3,180' },
627
+ ];
628
+ loading.value = false;
584
629
  };
585
630
 
586
- const onReload = () => {
587
- load();
631
+ const reload = () => {
632
+ stats.value = [];
633
+ loading.value = true;
588
634
  };
589
-
590
- onMounted(load);
591
635
  </script>
592
636
 
593
637
  <style scoped>
@@ -717,14 +761,14 @@ By default the spinner is decorative — no live region and no ARIA role. Pair i
717
761
  with adjacent visible text (`Loading results…`) and put `role="status"` /
718
762
  `aria-live="polite"` on the text container you control. When the spinner stands
719
763
  alone (icon-only pending affordance), set `label` so the component wraps itself
720
- in `role="status"` with that text rendered `sr-only`. Prefer `BbButton`'s
764
+ in `role="status"` with that text rendered visually hidden (`bb-sr-only`). Prefer `BbButton`'s
721
765
  `loading` for action feedback since it wires up ARIA for you.
722
766
 
723
767
  ## Props
724
768
 
725
769
  | Prop | Type | Default | Required | Description |
726
770
  | --- | --- | --- | --- | --- |
727
- | `label` | `string \| undefined` | | | Accessible loading announcement. When set, the spinner is wrapped in a `role="status"` element containing this text rendered `sr-only`, so screen readers announce the loading state. When omitted the markup is unchanged and the spinner is pu… |
771
+ | `label` | `string \| undefined` | | | Accessible loading announcement. When set, the spinner is wrapped in a `role="status"` element containing this text rendered `bb-sr-only`, so screen readers announce the loading state. When omitted the markup is unchanged and the spinner is… |
728
772
  | `size` | `number \| ({ size?: any; } & string) \| keyof Sizes \| undefined` | `"md"` | | Defines the component size. Accepted values: - A key from the provided size map (`keyof T`), e.g. `'sm' \| 'md' \| 'lg'` - A custom CSS size string (e.g. `'20px'`, `'1.5rem'`, `'2em'`) - A number, typically interpreted as pixels by size par… |
729
773
 
730
774
  ## Events
@@ -39,10 +39,10 @@ the switch has focus — pin it open with `persistent-hint`.
39
39
 
40
40
  ```vue
41
41
  <template>
42
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-4">
42
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-4">
43
43
  <div class="flex flex-col gap-0.5">
44
44
  <p class="text-sm font-medium">Account security</p>
45
- <p class="text-xs text-[color:var(--bb-text-muted)]">
45
+ <p class="text-xs text-(--bb-text-muted)">
46
46
  Preferences take effect as soon as you flip them.
47
47
  </p>
48
48
  </div>
@@ -88,10 +88,10 @@ state maps to. Handy when the switch drives an enum-like setting.
88
88
 
89
89
  ```vue
90
90
  <template>
91
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-4">
91
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-4">
92
92
  <div class="flex flex-col gap-0.5">
93
93
  <p class="text-sm font-medium">Profile visibility</p>
94
- <p class="text-xs text-[color:var(--bb-text-muted)]">
94
+ <p class="text-xs text-(--bb-text-muted)">
95
95
  Stores an enum string instead of a boolean.
96
96
  </p>
97
97
  </div>
@@ -102,7 +102,7 @@ state maps to. Handy when the switch drives an enum-like setting.
102
102
  name="visibility"
103
103
  true-value="public"
104
104
  />
105
- <p class="text-xs text-[color:var(--bb-text-muted)]">
105
+ <p class="text-xs text-(--bb-text-muted)">
106
106
  Stored value: <code>{{ visibility }}</code>
107
107
  </p>
108
108
  </div>
@@ -133,10 +133,10 @@ the user cannot queue conflicting toggles.
133
133
 
134
134
  ```vue
135
135
  <template>
136
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-4">
136
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-4">
137
137
  <div class="flex flex-col gap-0.5">
138
138
  <p class="text-sm font-medium">Workspace</p>
139
- <p class="text-xs text-[color:var(--bb-text-muted)]">
139
+ <p class="text-xs text-(--bb-text-muted)">
140
140
  Northwind Billing — flips save immediately and revert on failure.
141
141
  </p>
142
142
  </div>
@@ -151,7 +151,7 @@ the user cannot queue conflicting toggles.
151
151
  <p v-if="error" class="text-xs text-red-600" data-testid="error">
152
152
  {{ error }}
153
153
  </p>
154
- <p class="text-xs text-[color:var(--bb-text-muted)]" data-testid="status">
154
+ <p class="text-xs text-(--bb-text-muted)" data-testid="status">
155
155
  {{ saving ? 'Saving…' : maintenance ? 'Enabled' : 'Disabled' }}
156
156
  </p>
157
157
  </div>
@@ -233,10 +233,10 @@ its focus ring (style against `focusVisible`).
233
233
 
234
234
  ```vue
235
235
  <template>
236
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-4">
236
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-4">
237
237
  <div class="flex flex-col gap-0.5">
238
238
  <p class="text-sm font-medium">Appearance</p>
239
- <p class="text-xs text-[color:var(--bb-text-muted)]">
239
+ <p class="text-xs text-(--bb-text-muted)">
240
240
  The icon slot replaces the whole track — style checked state yourself.
241
241
  </p>
242
242
  </div>
@@ -291,10 +291,10 @@ a stage pill next to the flag name:
291
291
 
292
292
  ```vue
293
293
  <template>
294
- <div class="grid max-w-sm gap-3 rounded-[var(--bb-radius)] border p-4">
294
+ <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-4">
295
295
  <div class="flex flex-col gap-0.5">
296
296
  <p class="text-sm font-medium">Feature flags</p>
297
- <p class="text-xs text-[color:var(--bb-text-muted)]">
297
+ <p class="text-xs text-(--bb-text-muted)">
298
298
  Workspace experiments for Northwind Billing.
299
299
  </p>
300
300
  </div>
@@ -365,16 +365,22 @@ const usageAnalytics = ref(true);
365
365
 
366
366
  ```vue
367
367
  <template>
368
- <div class="grid max-w-md gap-2 rounded-[var(--bb-radius)] border p-3">
368
+ <div class="grid max-w-md gap-2 rounded-(--bb-radius) border p-3">
369
369
  <div class="flex flex-col gap-0.5 pb-1">
370
370
  <p class="text-sm font-medium">Playback</p>
371
- <p class="text-xs text-[color:var(--bb-text-muted)]">
371
+ <p class="text-xs text-(--bb-text-muted)">
372
372
  Label-heavy rows with the control pinned to the trailing edge.
373
373
  </p>
374
374
  </div>
375
+ <!-- `reverse` puts the label first; the ratio pattern then reads in that
376
+ VISUAL order (label 6 : control 2 → 75% / 25%). The ratio alone
377
+ leaves the switch at the START of its track, a quarter-width in
378
+ from the edge — `input-position="right"` is what pins it to the
379
+ trailing edge. -->
375
380
  <BbSwitch
376
381
  v-model="autoplay"
377
382
  direction="xxxxxx xx"
383
+ input-position="right"
378
384
  label="Autoplay videos"
379
385
  name="autoplay"
380
386
  reverse
@@ -382,12 +388,13 @@ const usageAnalytics = ref(true);
382
388
  <BbSwitch
383
389
  v-model="captions"
384
390
  direction="xxxxxx xx"
391
+ input-position="right"
385
392
  label="Always show captions"
386
393
  name="captions"
387
394
  reverse
388
395
  />
389
396
  <!-- A described row keeps the same trailing edge: `input-position`
390
- applies on the description path too, and the switch pins to the
397
+ applies on the description path too, and the switch aligns with the
391
398
  label's FIRST line rather than the centre of the two-line block. -->
392
399
  <BbSwitch
393
400
  v-model="downloads"
@@ -422,10 +429,10 @@ What a panel must not do is both.
422
429
 
423
430
  ```vue
424
431
  <template>
425
- <div class="grid max-w-md gap-3 rounded-[var(--bb-radius)] border p-4">
432
+ <div class="grid max-w-md gap-3 rounded-(--bb-radius) border p-4">
426
433
  <div class="flex flex-col gap-0.5">
427
434
  <p class="text-sm font-medium">Email notifications</p>
428
- <p class="text-xs text-[color:var(--bb-text-muted)]">
435
+ <p class="text-xs text-(--bb-text-muted)">
429
436
  Sent to alex@northwind.io — changes apply immediately.
430
437
  </p>
431
438
  </div>
@@ -439,7 +446,7 @@ What a panel must not do is both.
439
446
  :name="pref.key"
440
447
  />
441
448
  </div>
442
- <p class="text-xs text-[color:var(--bb-text-muted)]" data-testid="summary">
449
+ <p class="text-xs text-(--bb-text-muted)" data-testid="summary">
443
450
  {{ enabledCount }} of {{ preferences.length }} notifications on
444
451
  </p>
445
452
  </div>
@@ -44,7 +44,7 @@ stored value with `item-text` / `item-value` — never pre-map into
44
44
 
45
45
  ```vue
46
46
  <template>
47
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-3">
47
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-3">
48
48
  <BbSwitchGroup
49
49
  v-model="channels"
50
50
  compact
@@ -55,7 +55,7 @@ stored value with `item-text` / `item-value` — never pre-map into
55
55
  legend="Where should we reach you?"
56
56
  name="workspace-notifications"
57
57
  />
58
- <p class="text-xs text-[color:var(--bb-text-muted)]" data-testid="summary">
58
+ <p class="text-xs text-(--bb-text-muted)" data-testid="summary">
59
59
  {{ channels.length }} channel{{ channels.length === 1 ? '' : 's' }} active
60
60
  </p>
61
61
  </div>
@@ -115,7 +115,7 @@ explains the limit.
115
115
 
116
116
  ```vue
117
117
  <template>
118
- <div class="max-w-sm rounded-[var(--bb-radius)] border p-3">
118
+ <div class="max-w-sm rounded-(--bb-radius) border p-3">
119
119
  <BbSwitchGroup
120
120
  v-model="channels"
121
121
  compact
@@ -163,7 +163,7 @@ prop; when a dead row adds nothing, filter the item out of `items`.
163
163
 
164
164
  ```vue
165
165
  <template>
166
- <div class="max-w-sm rounded-[var(--bb-radius)] border p-3">
166
+ <div class="max-w-sm rounded-(--bb-radius) border p-3">
167
167
  <BbSwitchGroup
168
168
  v-model="enabled"
169
169
  compact
@@ -213,7 +213,7 @@ explains why:
213
213
 
214
214
  ```vue
215
215
  <template>
216
- <div class="grid max-w-sm gap-2 rounded-[var(--bb-radius)] border p-3">
216
+ <div class="grid max-w-sm gap-2 rounded-(--bb-radius) border p-3">
217
217
  <BbSwitchGroup
218
218
  compact
219
219
  item-text="label"
@@ -227,15 +227,12 @@ explains why:
227
227
  />
228
228
  <p
229
229
  v-if="error"
230
- class="text-xs text-[color:var(--bb-danger)]"
230
+ class="text-xs text-(--bb-danger)"
231
231
  data-testid="flags-error"
232
232
  >
233
233
  {{ error }}
234
234
  </p>
235
- <p
236
- class="text-xs text-[color:var(--bb-text-muted)]"
237
- data-testid="flags-status"
238
- >
235
+ <p class="text-xs text-(--bb-text-muted)" data-testid="flags-status">
239
236
  {{ saving ? 'Saving…' : `${enabled.length} of ${flags.length} flags on` }}
240
237
  </p>
241
238
  </div>
@@ -310,9 +307,7 @@ involved. See the [fetch-items](../../ai/guides/fetch-items-playbook.md) and
310
307
 
311
308
  ```vue
312
309
  <template>
313
- <div
314
- class="flex max-w-sm flex-col gap-3 rounded-[var(--bb-radius)] border p-3"
315
- >
310
+ <div class="flex max-w-sm flex-col gap-3 rounded-(--bb-radius) border p-3">
316
311
  <BbRadioGroup
317
312
  v-model="plan"
318
313
  compact
@@ -338,7 +333,7 @@ involved. See the [fetch-items](../../ai/guides/fetch-items-playbook.md) and
338
333
  name="alert-channels-by-plan"
339
334
  persistent-hint
340
335
  />
341
- <p class="channel-summary text-xs text-[color:var(--bb-text-muted)]">
336
+ <p class="channel-summary text-xs text-(--bb-text-muted)">
342
337
  {{ enabled.length }} channel{{ enabled.length === 1 ? '' : 's' }} on
343
338
  </p>
344
339
  </div>