bitboss-ui 3.0.0-beta.0 → 3.0.0-beta.2

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 (35) hide show
  1. package/bin/bitboss-ui.mjs +133 -12
  2. package/dist/ai/changelog.json +1 -1
  3. package/dist/ai/components.json +2 -2
  4. package/dist/ai/guides/ai-router.md +2 -2
  5. package/dist/ai/guides/design-tokens.md +46 -6
  6. package/dist/ai/guides/installation-and-plugin-setup.md +71 -5
  7. package/dist/ai/guides/migration/components/bb-alert.md +37 -0
  8. package/dist/ai/guides/migration/components/bb-avatar.md +47 -8
  9. package/dist/ai/guides/migration/components/bb-badge.md +23 -1
  10. package/dist/ai/guides/migration/components/bb-button.md +64 -0
  11. package/dist/ai/guides/migration/components/bb-checkbox-group.md +55 -1
  12. package/dist/ai/guides/migration/components/bb-date-picker-input.md +9 -2
  13. package/dist/ai/guides/migration/components/bb-dialog.md +121 -11
  14. package/dist/ai/guides/migration/components/bb-icon.md +42 -0
  15. package/dist/ai/guides/migration/components/bb-offcanvas.md +35 -1
  16. package/dist/ai/guides/migration/components/bb-rating.md +52 -1
  17. package/dist/ai/guides/migration/components/bb-select.md +48 -0
  18. package/dist/ai/guides/migration/components/bb-table.md +156 -10
  19. package/dist/ai/guides/migration/components/bb-tabs.md +79 -1
  20. package/dist/ai/guides/migration/components/bb-text-input.md +23 -1
  21. package/dist/ai/guides/migration/components/bb-toast.md +44 -10
  22. package/dist/ai/guides/migration/components/use-confirm.md +48 -13
  23. package/dist/ai/guides/migration/v2-to-v3.md +626 -108
  24. package/dist/ai/index.md +9 -9
  25. package/dist/ai/source/BbDialog.md +0 -3
  26. package/dist/ai/source/BbDropdown.md +24 -1
  27. package/dist/ai/source/BbDropdownGroup.md +24 -1
  28. package/dist/index.d.ts +2 -1
  29. package/dist/llms-full.txt +1814 -367
  30. package/dist/llms-medium.txt +82 -16
  31. package/dist/llms.txt +11 -11
  32. package/dist/styles.css +1 -1
  33. package/llms.txt +12 -12
  34. package/package.json +2 -1
  35. package/scripts/lib/validate-bb-markup.mjs +105 -17
@@ -6,16 +6,16 @@
6
6
  **This file is for bulk ingestion — search indexes, embeddings, offline
7
7
  evaluation. It is far larger than a useful prompt.** If you are an agent
8
8
  about to write code, do NOT read this. Fetch
9
- `https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/llms-medium.txt` instead, then hop to the one
9
+ `https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/llms-medium.txt` instead, then hop to the one
10
10
  component contract you need.
11
11
 
12
- Canonical URL: https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/llms-full.txt
13
- Index: https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/llms.txt
12
+ Canonical URL: https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/llms-full.txt
13
+ Index: https://cdn.jsdelivr.net/npm/bitboss-ui@beta/llms.txt
14
14
 
15
15
  ---
16
16
 
17
17
  <!-- dist/ai/BbAccordion.md -->
18
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbAccordion.md -->
18
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbAccordion.md -->
19
19
 
20
20
  # BbAccordion
21
21
 
@@ -570,7 +570,7 @@ is generated for you.
570
570
  ---
571
571
 
572
572
  <!-- dist/ai/BbAlert.md -->
573
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbAlert.md -->
573
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbAlert.md -->
574
574
 
575
575
  # BbAlert
576
576
 
@@ -993,7 +993,7 @@ const publish = () => {
993
993
  ---
994
994
 
995
995
  <!-- dist/ai/BbAsterisk.md -->
996
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbAsterisk.md -->
996
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbAsterisk.md -->
997
997
 
998
998
  # BbAsterisk
999
999
 
@@ -1045,7 +1045,7 @@ doesn't suit the layout; the glyph inherits nothing else from its surroundings.
1045
1045
  ---
1046
1046
 
1047
1047
  <!-- dist/ai/BbAvatar.md -->
1048
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbAvatar.md -->
1048
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbAvatar.md -->
1049
1049
 
1050
1050
  # BbAvatar
1051
1051
 
@@ -1566,7 +1566,7 @@ available in visible text or an adjacent label.
1566
1566
  ---
1567
1567
 
1568
1568
  <!-- dist/ai/BbBadge.md -->
1569
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBadge.md -->
1569
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBadge.md -->
1570
1570
 
1571
1571
  # BbBadge
1572
1572
 
@@ -2304,7 +2304,7 @@ selectors like `.bb-badge > .bb-badge__content`, which no longer match.
2304
2304
  ---
2305
2305
 
2306
2306
  <!-- dist/ai/BbBadgeButton.md -->
2307
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBadgeButton.md -->
2307
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBadgeButton.md -->
2308
2308
 
2309
2309
  # BbBadgeButton
2310
2310
 
@@ -2377,7 +2377,7 @@ selectors like `.bb-badge > .bb-badge__content`, which no longer match.
2377
2377
  ---
2378
2378
 
2379
2379
  <!-- dist/ai/BbBaseButton.md -->
2380
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseButton.md -->
2380
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseButton.md -->
2381
2381
 
2382
2382
  # BbBaseButton
2383
2383
 
@@ -2896,7 +2896,7 @@ const sent = ref(false);
2896
2896
  ---
2897
2897
 
2898
2898
  <!-- dist/ai/BbBaseCheckbox.md -->
2899
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseCheckbox.md -->
2899
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseCheckbox.md -->
2900
2900
 
2901
2901
  # BbBaseCheckbox
2902
2902
 
@@ -2951,7 +2951,7 @@ const sent = ref(false);
2951
2951
  ---
2952
2952
 
2953
2953
  <!-- dist/ai/BbBaseCheckboxGroup.md -->
2954
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseCheckboxGroup.md -->
2954
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseCheckboxGroup.md -->
2955
2955
 
2956
2956
  # BbBaseCheckboxGroup
2957
2957
 
@@ -3021,7 +3021,7 @@ const sent = ref(false);
3021
3021
  ---
3022
3022
 
3023
3023
  <!-- dist/ai/BbBaseCheckboxIcon.md -->
3024
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseCheckboxIcon.md -->
3024
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseCheckboxIcon.md -->
3025
3025
 
3026
3026
  # BbBaseCheckboxIcon
3027
3027
 
@@ -3203,7 +3203,7 @@ on the element (or an ancestor — they inherit):
3203
3203
  ---
3204
3204
 
3205
3205
  <!-- dist/ai/BbBaseColorInput.md -->
3206
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseColorInput.md -->
3206
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseColorInput.md -->
3207
3207
 
3208
3208
  # BbBaseColorInput
3209
3209
 
@@ -3274,7 +3274,7 @@ on the element (or an ancestor — they inherit):
3274
3274
  ---
3275
3275
 
3276
3276
  <!-- dist/ai/BbBaseColorPalette.md -->
3277
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseColorPalette.md -->
3277
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseColorPalette.md -->
3278
3278
 
3279
3279
  # BbBaseColorPalette
3280
3280
 
@@ -3319,7 +3319,7 @@ on the element (or an ancestor — they inherit):
3319
3319
  ---
3320
3320
 
3321
3321
  <!-- dist/ai/BbBaseDatePicker.md -->
3322
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseDatePicker.md -->
3322
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseDatePicker.md -->
3323
3323
 
3324
3324
  # BbBaseDatePicker
3325
3325
 
@@ -3368,7 +3368,7 @@ on the element (or an ancestor — they inherit):
3368
3368
  ---
3369
3369
 
3370
3370
  <!-- dist/ai/BbBaseDatePickerInput.md -->
3371
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseDatePickerInput.md -->
3371
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseDatePickerInput.md -->
3372
3372
 
3373
3373
  # BbBaseDatePickerInput
3374
3374
 
@@ -3447,7 +3447,7 @@ on the element (or an ancestor — they inherit):
3447
3447
  ---
3448
3448
 
3449
3449
  <!-- dist/ai/BbBaseInputContainer.md -->
3450
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseInputContainer.md -->
3450
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseInputContainer.md -->
3451
3451
 
3452
3452
  # BbBaseInputContainer
3453
3453
 
@@ -3501,7 +3501,7 @@ on the element (or an ancestor — they inherit):
3501
3501
  ---
3502
3502
 
3503
3503
  <!-- dist/ai/BbBaseNumberInput.md -->
3504
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseNumberInput.md -->
3504
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseNumberInput.md -->
3505
3505
 
3506
3506
  # BbBaseNumberInput
3507
3507
 
@@ -3578,7 +3578,7 @@ on the element (or an ancestor — they inherit):
3578
3578
  ---
3579
3579
 
3580
3580
  <!-- dist/ai/BbBaseRadio.md -->
3581
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseRadio.md -->
3581
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseRadio.md -->
3582
3582
 
3583
3583
  # BbBaseRadio
3584
3584
 
@@ -3630,7 +3630,7 @@ on the element (or an ancestor — they inherit):
3630
3630
  ---
3631
3631
 
3632
3632
  <!-- dist/ai/BbBaseRadioGroup.md -->
3633
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseRadioGroup.md -->
3633
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseRadioGroup.md -->
3634
3634
 
3635
3635
  # BbBaseRadioGroup
3636
3636
 
@@ -3700,7 +3700,7 @@ on the element (or an ancestor — they inherit):
3700
3700
  ---
3701
3701
 
3702
3702
  <!-- dist/ai/BbBaseRadioIcon.md -->
3703
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseRadioIcon.md -->
3703
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseRadioIcon.md -->
3704
3704
 
3705
3705
  # BbBaseRadioIcon
3706
3706
 
@@ -3897,7 +3897,7 @@ ancestor — they inherit):
3897
3897
  ---
3898
3898
 
3899
3899
  <!-- dist/ai/BbBaseRating.md -->
3900
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseRating.md -->
3900
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseRating.md -->
3901
3901
 
3902
3902
  # BbBaseRating
3903
3903
 
@@ -3955,7 +3955,7 @@ ancestor — they inherit):
3955
3955
  ---
3956
3956
 
3957
3957
  <!-- dist/ai/BbBaseSelect.md -->
3958
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseSelect.md -->
3958
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseSelect.md -->
3959
3959
 
3960
3960
  # BbBaseSelect
3961
3961
 
@@ -4049,7 +4049,7 @@ ancestor — they inherit):
4049
4049
  ---
4050
4050
 
4051
4051
  <!-- dist/ai/BbBaseSlider.md -->
4052
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseSlider.md -->
4052
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseSlider.md -->
4053
4053
 
4054
4054
  # BbBaseSlider
4055
4055
 
@@ -4111,7 +4111,7 @@ ancestor — they inherit):
4111
4111
  ---
4112
4112
 
4113
4113
  <!-- dist/ai/BbBaseSwitch.md -->
4114
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseSwitch.md -->
4114
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseSwitch.md -->
4115
4115
 
4116
4116
  # BbBaseSwitch
4117
4117
 
@@ -4166,7 +4166,7 @@ ancestor — they inherit):
4166
4166
  ---
4167
4167
 
4168
4168
  <!-- dist/ai/BbBaseSwitchGroup.md -->
4169
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseSwitchGroup.md -->
4169
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseSwitchGroup.md -->
4170
4170
 
4171
4171
  # BbBaseSwitchGroup
4172
4172
 
@@ -4236,7 +4236,7 @@ ancestor — they inherit):
4236
4236
  ---
4237
4237
 
4238
4238
  <!-- dist/ai/BbBaseSwitchIcon.md -->
4239
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseSwitchIcon.md -->
4239
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseSwitchIcon.md -->
4240
4240
 
4241
4241
  # BbBaseSwitchIcon
4242
4242
 
@@ -4428,7 +4428,7 @@ ancestor — they inherit):
4428
4428
  ---
4429
4429
 
4430
4430
  <!-- dist/ai/BbBaseTag.md -->
4431
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseTag.md -->
4431
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseTag.md -->
4432
4432
 
4433
4433
  # BbBaseTag
4434
4434
 
@@ -4502,7 +4502,7 @@ ancestor — they inherit):
4502
4502
  ---
4503
4503
 
4504
4504
  <!-- dist/ai/BbBaseTextarea.md -->
4505
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseTextarea.md -->
4505
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseTextarea.md -->
4506
4506
 
4507
4507
  # BbBaseTextarea
4508
4508
 
@@ -4570,7 +4570,7 @@ ancestor — they inherit):
4570
4570
  ---
4571
4571
 
4572
4572
  <!-- dist/ai/BbBaseTextInput.md -->
4573
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseTextInput.md -->
4573
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseTextInput.md -->
4574
4574
 
4575
4575
  # BbBaseTextInput
4576
4576
 
@@ -4651,7 +4651,7 @@ ancestor — they inherit):
4651
4651
  ---
4652
4652
 
4653
4653
  <!-- dist/ai/BbBaseTimePickerInput.md -->
4654
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBaseTimePickerInput.md -->
4654
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBaseTimePickerInput.md -->
4655
4655
 
4656
4656
  # BbBaseTimePickerInput
4657
4657
 
@@ -4719,7 +4719,7 @@ ancestor — they inherit):
4719
4719
  ---
4720
4720
 
4721
4721
  <!-- dist/ai/BbBreadcrumbs.md -->
4722
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbBreadcrumbs.md -->
4722
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbBreadcrumbs.md -->
4723
4723
 
4724
4724
  # BbBreadcrumbs
4725
4725
 
@@ -5310,7 +5310,7 @@ overflow calculation runs after hydration once real widths exist.
5310
5310
  ---
5311
5311
 
5312
5312
  <!-- dist/ai/BbButton.md -->
5313
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbButton.md -->
5313
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbButton.md -->
5314
5314
 
5315
5315
  # BbButton
5316
5316
 
@@ -6265,7 +6265,7 @@ Anti-patterns:
6265
6265
  ---
6266
6266
 
6267
6267
  <!-- dist/ai/BbCheckbox.md -->
6268
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbCheckbox.md -->
6268
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbCheckbox.md -->
6269
6269
 
6270
6270
  # BbCheckbox
6271
6271
 
@@ -6881,7 +6881,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
6881
6881
  ---
6882
6882
 
6883
6883
  <!-- dist/ai/BbCheckboxGroup.md -->
6884
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbCheckboxGroup.md -->
6884
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbCheckboxGroup.md -->
6885
6885
 
6886
6886
  # BbCheckboxGroup
6887
6887
 
@@ -7481,7 +7481,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
7481
7481
  ---
7482
7482
 
7483
7483
  <!-- dist/ai/BbCollapsible.md -->
7484
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbCollapsible.md -->
7484
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbCollapsible.md -->
7485
7485
 
7486
7486
  # BbCollapsible
7487
7487
 
@@ -7927,7 +7927,7 @@ const active = ref<DetailTab | null>('specs');
7927
7927
  ---
7928
7928
 
7929
7929
  <!-- dist/ai/BbColorInput.md -->
7930
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbColorInput.md -->
7930
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbColorInput.md -->
7931
7931
 
7932
7932
  # BbColorInput
7933
7933
 
@@ -8503,7 +8503,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
8503
8503
  ---
8504
8504
 
8505
8505
  <!-- dist/ai/BbColorPalette.md -->
8506
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbColorPalette.md -->
8506
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbColorPalette.md -->
8507
8507
 
8508
8508
  # BbColorPalette
8509
8509
 
@@ -9133,7 +9133,7 @@ styling (a caret, a highlight). Two props gate interaction:
9133
9133
  ---
9134
9134
 
9135
9135
  <!-- dist/ai/BbConfirm.md -->
9136
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbConfirm.md -->
9136
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbConfirm.md -->
9137
9137
 
9138
9138
  # BbConfirm
9139
9139
 
@@ -9822,7 +9822,7 @@ standard footer and resolve from inside the slot with its `yes` / `no` helpers.
9822
9822
  ---
9823
9823
 
9824
9824
  <!-- dist/ai/BbConfirmPortal.md -->
9825
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbConfirmPortal.md -->
9825
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbConfirmPortal.md -->
9826
9826
 
9827
9827
  # BbConfirmPortal
9828
9828
 
@@ -9856,7 +9856,7 @@ standard footer and resolve from inside the slot with its `yes` / `no` helpers.
9856
9856
  ---
9857
9857
 
9858
9858
  <!-- dist/ai/BbDatePicker.md -->
9859
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDatePicker.md -->
9859
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDatePicker.md -->
9860
9860
 
9861
9861
  # BbDatePicker
9862
9862
 
@@ -10172,7 +10172,7 @@ and digit typeahead; the time rail exposes labelled columns. Always pass a
10172
10172
  ---
10173
10173
 
10174
10174
  <!-- dist/ai/BbDatePickerInput.md -->
10175
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDatePickerInput.md -->
10175
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDatePickerInput.md -->
10176
10176
 
10177
10177
  # BbDatePickerInput
10178
10178
 
@@ -10963,7 +10963,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
10963
10963
  ---
10964
10964
 
10965
10965
  <!-- dist/ai/BbDialog.md -->
10966
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDialog.md -->
10966
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDialog.md -->
10967
10967
 
10968
10968
  # BbDialog
10969
10969
 
@@ -11777,7 +11777,7 @@ default falls back to `config.adaptive`.
11777
11777
  ---
11778
11778
 
11779
11779
  <!-- dist/ai/BbDropdown.md -->
11780
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDropdown.md -->
11780
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDropdown.md -->
11781
11781
 
11782
11782
  # BbDropdown
11783
11783
 
@@ -12930,7 +12930,7 @@ mid-session never swaps a live menu between flyout and sheet.
12930
12930
  ---
12931
12931
 
12932
12932
  <!-- dist/ai/BbDropdownButton.md -->
12933
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDropdownButton.md -->
12933
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDropdownButton.md -->
12934
12934
 
12935
12935
  # BbDropdownButton
12936
12936
 
@@ -13490,7 +13490,7 @@ the split control) and `boundary`.
13490
13490
  ---
13491
13491
 
13492
13492
  <!-- dist/ai/BbDropdownGroup.md -->
13493
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDropdownGroup.md -->
13493
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDropdownGroup.md -->
13494
13494
 
13495
13495
  # BbDropdownGroup
13496
13496
 
@@ -13530,7 +13530,7 @@ the split control) and `boundary`.
13530
13530
  ---
13531
13531
 
13532
13532
  <!-- dist/ai/BbDropzone.md -->
13533
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbDropzone.md -->
13533
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbDropzone.md -->
13534
13534
 
13535
13535
  # BbDropzone
13536
13536
 
@@ -14909,7 +14909,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
14909
14909
  ---
14910
14910
 
14911
14911
  <!-- dist/ai/BbForm.md -->
14912
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbForm.md -->
14912
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbForm.md -->
14913
14913
 
14914
14914
  # BbForm
14915
14915
 
@@ -15154,7 +15154,7 @@ marker stays consistent with the rules.
15154
15154
  ---
15155
15155
 
15156
15156
  <!-- dist/ai/BbIcon.md -->
15157
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbIcon.md -->
15157
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbIcon.md -->
15158
15158
 
15159
15159
  # BbIcon
15160
15160
 
@@ -15572,7 +15572,7 @@ wrapping it.
15572
15572
  ---
15573
15573
 
15574
15574
  <!-- dist/ai/BbIndicator.md -->
15575
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbIndicator.md -->
15575
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbIndicator.md -->
15576
15576
 
15577
15577
  # BbIndicator
15578
15578
 
@@ -15861,7 +15861,7 @@ const onClear = () => {
15861
15861
  ---
15862
15862
 
15863
15863
  <!-- dist/ai/BbNumberInput.md -->
15864
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbNumberInput.md -->
15864
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbNumberInput.md -->
15865
15865
 
15866
15866
  # BbNumberInput
15867
15867
 
@@ -16603,7 +16603,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
16603
16603
  ---
16604
16604
 
16605
16605
  <!-- dist/ai/BbOffCanvas.md -->
16606
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbOffCanvas.md -->
16606
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbOffCanvas.md -->
16607
16607
 
16608
16608
  # BbOffCanvas
16609
16609
 
@@ -17573,7 +17573,7 @@ The panel is a native `<dialog>` opened with `showModal()` — the same mechanic
17573
17573
  ---
17574
17574
 
17575
17575
  <!-- dist/ai/BbPagination.md -->
17576
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbPagination.md -->
17576
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbPagination.md -->
17577
17577
 
17578
17578
  # BbPagination
17579
17579
 
@@ -18102,7 +18102,7 @@ reach invalid pages.
18102
18102
  ---
18103
18103
 
18104
18104
  <!-- dist/ai/BbPopover.md -->
18105
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbPopover.md -->
18105
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbPopover.md -->
18106
18106
 
18107
18107
  # BbPopover
18108
18108
 
@@ -18755,7 +18755,7 @@ falls back to `config.adaptive`.
18755
18755
  ---
18756
18756
 
18757
18757
  <!-- dist/ai/BbProgress.md -->
18758
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbProgress.md -->
18758
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbProgress.md -->
18759
18759
 
18760
18760
  # BbProgress
18761
18761
 
@@ -19250,7 +19250,7 @@ What's still on you:
19250
19250
  ---
19251
19251
 
19252
19252
  <!-- dist/ai/BbRadio.md -->
19253
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbRadio.md -->
19253
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbRadio.md -->
19254
19254
 
19255
19255
  # BbRadio
19256
19256
 
@@ -19645,7 +19645,7 @@ listen to them for side concerns only. `autofocus` focuses the input on mount.
19645
19645
  ---
19646
19646
 
19647
19647
  <!-- dist/ai/BbRadioGroup.md -->
19648
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbRadioGroup.md -->
19648
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbRadioGroup.md -->
19649
19649
 
19650
19650
  # BbRadioGroup
19651
19651
 
@@ -20205,7 +20205,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
20205
20205
  ---
20206
20206
 
20207
20207
  <!-- dist/ai/BbRating.md -->
20208
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbRating.md -->
20208
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbRating.md -->
20209
20209
 
20210
20210
  # BbRating
20211
20211
 
@@ -20602,7 +20602,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
20602
20602
  ---
20603
20603
 
20604
20604
  <!-- dist/ai/BbSelect.md -->
20605
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSelect.md -->
20605
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSelect.md -->
20606
20606
 
20607
20607
  # BbSelect
20608
20608
 
@@ -22053,7 +22053,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
22053
22053
  ---
22054
22054
 
22055
22055
  <!-- dist/ai/BbSelectPopover.md -->
22056
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSelectPopover.md -->
22056
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSelectPopover.md -->
22057
22057
 
22058
22058
  # BbSelectPopover
22059
22059
 
@@ -23461,7 +23461,7 @@ floating popover everywhere.
23461
23461
  ---
23462
23462
 
23463
23463
  <!-- dist/ai/BbSlider.md -->
23464
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSlider.md -->
23464
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSlider.md -->
23465
23465
 
23466
23466
  # BbSlider
23467
23467
 
@@ -23987,7 +23987,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
23987
23987
  ---
23988
23988
 
23989
23989
  <!-- dist/ai/BbSmoothHeight.md -->
23990
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSmoothHeight.md -->
23990
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSmoothHeight.md -->
23991
23991
 
23992
23992
  # BbSmoothHeight
23993
23993
 
@@ -24505,7 +24505,7 @@ const expanded = ref(false);
24505
24505
  ---
24506
24506
 
24507
24507
  <!-- dist/ai/BbSpinner.md -->
24508
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSpinner.md -->
24508
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSpinner.md -->
24509
24509
 
24510
24510
  # BbSpinner
24511
24511
 
@@ -25251,7 +25251,7 @@ in `role="status"` with that text rendered `sr-only`. Prefer `BbButton`'s
25251
25251
  ---
25252
25252
 
25253
25253
  <!-- dist/ai/BbSwitch.md -->
25254
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSwitch.md -->
25254
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSwitch.md -->
25255
25255
 
25256
25256
  # BbSwitch
25257
25257
 
@@ -25861,7 +25861,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
25861
25861
  ---
25862
25862
 
25863
25863
  <!-- dist/ai/BbSwitchGroup.md -->
25864
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbSwitchGroup.md -->
25864
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbSwitchGroup.md -->
25865
25865
 
25866
25866
  # BbSwitchGroup
25867
25867
 
@@ -26407,7 +26407,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
26407
26407
  ---
26408
26408
 
26409
26409
  <!-- dist/ai/BbTable.md -->
26410
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTable.md -->
26410
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTable.md -->
26411
26411
 
26412
26412
  # BbTable
26413
26413
 
@@ -28675,7 +28675,7 @@ records list.
28675
28675
  ---
28676
28676
 
28677
28677
  <!-- dist/ai/BbTabs.md -->
28678
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTabs.md -->
28678
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTabs.md -->
28679
28679
 
28680
28680
  # BbTabs
28681
28681
 
@@ -29862,7 +29862,7 @@ route, so it is SSR-safe and shareable:
29862
29862
  ---
29863
29863
 
29864
29864
  <!-- dist/ai/BbTabsList.md -->
29865
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTabsList.md -->
29865
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTabsList.md -->
29866
29866
 
29867
29867
  # BbTabsList
29868
29868
 
@@ -29897,7 +29897,7 @@ route, so it is SSR-safe and shareable:
29897
29897
  ---
29898
29898
 
29899
29899
  <!-- dist/ai/BbTabsPanes.md -->
29900
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTabsPanes.md -->
29900
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTabsPanes.md -->
29901
29901
 
29902
29902
  # BbTabsPanes
29903
29903
 
@@ -29930,7 +29930,7 @@ route, so it is SSR-safe and shareable:
29930
29930
  ---
29931
29931
 
29932
29932
  <!-- dist/ai/BbTabsRoot.md -->
29933
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTabsRoot.md -->
29933
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTabsRoot.md -->
29934
29934
 
29935
29935
  # BbTabsRoot
29936
29936
 
@@ -29993,7 +29993,7 @@ route, so it is SSR-safe and shareable:
29993
29993
  ---
29994
29994
 
29995
29995
  <!-- dist/ai/BbTag.md -->
29996
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTag.md -->
29996
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTag.md -->
29997
29997
 
29998
29998
  # BbTag
29999
29999
 
@@ -30437,7 +30437,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
30437
30437
  ---
30438
30438
 
30439
30439
  <!-- dist/ai/BbTextarea.md -->
30440
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTextarea.md -->
30440
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTextarea.md -->
30441
30441
 
30442
30442
  # BbTextarea
30443
30443
 
@@ -31316,7 +31316,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
31316
31316
  ---
31317
31317
 
31318
31318
  <!-- dist/ai/BbTextInput.md -->
31319
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTextInput.md -->
31319
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTextInput.md -->
31320
31320
 
31321
31321
  # BbTextInput
31322
31322
 
@@ -32262,7 +32262,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
32262
32262
  ---
32263
32263
 
32264
32264
  <!-- dist/ai/BbTimePicker.md -->
32265
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTimePicker.md -->
32265
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTimePicker.md -->
32266
32266
 
32267
32267
  # BbTimePicker
32268
32268
 
@@ -32499,7 +32499,7 @@ typeahead. Always pass a `label` when the trigger is an unlabelled cell or icon.
32499
32499
  ---
32500
32500
 
32501
32501
  <!-- dist/ai/BbTimePickerInput.md -->
32502
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTimePickerInput.md -->
32502
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTimePickerInput.md -->
32503
32503
 
32504
32504
  # BbTimePickerInput
32505
32505
 
@@ -32707,7 +32707,7 @@ Importing this component from `bitboss-ui/validated` instead of `bitboss-ui` bin
32707
32707
  ---
32708
32708
 
32709
32709
  <!-- dist/ai/BbToast.md -->
32710
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbToast.md -->
32710
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbToast.md -->
32711
32711
 
32712
32712
  # BbToast
32713
32713
 
@@ -33412,7 +33412,7 @@ see the `useSafeArea` composable doc.
33412
33412
  ---
33413
33413
 
33414
33414
  <!-- dist/ai/BbToastPortal.md -->
33415
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbToastPortal.md -->
33415
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbToastPortal.md -->
33416
33416
 
33417
33417
  # BbToastPortal
33418
33418
 
@@ -33446,7 +33446,7 @@ see the `useSafeArea` composable doc.
33446
33446
  ---
33447
33447
 
33448
33448
  <!-- dist/ai/BbTooltip.md -->
33449
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTooltip.md -->
33449
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTooltip.md -->
33450
33450
 
33451
33451
  # BbTooltip
33452
33452
 
@@ -33934,7 +33934,7 @@ const files: SharedFile[] = [
33934
33934
  ---
33935
33935
 
33936
33936
  <!-- dist/ai/BbTree.md -->
33937
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/BbTree.md -->
33937
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/BbTree.md -->
33938
33938
 
33939
33939
  # BbTree
33940
33940
 
@@ -34989,7 +34989,7 @@ row) with that in mind.
34989
34989
  ---
34990
34990
 
34991
34991
  <!-- dist/ai/ChipsBox.md -->
34992
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/ChipsBox.md -->
34992
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/ChipsBox.md -->
34993
34993
 
34994
34994
  # ChipsBox
34995
34995
 
@@ -35033,7 +35033,7 @@ row) with that in mind.
35033
35033
  ---
35034
35034
 
35035
35035
  <!-- dist/ai/ClearableButton.md -->
35036
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/ClearableButton.md -->
35036
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/ClearableButton.md -->
35037
35037
 
35038
35038
  # ClearableButton
35039
35039
 
@@ -35062,7 +35062,7 @@ row) with that in mind.
35062
35062
  ---
35063
35063
 
35064
35064
  <!-- dist/ai/CommaBox.md -->
35065
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/CommaBox.md -->
35065
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/CommaBox.md -->
35066
35066
 
35067
35067
  # CommaBox
35068
35068
 
@@ -35104,7 +35104,7 @@ row) with that in mind.
35104
35104
  ---
35105
35105
 
35106
35106
  <!-- dist/ai/CommonInputWrapper.md -->
35107
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/CommonInputWrapper.md -->
35107
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/CommonInputWrapper.md -->
35108
35108
 
35109
35109
  # CommonInputWrapper
35110
35110
 
@@ -35152,7 +35152,7 @@ row) with that in mind.
35152
35152
  ---
35153
35153
 
35154
35154
  <!-- dist/ai/CommonPopover.md -->
35155
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/CommonPopover.md -->
35155
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/CommonPopover.md -->
35156
35156
 
35157
35157
  # CommonPopover
35158
35158
 
@@ -35199,7 +35199,7 @@ row) with that in mind.
35199
35199
  ---
35200
35200
 
35201
35201
  <!-- dist/ai/composables/useBbConfig.md -->
35202
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useBbConfig.md -->
35202
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useBbConfig.md -->
35203
35203
 
35204
35204
  # useBbConfig
35205
35205
 
@@ -35348,7 +35348,7 @@ hydration, so the very first paint still renders in the default locale.
35348
35348
  ---
35349
35349
 
35350
35350
  <!-- dist/ai/composables/useBbFormContext.md -->
35351
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useBbFormContext.md -->
35351
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useBbFormContext.md -->
35352
35352
 
35353
35353
  # useBbFormContext
35354
35354
 
@@ -35458,7 +35458,7 @@ stays the only place the work happens.
35458
35458
  ---
35459
35459
 
35460
35460
  <!-- dist/ai/composables/useBbTableContext.md -->
35461
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useBbTableContext.md -->
35461
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useBbTableContext.md -->
35462
35462
 
35463
35463
  # useBbTableContext
35464
35464
 
@@ -35625,7 +35625,7 @@ const deleteSelected = async () => {
35625
35625
  ---
35626
35626
 
35627
35627
  <!-- dist/ai/composables/useBbTabsContext.md -->
35628
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useBbTabsContext.md -->
35628
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useBbTabsContext.md -->
35629
35629
 
35630
35630
  # useBbTabsContext
35631
35631
 
@@ -35743,7 +35743,7 @@ const { current, goTo, isLast } = useBbTabsContext<
35743
35743
  ---
35744
35744
 
35745
35745
  <!-- dist/ai/composables/useConfirm.md -->
35746
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useConfirm.md -->
35746
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useConfirm.md -->
35747
35747
 
35748
35748
  # useConfirm
35749
35749
 
@@ -36056,7 +36056,7 @@ content by name and render nothing themselves.
36056
36056
  ---
36057
36057
 
36058
36058
  <!-- dist/ai/composables/useCountdown.md -->
36059
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useCountdown.md -->
36059
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useCountdown.md -->
36060
36060
 
36061
36061
  # useCountdown
36062
36062
 
@@ -36142,7 +36142,7 @@ const { remainingFormatted, progress, running, paused, start, pause, reset } =
36142
36142
  ---
36143
36143
 
36144
36144
  <!-- dist/ai/composables/useId.md -->
36145
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useId.md -->
36145
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useId.md -->
36146
36146
 
36147
36147
  # useId
36148
36148
 
@@ -36202,7 +36202,7 @@ const { id } = useId();
36202
36202
  ---
36203
36203
 
36204
36204
  <!-- dist/ai/composables/useMobile.md -->
36205
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useMobile.md -->
36205
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useMobile.md -->
36206
36206
 
36207
36207
  # useMobile
36208
36208
 
@@ -36287,7 +36287,7 @@ const filters = ref({ status: 'active' });
36287
36287
  ---
36288
36288
 
36289
36289
  <!-- dist/ai/composables/useSafeArea.md -->
36290
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useSafeArea.md -->
36290
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useSafeArea.md -->
36291
36291
 
36292
36292
  # useSafeArea
36293
36293
 
@@ -36450,7 +36450,7 @@ useSafeArea({ side: 'right', element: sidebar });
36450
36450
  ---
36451
36451
 
36452
36452
  <!-- dist/ai/composables/useToast.md -->
36453
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/composables/useToast.md -->
36453
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/composables/useToast.md -->
36454
36454
 
36455
36455
  # useToast
36456
36456
 
@@ -36688,7 +36688,7 @@ const id = toast({
36688
36688
  ---
36689
36689
 
36690
36690
  <!-- dist/ai/ErrorIcon.md -->
36691
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/ErrorIcon.md -->
36691
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/ErrorIcon.md -->
36692
36692
 
36693
36693
  # ErrorIcon
36694
36694
 
@@ -36715,7 +36715,7 @@ const id = toast({
36715
36715
  ---
36716
36716
 
36717
36717
  <!-- dist/ai/FlatListBox.md -->
36718
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/FlatListBox.md -->
36718
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/FlatListBox.md -->
36719
36719
 
36720
36720
  # FlatListBox
36721
36721
 
@@ -36759,7 +36759,7 @@ const id = toast({
36759
36759
  ---
36760
36760
 
36761
36761
  <!-- dist/ai/GroupedListBox.md -->
36762
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/GroupedListBox.md -->
36762
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/GroupedListBox.md -->
36763
36763
 
36764
36764
  # GroupedListBox
36765
36765
 
@@ -36805,7 +36805,7 @@ const id = toast({
36805
36805
  ---
36806
36806
 
36807
36807
  <!-- dist/ai/guides/agent-contract.md -->
36808
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/agent-contract.md -->
36808
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/agent-contract.md -->
36809
36809
 
36810
36810
  # Agent Contract
36811
36811
 
@@ -37063,7 +37063,7 @@ case is not decidable from markup and has no hint.
37063
37063
  ---
37064
37064
 
37065
37065
  <!-- dist/ai/guides/ai-router.md -->
37066
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/ai-router.md -->
37066
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/ai-router.md -->
37067
37067
 
37068
37068
  # AI Router
37069
37069
 
@@ -37077,9 +37077,9 @@ Package export entry: `bitboss-ui/ai` → `dist/ai/index.md`.
37077
37077
  Subpaths: `bitboss-ui/ai/components.json`, `bitboss-ui/ai/guides/…`, etc.
37078
37078
 
37079
37079
  **Not installed, or can only fetch URLs?** The same files are served from the
37080
- published package at `https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/…`.
37080
+ published package at `https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/…`.
37081
37081
  If you can make only one request, fetch
37082
- [`…@alpha/dist/llms-medium.txt`](https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/llms-medium.txt)
37082
+ [`…@beta/dist/llms-medium.txt`](https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/llms-medium.txt)
37083
37083
  — this router plus the agent contract, setup, component picker, design language
37084
37084
  and the full component catalogue, in ~110 KB.
37085
37085
 
@@ -37182,7 +37182,7 @@ nothing else); the command names them if they are missing.
37182
37182
  ---
37183
37183
 
37184
37184
  <!-- dist/ai/guides/app-layout.md -->
37185
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/app-layout.md -->
37185
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/app-layout.md -->
37186
37186
 
37187
37187
  # App Layout
37188
37188
 
@@ -37549,7 +37549,7 @@ const footer = [
37549
37549
  ---
37550
37550
 
37551
37551
  <!-- dist/ai/guides/coherence-playbook.md -->
37552
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/coherence-playbook.md -->
37552
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/coherence-playbook.md -->
37553
37553
 
37554
37554
  # Coherence Playbook
37555
37555
 
@@ -37686,7 +37686,7 @@ Only for rules the `items` + `dependencies` contract cannot express — cross-co
37686
37686
  ---
37687
37687
 
37688
37688
  <!-- dist/ai/guides/component-picker.md -->
37689
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/component-picker.md -->
37689
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/component-picker.md -->
37690
37690
 
37691
37691
  # Component Picker
37692
37692
 
@@ -37961,7 +37961,7 @@ See:
37961
37961
  ---
37962
37962
 
37963
37963
  <!-- dist/ai/guides/design-language.md -->
37964
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/design-language.md -->
37964
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/design-language.md -->
37965
37965
 
37966
37966
  # Design Language
37967
37967
 
@@ -38545,7 +38545,7 @@ text, `--bb-label-weight`
38545
38545
  ---
38546
38546
 
38547
38547
  <!-- dist/ai/guides/design-tokens.md -->
38548
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/design-tokens.md -->
38548
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/design-tokens.md -->
38549
38549
 
38550
38550
  # Design Tokens
38551
38551
 
@@ -38968,12 +38968,40 @@ scope (`.brand { --bb-primary: … }`) does not re-derive them — re-apply the
38968
38968
  `light`/`dark` class on that scope, or override the derived tokens directly.
38969
38969
  Root-level theming (what the theme builder emits) is unaffected.
38970
38970
 
38971
- ### Animated themes
38971
+ ### Registered knobs (`@property`)
38972
38972
 
38973
- The color knobs (and `--bb-ring-opacity`) are registered with `@property`
38974
- (typed `<color>`, `inherits: true`), which makes them **animatable** and
38975
- fail-soft (an invalid `--bb-panel` falls back to the registered initial
38976
- value instead of detonating every derivation). Add `.theme-animated`
38973
+ Nine tokens are registered in `variables.css`: the eight color knobs with
38974
+ `syntax: '<color>'`, and `--bb-ring-opacity` with `syntax: '<percentage>'`.
38975
+ All are `inherits: true`.
38976
+
38977
+ **Registration types them, and a typed property rejects.** A declaration that
38978
+ does not parse as the declared syntax is invalid at computed-value time — the
38979
+ value is discarded, and because these inherit, an override at `:root` (no
38980
+ parent to inherit from) lands on the registered `initial-value`. That is the
38981
+ fail-soft half: an unparseable `--bb-panel` falls back instead of detonating
38982
+ every derivation that mixes it. It is also the trap. Write
38983
+
38984
+ ```css
38985
+ :root {
38986
+ --bb-ring-opacity: 0.15;
38987
+ } /* wrong: not a <percentage> */
38988
+ ```
38989
+
38990
+ and nothing errors — not the build, not the console. The computed value is
38991
+ just `25%`, and every ring paints 1.67× stronger than the `15%` you asked for
38992
+ (opacity is the ring's strength; its thickness is `--bb-ring-size`).
38993
+ Percentages need the `%`; colors must be a `<color>` (`transparent` and
38994
+ `currentColor` qualify, `none` and a mistyped hex do not). Nothing in the
38995
+ toolchain catches it either: `bitboss-ui check` reads `.vue`/`.md`, never
38996
+ stylesheets.
38997
+
38998
+ Fail-soft is only soft when the initial value is close to the real one. Every
38999
+ color knob is registered at its light default, so a rejected color override
39000
+ renders the library's own light theme. `--bb-ring-opacity` is registered at
39001
+ `25%`, which matches neither shipped scope (light `15%`, dark `40%`), so a
39002
+ rejected value there is the one that shows.
39003
+
39004
+ Typed knobs are also **animatable**. Add `.theme-animated`
38977
39005
  (main.css) to the element that carries the scheme class or theme overrides:
38978
39006
 
38979
39007
  ```html
@@ -39114,7 +39142,7 @@ escape the host dialog's `overflow: hidden` and centering transform.
39114
39142
  | `color-mix(in srgb, …)` | `color-mix(in oklab, …)` everywhere |
39115
39143
  | dialog/offcanvas `--radius-multiplier` ×1.8, literal `6px`/`8px`/`0.375rem` radii | `--bb-radius-surface` / `--bb-radius-sm` |
39116
39144
  | `--bb-contrasting` | `--bb-primary-fg` |
39117
- | `--bb-easing-bezier` | `--bb-ease` |
39145
+ | hard-coded `cubic-bezier()` literals (v2 exposed no easing token) | `--bb-ease` |
39118
39146
  | `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
39119
39147
  | `--bb-muted-color`, `--bb-hint`, `--bb-placeholder`, `--bb-icon-color`, `--bb-prefix-color` | `--bb-text-muted` |
39120
39148
  | `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
@@ -39131,6 +39159,18 @@ escape the host dialog's `overflow: hidden` and centering transform.
39131
39159
  | `--bb-table-{id}-track-{key}` (parent→nested width bridge) | `--table-{id}-track-{key}` (distinctive cross-component name, rule 3) |
39132
39160
  | `--bb-select-option-*`, `--bb-primary-base`, all `--x-light`/`--x-dark` shadows | deleted (dead) |
39133
39161
 
39162
+ **This table maps names, not values.** A token that is absent from it survived
39163
+ the rename — which is not the same as surviving unchanged. Six kept their v2
39164
+ name and their global scope and moved default anyway (input height, input
39165
+ vertical padding, prefix column width, label weight, ring size, leading); the
39166
+ before/after numbers are tabled in the v2→v3 migration guide, §3. The rule
39167
+ generalizes past this migration: a `--bb-*` name is a slot in the derivation
39168
+ graph, not a promise about the number in it. An override you carried forward
39169
+ keeps working and keeps your old look; an override you deleted because "the
39170
+ name still exists" hands you whatever `variables.css` declares today. Check
39171
+ each survivor against `variables.css` and delete only the ones that were
39172
+ restating a default.
39173
+
39134
39174
  ## Enforcement
39135
39175
 
39136
39176
  `npm run check:tokens` (see `scripts/check-tokens.mjs`) fails the build when:
@@ -39174,7 +39214,7 @@ like it would replace the seal rule structurally, but it breaks the system:
39174
39214
  ---
39175
39215
 
39176
39216
  <!-- dist/ai/guides/fetch-items-playbook.md -->
39177
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/fetch-items-playbook.md -->
39217
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/fetch-items-playbook.md -->
39178
39218
 
39179
39219
  # Fetch Data Items Playbook
39180
39220
 
@@ -39338,7 +39378,7 @@ const fetchInvoices = async (): Promise<Invoice[]> => {
39338
39378
  ---
39339
39379
 
39340
39380
  <!-- dist/ai/guides/floating-bottom-panel.md -->
39341
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/floating-bottom-panel.md -->
39381
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/floating-bottom-panel.md -->
39342
39382
 
39343
39383
  # Floating Bottom Panel
39344
39384
 
@@ -39469,7 +39509,7 @@ destructive path and clearing the selection after a bulk action.
39469
39509
  ---
39470
39510
 
39471
39511
  <!-- dist/ai/guides/icons-policy.md -->
39472
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/icons-policy.md -->
39512
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/icons-policy.md -->
39473
39513
 
39474
39514
  # Icons Policy
39475
39515
 
@@ -39727,7 +39767,7 @@ markup) yourself:
39727
39767
  ---
39728
39768
 
39729
39769
  <!-- dist/ai/guides/inertia-helpers.md -->
39730
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/inertia-helpers.md -->
39770
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/inertia-helpers.md -->
39731
39771
 
39732
39772
  # Inertia Helpers
39733
39773
 
@@ -41347,7 +41387,7 @@ export const useUntil = <T = boolean, U = boolean>(
41347
41387
  ---
41348
41388
 
41349
41389
  <!-- dist/ai/guides/inline-edit-playbook.md -->
41350
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/inline-edit-playbook.md -->
41390
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/inline-edit-playbook.md -->
41351
41391
 
41352
41392
  # Inline Edit Playbook
41353
41393
 
@@ -41689,7 +41729,7 @@ about_ the record rather than a _decision on_ it, leave it alone.
41689
41729
  ---
41690
41730
 
41691
41731
  <!-- dist/ai/guides/installation-and-plugin-setup.md -->
41692
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/installation-and-plugin-setup.md -->
41732
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/installation-and-plugin-setup.md -->
41693
41733
 
41694
41734
  # Installation and Plugin Setup
41695
41735
 
@@ -41701,16 +41741,21 @@ Source of truth for integrating `bitboss-ui`. Two pieces are always required:
41701
41741
  ## 1) Install
41702
41742
 
41703
41743
  ```bash
41704
- # v3 is a prerelease — the `@alpha` tag is REQUIRED.
41744
+ # v3 is a prerelease — a dist-tag is REQUIRED.
41705
41745
  # A bare `npm install bitboss-ui` still resolves to v2, which has no build
41706
41746
  # plugin and no `dist/ai` knowledge base, so everything below silently fails.
41707
- npm install bitboss-ui@alpha
41747
+ npm install bitboss-ui@beta
41708
41748
  # recommended default icon set (any @iconify-json/* works)
41709
41749
  npm install -D @iconify-json/lucide
41710
41750
  ```
41711
41751
 
41712
41752
  Confirm you got v3 before continuing — `npm ls bitboss-ui` must report a
41713
- `3.0.0-alpha.*` version. If it reports `2.x`, nothing in this guide applies.
41753
+ `3.0.0-*` version. If it reports `2.x`, nothing in this guide applies.
41754
+
41755
+ **`@beta` is the current v3 channel.** `@alpha` still resolves, but it is
41756
+ frozen at the last alpha and does not receive fixes — it is not simply an older
41757
+ copy of the same line. `@latest` is the **v2** line and always will be until v3
41758
+ goes stable, so never reach for it here.
41714
41759
 
41715
41760
  Peer dependency: `vue ^3.5.12` — the only required one. `@inertiajs/vue3` is an **optional** peer, needed only in Inertia apps.
41716
41761
 
@@ -41907,6 +41952,48 @@ import 'bitboss-ui/styles.css';
41907
41952
  import 'bitboss-ui/reset.css';
41908
41953
  ```
41909
41954
 
41955
+ **Exception — an app that needs its own CSS to load _before_ the library.**
41956
+ Auto-injection is library-first and not configurable: the runtime plugin
41957
+ inserts its `<style>` **before the first** `style`/`link[rel="stylesheet"]`
41958
+ already in `<head>` (`src/utils/injectLibraryStyles.ts`), so the guaranteed
41959
+ order above is exactly what you get and every app stylesheet — your Tailwind
41960
+ entry included — lands after the library. There is no option to inject later.
41961
+
41962
+ That is the order most apps want. It is the wrong order if you need library
41963
+ rules to beat Tailwind's Preflight **by source order**, which is the case on
41964
+ **Tailwind v3**: `@tailwind base` compiles to ordinary unlayered CSS, so under
41965
+ auto-injection Preflight sits after the library sheet and its element resets
41966
+ (`button`, `input`, `h1`, …) win every equal-specificity tie. `injectStyles:
41967
+ false` + `resetCss: false` (the default) disables injection entirely and hands
41968
+ you the order:
41969
+
41970
+ ```ts
41971
+ // vite.config.ts / the `bitboss` key in nuxt.config.ts
41972
+ bitbossUi({ iconDir: '…', injectStyles: false, resetCss: false });
41973
+ ```
41974
+
41975
+ ```ts
41976
+ // app entry — order is yours now
41977
+ import './app.css'; // your Tailwind entry
41978
+ import 'bitboss-ui/styles.css'; // after Tailwind, so library rules win the ties
41979
+ ```
41980
+
41981
+ Leave `resetCss` at `false` here — Preflight already reset everything.
41982
+ Nothing else about the plugin changes; `injectStyles` controls only whether the
41983
+ library injects its own `<style>`.
41984
+
41985
+ **Tailwind v4 apps do not need this** — source order does not decide their ties
41986
+ at all. `@import 'tailwindcss'` puts Preflight in a real `@layer base` and
41987
+ utilities in `@layer utilities`, and layered rules lose to unlayered rules of
41988
+ equal specificity **regardless of source order**. `dist/styles.css` declares no
41989
+ `@layer`, so the library wins those ties wherever it is injected: leave
41990
+ auto-injection on. The same mechanic bites the other way, and silently — a
41991
+ utility cannot re-size a Bb component either. `<BbButton class="h-12 px-6">`
41992
+ loses to the library's own `.bb-button { height: var(--h); padding-left:
41993
+ var(--px); padding-right: var(--px) }` (equal specificity, unlayered), with no
41994
+ warning from anything. Size Bb components through their props and tokens, never
41995
+ utilities. (Same trap as the scoped-`<style>` case above, one level up.)
41996
+
41910
41997
  ## 6) Icons
41911
41998
 
41912
41999
  - `iconDir` (required) is scanned recursively for `.svg` files; each file becomes `local:<basename>`.
@@ -41948,6 +42035,15 @@ When `true`, the **dev server** adds `./node_modules/.bitboss-ui/local-icons.jso
41948
42035
 
41949
42036
  When `true`, the **dev server** registers the bitboss-ui MCP server in each project-scoped agent-harness config on boot — `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.vscode/mcp.json` (VS Code / Copilot) — so agents can query the knowledge base with no manual setup. The write is merge-safe (leaves other servers alone) and idempotent (only logs when it actually changes a file). Windsurf is intentionally **not** written here: its config is global (`~/.codeium`), and silently editing a home-dir file that affects every project on the machine from a dev-server boot would be surprising — run `npx bitboss-ui ai-init --mcp` to register Windsurf too. This flag is the zero-command equivalent of that command's project-scoped writes. The MCP server's own runtime — `@modelcontextprotocol/sdk` and `zod` — ships as **optional peer dependencies** (~12 MB, more than every runtime dependency of the library combined, and loaded by nothing else), so install them with `npm i -D @modelcontextprotocol/sdk zod` before the harness first launches the server; both this flag and `ai-init --mcp` warn when they are missing.
41950
42037
 
42038
+ **MCP is a convenience, not a requirement, and skipping it costs no
42039
+ knowledge.** The server is a reader over files the install already put on disk
42040
+ — every tool `readFileSync`s out of `node_modules/bitboss-ui/dist/ai/` (plus
42041
+ `dist/styles.css` for the token lookup). An agent that opens that directory
42042
+ itself has the identical knowledge base, `npx bitboss-ui check` runs without the
42043
+ peers, and `ai-init` (no `--mcp`) still writes the harness pointers. If you are
42044
+ migrating under a "no new dependencies" constraint, leave `mcp` at `false`,
42045
+ skip `--mcp`, and point your agent at `dist/ai/guides/ai-router.md`.
42046
+
41951
42047
  ### `inertiaLinkName?: string` (default: `'Link'`)
41952
42048
 
41953
42049
  Global component name used for Inertia links. **Optional** — when nothing is
@@ -42126,7 +42222,17 @@ than inventing hues.
42126
42222
 
42127
42223
  The package ships its agent docs in `node_modules/bitboss-ui/dist/ai/`. **Start at** `guides/ai-router.md`, then the catalogue `index.md`. Run `npx bitboss-ui ai-init` in the consumer project to inject harness-agnostic pointers (AGENTS.md, Cursor rules, Copilot instructions, Claude skill, Windsurf rules) so coding agents discover them without a vendor lock-in. After generating Bb\* markup, run `npx bitboss-ui check` (supports `--json`) so unknown props fail before review.
42128
42224
 
42129
- Add `--mcp` to also register a live MCP server (`npx bitboss-ui@<installed version> mcp`, pinned so npx can never fetch a different version from the registry) the harness launches on demand — it merges the entry (never overwriting other servers) into `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (VS Code / Copilot), and `~/.codeium/windsurf/mcp_config.json` (Windsurf). **Windsurf's config is global** — it has no per-project scope, so registering it affects every project on the machine; that is why only the explicit `ai-init --mcp` command writes it, while the plugin's `mcp: true` dev-server flag (§7) auto-registers the project-scoped harnesses only. Install the server's optional peers first — `npm i -D @modelcontextprotocol/sdk zod` — they are not dependencies of the library (~12 MB used only by the MCP server); the command warns if they are absent.
42225
+ **Three tiers ship under `dist/ai/`**, and the third one is easy to miss:
42226
+ `guides/` + `recipes/` are the authored prose; `<Name>.md` at the root is the
42227
+ generated typed contract for each component (props, slots, events, examples);
42228
+ `source/<Name>.md` is that same component's **complete implementation** —
42229
+ `.vue` template, `types.ts` and `index.css` in one file, one per component
42230
+ including the internal ones the package does not export. Read the source tier
42231
+ when the contract cannot answer the question — the usual case is rebuilding
42232
+ chrome around a control the library does not ship, since the class names such a
42233
+ template emits are already styled by the public `bitboss-ui/styles.css`.
42234
+
42235
+ Add `--mcp` to also register a live MCP server (`npx bitboss-ui@<installed version> mcp`, pinned so npx can never fetch a different version from the registry) the harness launches on demand — it merges the entry (never overwriting other servers) into `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (VS Code / Copilot), and `~/.codeium/windsurf/mcp_config.json` (Windsurf). **Windsurf's config is global** — it has no per-project scope, so registering it affects every project on the machine; that is why only the explicit `ai-init --mcp` command writes it, while the plugin's `mcp: true` dev-server flag (§7) auto-registers the project-scoped harnesses only. Install the server's optional peers first — `npm i -D @modelcontextprotocol/sdk zod` — they are not dependencies of the library (~12 MB used only by the MCP server); the command warns if they are absent. **`--mcp` is optional and skipping it loses you nothing but the transport** — the server only reads the `dist/ai/` files listed above, so run plain `ai-init` and let the agent read that directory if you cannot add the peers (see `mcp?: boolean` in §7).
42130
42236
 
42131
42237
  For a human-in-the-loop (and agent) safety net, add the `bitboss-ui/eslint-plugin` flat-config plugin (`...bitbossUi.configs.recommended`) so unknown/removed `Bb*` props surface as ESLint errors in the editor and CI — the same manifest checks as `bitboss-ui check`, reusing your `eslint-plugin-vue` parser. Real HTML attributes fall through untouched; escape hatches are the standard `<!-- eslint-disable-next-line bitboss-ui/no-unknown-attributes -->` and the rule's `allowAttributes` / `allowComponents` options. **`eslint --fix` also auto-migrates deprecated v2 props** (renames, boolean-polarity inversions like `allowWriting`→`disableWriting`, value remaps like BbToast `placement`→`position`, and deletion of inert removed props); structural migrations that need a slot/directive/CSS are reported but left by hand. **This library is TS-first (recipes ship `<script setup lang="ts">`, some with `generic="T"`), so wire `@typescript-eslint/parser` for both `.ts` and `.vue` files** — without it `vue-eslint-parser` throws a `Parsing error` on those files, which means no rule (including `no-unknown-attributes`) runs on them at all. See the README "ESLint plugin" section for the full config snippet (both parser blocks + the `npm i -D @typescript-eslint/parser` line) and the fix table. `recommended` also enables `bitboss-ui/no-active-class-on-root-link`: `active-class` on a link-capable `Bb*` component (`BbButton`/`BbBaseButton`/`BbBadgeButton`) matches by path PREFIX, so a link whose target is the literal root (`href="/"` / `to="/"`) reads as "active" on every URL — the rule flags that combination and points you at `exact-active-class` instead.
42132
42238
 
@@ -42146,7 +42252,7 @@ It also enables `bitboss-ui/require-partner-prop`: an opt-in prop set without it
42146
42252
  - [ ] build plugin configured: `bitboss-ui/vite` plugin or `bitboss-ui/nuxt` module (`bitboss` config key);
42147
42253
  - [ ] `iconDir` points at an existing SVG folder;
42148
42254
  - [ ] runtime plugin installed: `app.use(bitbossUiPlugin)` (automatic in Nuxt);
42149
- - [ ] style strategy chosen: auto injection (default) or `injectStyles: false` + `import 'bitboss-ui/styles.css'`;
42255
+ - [ ] style strategy chosen: auto injection (default) or `injectStyles: false` + `import 'bitboss-ui/styles.css'` — auto injection is always library-first, so a Tailwind **v3** app that needs Preflight to load before the library must take the second option (§5);
42150
42256
  - [ ] if a reset is required: `resetCss: true` or `import 'bitboss-ui/reset.css'`;
42151
42257
  - [ ] Inertia apps: `@inertiajs/vue3` installed (no link registration needed; set `inertiaLinkName` only if you register your own link wrapper).
42152
42258
  - [ ] building the [Layout Scaffold](../recipes/vue/layout-scaffold.md)? `npm i @vueuse/core` — `usePageShellContext.ts` imports `injectLocal`/`provideLocal` from it. This library depends on it too, so it can resolve via hoisting without being declared — don't rely on that; declare it in your own `package.json` or a strict installer (pnpm, Yarn PnP) will fail to resolve it.
@@ -42159,7 +42265,7 @@ If one of the checks fails, fix setup before creating component examples.
42159
42265
  ---
42160
42266
 
42161
42267
  <!-- dist/ai/guides/migration/components/bb-alert.md -->
42162
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-alert.md -->
42268
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-alert.md -->
42163
42269
 
42164
42270
  # BbAlert — v2 → v3
42165
42271
 
@@ -42194,19 +42300,56 @@ A hand-applied `class="bb-alert--warning"` also becomes `variant="warning"` —
42194
42300
  see [main guide §7](../v2-to-v3.md). Custom `theme` names: register via the
42195
42301
  plugin's `alertVariants` option and keep your CSS.
42196
42302
 
42303
+ ## ⚠ `theme` painted nothing in v2 — and now two identical call sites diverge
42304
+
42305
+ v2's stylesheet shipped **zero** `.bb-alert--*` rules. The component emitted
42306
+ `bb-alert--${theme}` faithfully, but unless your own CSS defined the rule, every
42307
+ value rendered the same default surface. `theme="success"`, `theme="blue"` and
42308
+ `theme="warning"` were the same alert.
42309
+
42310
+ In v3 the string is live, and what happens next depends on nothing but whether
42311
+ the name collides with a built-in (`outline`, `primary`, `destructive`,
42312
+ `warning`):
42313
+
42314
+ ```diff
42315
+ <!-- these two rendered identically in v2 -->
42316
+ - <BbAlert theme="success" title="Saved" />
42317
+ + <BbAlert variant="success" title="Saved" />
42318
+ <!-- `success` is not an alert built-in → compile error until you register it. You will notice. -->
42319
+
42320
+ - <BbAlert theme="warning" title="Quota almost reached" />
42321
+ + <BbAlert variant="warning" title="Quota almost reached" />
42322
+ <!-- `warning` IS a built-in → compiles, and silently repaints amber (#fffbea on #fee685). You will not. -->
42323
+ ```
42324
+
42325
+ The rename diff above is a **visual change** for `warning`, not a no-op. Before
42326
+ converting any `theme=` value, grep your own stylesheets for
42327
+ `.bb-alert--<value>`:
42328
+
42329
+ - **Nothing styled it** → the value was decoration. Drop the prop and take the
42330
+ v3 default, or register the name via `alertVariants` and ship no CSS for it,
42331
+ which holds v2's appearance exactly.
42332
+ - **Your CSS styled it** → register the name and keep your rule; `warning` is
42333
+ the one case where your rule now lands on top of a library skin, so check
42334
+ that you override every property it sets (background, border, `--main-color`,
42335
+ `--muted-color`) and not just the ones your v2 rule needed.
42336
+
42337
+ The same trap applies to `BbTooltip` and `toast()` — v2 styled none of their
42338
+ theme classes either.
42339
+
42197
42340
  ---
42198
42341
 
42199
42342
  <!-- dist/ai/guides/migration/components/bb-avatar.md -->
42200
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-avatar.md -->
42343
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-avatar.md -->
42201
42344
 
42202
42345
  # BbAvatar — v2 → v3
42203
42346
 
42204
42347
  ## Changes
42205
42348
 
42206
- | v2 | v3 | Kind |
42207
- | ---------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
42208
- | `color?: string` | removed | initials fallback always uses the primary tokens; override `.bb-avatar` CSS for anything custom |
42209
- | `timeout` | unchanged (SSR image grace period — **not** renamed; only `BbTooltip.timeout` became `delay`) | — |
42349
+ | v2 | v3 | Kind |
42350
+ | ---------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
42351
+ | `color?: string` | removed | the fallback surface paints from two locals on `.bb-avatar` — `--bg-color` / `--text-color` (below) |
42352
+ | `timeout` | unchanged (SSR image grace period — **not** renamed; only `BbTooltip.timeout` became `delay`) | — |
42210
42353
 
42211
42354
  ## Edits
42212
42355
 
@@ -42215,14 +42358,53 @@ plugin's `alertVariants` option and keep your CSS.
42215
42358
  + <BbAvatar :src="user.photo" :alt="user.name" />
42216
42359
  ```
42217
42360
 
42218
- If per-user colors mattered, set them in CSS on a wrapper
42219
- (`.team-avatar--purple .bb-avatar { … }`) — the component no longer carries
42220
- presentation-by-prop.
42361
+ ## Where the color went
42362
+
42363
+ `.bb-avatar` declares three locals and reads them for the fallback (initials,
42364
+ slotted content, or the built-in person glyph — an image covers all of them):
42365
+
42366
+ | local | default | paints |
42367
+ | ---------------------- | ------------------------------------------ | --------------------------- |
42368
+ | `--bg-color` | `var(--bb-primary)` | the avatar background |
42369
+ | `--text-color` | `var(--bb-primary-fg)` | initials / slotted fallback |
42370
+ | `--default-icon-color` | 80% mix of `--bb-primary-fg` over the fill | the built-in person glyph |
42371
+
42372
+ Set them on a scope, not on the component:
42373
+
42374
+ ```css
42375
+ .team-avatar .bb-avatar {
42376
+ --bg-color: #7c3aed;
42377
+ --text-color: #fff;
42378
+ }
42379
+ ```
42380
+
42381
+ ⚠ Set **both**. `--bg-color` alone leaves the initials at `--bb-primary-fg`,
42382
+ which is the contrast pair for the _primary_ color, not for yours — the symptom
42383
+ is white-on-pale or black-on-dark initials.
42384
+
42385
+ Use this for a color that genuinely varies per instance (per-team, per-tenant).
42386
+ If the v2 `color` was your brand color repeated at every call site, retheme
42387
+ `--bb-primary` / `--bb-primary-fg` once instead and write no per-avatar rules.
42388
+ That is the order the BbAvatar usage guide prescribes: retheme the tokens
42389
+ first, reach for the locals only when the value is per-instance.
42390
+
42391
+ ## `.bb-avatar--square` was never a library modifier
42392
+
42393
+ If your app has a `.bb-avatar--square` rule, it is **your** CSS. v2 shipped no
42394
+ such modifier — its entire avatar stylesheet was four rules, all hardcoding
42395
+ `border-radius: 50%`, and the string `square` appears nowhere in the v2
42396
+ package. v3 bakes in the same circle.
42397
+
42398
+ So nothing was removed and there is no v3 modifier to rename to. Your rule
42399
+ keeps working: it has the same specificity as the library's `.bb-avatar` rule
42400
+ and the library sheet is injected above your app CSS, so yours wins the tie.
42401
+ Rename the class to an app-owned block (`.p-avatar--square`) anyway, so the
42402
+ next reader does not take it for library API and go hunting for a `shape` prop.
42221
42403
 
42222
42404
  ---
42223
42405
 
42224
42406
  <!-- dist/ai/guides/migration/components/bb-badge.md -->
42225
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-badge.md -->
42407
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-badge.md -->
42226
42408
 
42227
42409
  # BbBadge — v2 → v3
42228
42410
 
@@ -42244,7 +42426,7 @@ re-express it with the new badge's `variant` API.
42244
42426
  | ------------------------------------ | ---------------------------------------------------------------------------------------------------- |
42245
42427
  | `content?: string \| number \| null` | `text?: string \| number` — plus new `max?: number` (renders `${max}+`) |
42246
42428
  | `color?: string` (free-form) | `variant?: IndicatorVariantType` (`default, success, info, warning, destructive`; default `default`) |
42247
- | `dot`, `left`, `bottom` | unchanged |
42429
+ | `dot`, `left`, `bottom` | unchanged — including precedence: `dot` renders a contentless bubble and **suppresses** the label |
42248
42430
  | `floating` | removed — the indicator always positions against its slotted anchor |
42249
42431
  | `#content` slot | dropped — use `text` |
42250
42432
 
@@ -42259,6 +42441,28 @@ re-express it with the new badge's `variant` API.
42259
42441
  If you styled `.bb-badge` in CSS for this use, the block is now
42260
42442
  `.bb-indicator` (`--dot`, `--left`, `--bottom`, `--<variant>` modifiers).
42261
42443
 
42444
+ ### `dot` beats the label — in both versions
42445
+
42446
+ `dot` and the label are not additive and never were. v2 rendered the content
42447
+ span only when `dot` was false; v3 does the same with `text`. Passing both has
42448
+ always produced a bare dot.
42449
+
42450
+ The trap is on the way out, not the way in. A v2 call site that toggled the dot
42451
+ off a count —
42452
+
42453
+ ```diff
42454
+ - <BbBadge :content="unread" :dot="unread !== null" />
42455
+ + <BbIndicator :text="unread" :dot="unread !== null" />
42456
+ ```
42457
+
42458
+ — shipped a **dot**, never the number, and the port above keeps it that way.
42459
+ Drop `dot` and port `content` to `:text="unread"` alone and the number appears
42460
+ where it never did before. Carry `dot` across unchanged unless you mean to
42461
+ change the design.
42462
+
42463
+ Nothing renders at all when `dot` is false and `text` is `null`/`undefined`, so
42464
+ `:text="count || undefined"` is the way to hide a zero.
42465
+
42262
42466
  ## v2 BbChip → v3 BbBadge
42263
42467
 
42264
42468
  Same role (dismissible token), two functional deltas: `clearable` is now
@@ -42333,7 +42537,7 @@ button can span the full pill height while only the circle looks interactive.
42333
42537
  ---
42334
42538
 
42335
42539
  <!-- dist/ai/guides/migration/components/bb-breadcrumbs.md -->
42336
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-breadcrumbs.md -->
42540
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-breadcrumbs.md -->
42337
42541
 
42338
42542
  # BbBreadcrumbs — v2 → v3
42339
42543
 
@@ -42360,7 +42564,7 @@ button can span the full pill height while only the circle looks interactive.
42360
42564
  ---
42361
42565
 
42362
42566
  <!-- dist/ai/guides/migration/components/bb-button.md -->
42363
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-button.md -->
42567
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-button.md -->
42364
42568
 
42365
42569
  # BbButton — v2 → v3
42366
42570
 
@@ -42413,10 +42617,74 @@ The `v-bb-tooltip` directive is registered by the runtime plugin (see the
42413
42617
  See [main guide §7](../v2-to-v3.md) for the full pattern and how to register
42414
42618
  custom variant names.
42415
42619
 
42620
+ ## Skin colors: four locals became six theme tokens per variant
42621
+
42622
+ v2 shipped **no** variant CSS — `.bb-button--outline`, `--secondary`, `--ghost`
42623
+ and friends had zero rules in the library sheet. If your app has buttons that
42624
+ look like anything but the primary fill, your stylesheet built the skin, by
42625
+ setting locals that `.bb-button` read:
42626
+
42627
+ ```css
42628
+ /* v2 theming.css — you wrote this, and it was the whole variant */
42629
+ .bb-button--outline {
42630
+ --color: #fff;
42631
+ --border-color: #d1d5db;
42632
+ --text-color: #111827;
42633
+ /* hover / pressed / focus were DERIVED from --color automatically */
42634
+ }
42635
+ ```
42636
+
42637
+ v3 reads none of those four names. Each variant paints from its own `--bb-*`
42638
+ family, declared in `variables.css` and overridable globally or on any scope:
42639
+
42640
+ | v2 local on `.bb-button` | v3 |
42641
+ | ------------------------------------------ | ------------------------ |
42642
+ | `--color` (fill) | `--bb-<variant>` |
42643
+ | `--text-color` | `--bb-<variant>-fg` |
42644
+ | `--border-color` | `--bb-<variant>-border` |
42645
+ | `--ring-color` | `--bb-<variant>-ring` |
42646
+ | _derived_ `mix(--color 90%, black)`, hover | `--bb-<variant>-hover` |
42647
+ | _derived_ `mix(--color 80%, black)`, press | `--bb-<variant>-pressed` |
42648
+
42649
+ `<variant>` is `primary`, `secondary`, `outline`, `ghost`, `destructive` or
42650
+ `link`.
42651
+
42652
+ ⚠ Note the last two rows. v2 **derived** hover and pressed from the fill; v3
42653
+ **states** them. Port only `--bb-outline` and the button keeps the library's
42654
+ default hover, which will not be a shade of your color.
42655
+
42656
+ ```css
42657
+ /* v3 equivalent of the v2 block above */
42658
+ :root {
42659
+ --bb-outline: #fff;
42660
+ --bb-outline-border: #d1d5db;
42661
+ --bb-outline-fg: #111827;
42662
+ --bb-outline-hover: #f3f4f6;
42663
+ --bb-outline-pressed: #e5e7eb;
42664
+ }
42665
+ ```
42666
+
42667
+ Silent by construction: a rule setting `--color` on `.bb-button` still parses,
42668
+ still applies, and is read by nothing. `rg -n -- '--color|--text-color|--border-color|--ring-color'`
42669
+ across your CSS and find the ones scoped to a button.
42670
+
42671
+ Don't confuse this with the `--bb-button-*` row in
42672
+ [main guide §3](../v2-to-v3.md): the button's **dimensions** went down into
42673
+ component locals, its **colors** went up into global theme tokens. Opposite
42674
+ directions, same component.
42675
+
42676
+ ## `loading-text` was never a BbButton prop
42677
+
42678
+ Not in v2, not in v3. If you find `loading-text` on a v2 button it was landing
42679
+ in `$attrs` and rendering nothing — delete it rather than looking for the
42680
+ replacement. The prop is real on the list and group surfaces — `BbSelect`,
42681
+ `BbCheckboxGroup`, `BbRadioGroup`, `BbTable`, `BbSelectPopover` — where it
42682
+ carries over to v3 under the same name.
42683
+
42416
42684
  ---
42417
42685
 
42418
42686
  <!-- dist/ai/guides/migration/components/bb-checkbox-group.md -->
42419
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-checkbox-group.md -->
42687
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-checkbox-group.md -->
42420
42688
 
42421
42689
  # BbCheckboxGroup / BbRadioGroup / BbSwitchGroup — v2 → v3
42422
42690
 
@@ -42469,10 +42737,64 @@ The `item.disabled` change is the dangerous one: nothing errors, options that
42469
42737
  used to render disabled simply become selectable. Grep item-building code for
42470
42738
  `disabled:` near these groups.
42471
42739
 
42740
+ ## DOM: the shared `bb-cr-container` block split three ways
42741
+
42742
+ v2 rendered one prefix for all three groups — `bb-cr-container` ("checkbox /
42743
+ radio") — so a rule written for one group hit all of them. v3 gives each group
42744
+ its own block; the suffixes are otherwise identical.
42745
+
42746
+ | v2 | v3 (`{checkbox,radio,switch}`) |
42747
+ | ------------------------------------- | ----------------------------------------- |
42748
+ | `.bb-cr-container` | `.bb-base-{checkbox,radio,switch}-group` |
42749
+ | `.bb-cr-container--horizontal` | `.bb-base-…-group--horizontal` |
42750
+ | `.bb-cr-container--vertical` | `.bb-base-…-group--vertical` |
42751
+ | `.bb-cr-container--errors` | `.bb-base-…-group--errors` |
42752
+ | `.bb-cr-container__container` | `.bb-base-…-group__container` |
42753
+ | `.bb-cr-container__loading-container` | `.bb-base-…-group__loading-container` |
42754
+ | `.bb-cr-container__no-data-container` | `.bb-base-…-group__no-data-container` |
42755
+ | `.bb-cr-container-option` | `.bb-base-…-group-option` |
42756
+ | `.bb-cr-container-option__text` | `.bb-base-…-group-option__text` |
42757
+ | — | `.bb-base-…-group--warnings` (new) |
42758
+ | — | `.bb-base-…-group-option--selected` (new) |
42759
+
42760
+ Nothing in v3 renders any `bb-cr-container` class, and nothing warns — the rules
42761
+ simply stop matching. **Grep your CSS and test selectors for `bb-cr-container`.**
42762
+
42763
+ One rule that used to cover three components is now three rules. If you were
42764
+ relying on the shared prefix, group the selectors:
42765
+
42766
+ ```css
42767
+ .bb-base-checkbox-group-option__text,
42768
+ .bb-base-radio-group-option__text,
42769
+ .bb-base-switch-group-option__text {
42770
+ font-weight: 500;
42771
+ }
42772
+ ```
42773
+
42774
+ **`BbRating` is not in this family.** v2's stylesheet carried
42775
+ `.bb-rating … .bb-cr-container__container` rules, but the component never
42776
+ rendered those classes on either version — it has always used
42777
+ `.bb-base-rating__inner-container` / `.bb-base-rating__option`. See
42778
+ [bb-rating.md](./bb-rating.md).
42779
+
42780
+ ## `legend` is required — and always was
42781
+
42782
+ `legend: string` is a required prop on `BbCheckboxGroup`, `BbRadioGroup` and
42783
+ `BbSwitchGroup` in **v2 and v3 alike** (v2 `dist/components/BbCheckboxGroup/types.d.ts`
42784
+ declares `legend: string`, not `legend?`). If the upgrade surfaced missing-legend
42785
+ errors across your app, that is a pre-existing bug the stricter v3 build made
42786
+ visible — not a v3 break, and not something to "fix" as part of the upgrade.
42787
+
42788
+ Treat it as its own change, separately from the migration. One migration team
42789
+ added `legend` to six groups mid-upgrade; supplying it let the generic `T`
42790
+ resolve properly, which unblocked type inference on the whole subtree and
42791
+ surfaced a cascade of unrelated downstream type errors. They reverted the lot.
42792
+ Land the migration first, then add legends one component at a time.
42793
+
42472
42794
  ---
42473
42795
 
42474
42796
  <!-- dist/ai/guides/migration/components/bb-checkbox.md -->
42475
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-checkbox.md -->
42797
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-checkbox.md -->
42476
42798
 
42477
42799
  # BbCheckbox / BbRadio / BbSwitch — v2 → v3
42478
42800
 
@@ -42519,7 +42841,7 @@ compiler cannot catch a boolean that changed meaning.
42519
42841
  ---
42520
42842
 
42521
42843
  <!-- dist/ai/guides/migration/components/bb-chip.md -->
42522
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-chip.md -->
42844
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-chip.md -->
42523
42845
 
42524
42846
  # BbChip — removed in v3
42525
42847
 
@@ -42555,7 +42877,7 @@ Full badge/indicator picture: [bb-badge.md](./bb-badge.md).
42555
42877
  ---
42556
42878
 
42557
42879
  <!-- dist/ai/guides/migration/components/bb-collapsible.md -->
42558
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-collapsible.md -->
42880
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-collapsible.md -->
42559
42881
 
42560
42882
  # BbCollapsible — v2 → v3
42561
42883
 
@@ -42582,7 +42904,7 @@ stays in your markup.
42582
42904
  ---
42583
42905
 
42584
42906
  <!-- dist/ai/guides/migration/components/bb-color-input.md -->
42585
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-color-input.md -->
42907
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-color-input.md -->
42586
42908
 
42587
42909
  # BbColorInput — v2 → v3
42588
42910
 
@@ -42606,7 +42928,7 @@ stays in your markup.
42606
42928
  ---
42607
42929
 
42608
42930
  <!-- dist/ai/guides/migration/components/bb-color-palette.md -->
42609
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-color-palette.md -->
42931
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-color-palette.md -->
42610
42932
 
42611
42933
  # BbColorPalette — v2 → v3
42612
42934
 
@@ -42638,7 +42960,7 @@ stays in your markup.
42638
42960
  ---
42639
42961
 
42640
42962
  <!-- dist/ai/guides/migration/components/bb-date-picker-input.md -->
42641
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-date-picker-input.md -->
42963
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-date-picker-input.md -->
42642
42964
 
42643
42965
  # BbDatePickerInput — v2 → v3
42644
42966
 
@@ -42770,8 +43092,15 @@ friends, rename it:
42770
43092
  | `…__calendar-btn--active` | `.bb-segmented-field__trigger-btn--active` |
42771
43093
  | `.bb-base-date-picker-input__calendar-icon` | `.bb-segmented-field__trigger-icon` |
42772
43094
  | `…__calendar--sheet` | `.bb-base-time-picker-input__panel--sheet` (time input) |
42773
- | `…__calendar`, `…__calendar--shown` | removed — they were styled nowhere |
42774
- | `.bb-base-time-picker-input__clock-btn` | removed — use `.bb-segmented-field__trigger-btn` |
43095
+ | `.bb-base-date-picker-input__inner-wrapper` | **removed** — see below |
43096
+
43097
+ `__inner-wrapper` has no v3 counterpart. It was a bare layout box
43098
+ (`flex: auto; display: block`) between the input chrome and the fields; v3
43099
+ drops the element and `.bb-segmented-field__fields` carries the layout. A rule
43100
+ hanging off it stops matching with nothing to rename it to — delete it, and if
43101
+ it was doing real work, move the declarations onto `__fields`.
43102
+ | `…__calendar`, `…__calendar--shown` | removed — they were styled nowhere |
43103
+ | `.bb-base-time-picker-input__clock-btn` | removed — use `.bb-segmented-field__trigger-btn` |
42775
43104
 
42776
43105
  **The block names are unchanged.** `.bb-base-date-picker-input` and
42777
43106
  `.bb-base-time-picker-input` are still the roots, so a rule that has to reach
@@ -42791,7 +43120,7 @@ move: `__accessible-description` on both, and `__panel`, `__panel--sheet`,
42791
43120
  ---
42792
43121
 
42793
43122
  <!-- dist/ai/guides/migration/components/bb-dialog.md -->
42794
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-dialog.md -->
43123
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-dialog.md -->
42795
43124
 
42796
43125
  # BbDialog — v2 → v3
42797
43126
 
@@ -42800,16 +43129,18 @@ v2's `BbDialog` was a re-export of `BaseDialog`; v3 makes it first-class
42800
43129
 
42801
43130
  ## Changes
42802
43131
 
42803
- | v2 | v3 | Kind |
42804
- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
42805
- | `showClose?: boolean` (default `true`) | `hideClose?: boolean` (default `false`) | rename, polarity inverted |
42806
- | `description` | removed | put it in the default slot / `header` slot |
42807
- | `hideHeader` | removed | omit `title` and the header collapses; or use the `header` slot |
42808
- | `compact` | removed | spacing is token-driven now (`--bb-panel-p`) |
42809
- | `overlayClasses`, `panelClasses` (deprecated in v2) | removed | use normal `class` / CSS on `.bb-dialog*` |
42810
- | `size?: 'sm' \| 'md' \| 'lg'` | `size?: Responsive<'xs'…'2xl' \| CSS length>` | widened — **but defaults changed, see below** |
42811
- | `transitionDuration` default `300` | default `250` | visual |
42812
- | — | `adaptive` (default from config, **on**), `offCanvasProps`, `stack`, `fullscreen`, `persistent`, `disabled`, `focusTarget`, `eager` | additive |
43132
+ | v2 | v3 | Kind |
43133
+ | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
43134
+ | `showClose?: boolean` (default `true`) | `hideClose?: boolean` (default `false`) | rename, polarity inverted |
43135
+ | `description` | removed | put it in the default slot / `header` slot |
43136
+ | `#description` slot | removed | same — and the `aria-describedby` wiring goes with it |
43137
+ | `#close` slot | removed | a custom close control goes inside `#header`, which replaces the default ✕ — no `hide-close` alongside it |
43138
+ | `hideHeader` | removed | omit `title` and the header collapses; or use the `header` slot |
43139
+ | `compact` | removed | spacing is token-driven now (`--bb-panel-p`) |
43140
+ | `overlayClasses`, `panelClasses` (deprecated in v2) | removed | use normal `class` / CSS on `.bb-dialog*` |
43141
+ | `size?: 'sm' \| 'md' \| 'lg'` | `size?: Responsive<'xs'…'2xl' \| CSS length>` | widened — **but defaults changed, see below** |
43142
+ | `transitionDuration` default `300` | default `250` | visual |
43143
+ | — | `adaptive` (default from config, **on**), `offCanvasProps`, `stack`, `fullscreen`, `persistent`, `disabled`, `focusTarget`, `eager` | additive |
42813
43144
 
42814
43145
  ## ⚠ The two silent ones
42815
43146
 
@@ -42835,10 +43166,118 @@ v2's `BbDialog` was a re-export of `BaseDialog`; v3 makes it first-class
42835
43166
  </BbDialog>
42836
43167
  ```
42837
43168
 
43169
+ ## CSS and tokens
43170
+
43171
+ The `--bb-dialog-*` family is gone. Padding comes from the shared
43172
+ `--bb-panel-p`; the rest are unprefixed locals on `.bb-dialog` or plain CSS
43173
+ rules. `BbOffCanvas` is identical throughout with `.bb-offcanvas__*` parts.
43174
+
43175
+ | v2 token | v3 |
43176
+ | -------------------------------------------- | -------------------------------------------------------------------------------------- |
43177
+ | `--bb-dialog-px` / `--bb-dialog-py` (`24px`) | `--bb-panel-p` (`16px`) — **one** value on both axes; asymmetry needs a CSS rule |
43178
+ | `--bb-dialog-close` (`12px`) | **delete it** — v3's default already reproduces v2's control size |
43179
+ | `--bb-dialog-title-size` (`18px`) | local `--dialog-title-fs` on `.bb-dialog`, default `calc(var(--bb-fs) + 2px)` = `16px` |
43180
+ | `--bb-dialog-title-weight` (`600`) | no knob — `500` is hard-coded; override with a CSS rule |
43181
+ | — | local `--dialog-gap`, default `--bb-panel-p` — the body↔footer vertical rhythm |
43182
+
43183
+ ### `--bb-dialog-close` → delete it
43184
+
43185
+ v2's `--bb-dialog-close` was the close **icon** width; the button around it
43186
+ added `padding: 8px`, so the control measured `12 + 16 = 28px`. v3 inverts the
43187
+ relationship: `.bb-close-button { --size }` is the whole control box and the
43188
+ glyph is derived — `max(--size - 2 × --p, 10px)`. Inside a dialog v3 already
43189
+ ships `--size: 28px; --p: 7px`, a 14px glyph in the same 28px control v2's
43190
+ default produced.
43191
+
43192
+ So carrying the old number across shrinks the target: `--size: 12px` gives a
43193
+ 12×12px control, well under the 24px WCAG 2.5.8 minimum. To get the old 12px
43194
+ glyph back, widen the padding — never shrink the box:
43195
+
43196
+ ```css
43197
+ /* 12px glyph, still a 28px control */
43198
+ .my-dialog .bb-dialog__header > .bb-close-button {
43199
+ --p: 8px;
43200
+ }
43201
+ ```
43202
+
43203
+ The library rule is `.bb-dialog .bb-dialog__header > .bb-close-button` (0,3,0),
43204
+ so your selector needs three classes to win. A headerless dialog puts the ✕ at
43205
+ `.bb-dialog__body:first-child .bb-dialog__body-content > .bb-close-button`.
43206
+
43207
+ ### The title
43208
+
43209
+ ```css
43210
+ .my-dialog {
43211
+ --dialog-title-fs: 18px; /* v2 --bb-dialog-title-size; drives line-height too */
43212
+ }
43213
+ .my-dialog .bb-dialog__header .bb-dialog__title {
43214
+ font-weight: 600; /* v2 --bb-dialog-title-weight */
43215
+ }
43216
+ ```
43217
+
43218
+ There is no weight token in v3. `500` is hard-coded on
43219
+ `.bb-dialog .bb-dialog__header .bb-dialog__title` (0,3,0), so match that
43220
+ specificity — a two-class `.my-dialog .bb-dialog__title` loses.
43221
+
43222
+ ### Asymmetric padding
43223
+
43224
+ `--bb-panel-p` is a single value used on both axes; there is no
43225
+ `--bb-panel-px`/`--bb-panel-py` pair. If your v2 theme set `--bb-dialog-px` ≠
43226
+ `--bb-dialog-py`, set `--bb-panel-p` to the **horizontal** value (it also
43227
+ positions the close button) and override the vertical with a `padding-block`
43228
+ rule on the part classes.
43229
+
43230
+ ```css
43231
+ /* v2: --bb-dialog-px: 24px; header --bb-dialog-py: 10px; body --bb-dialog-py: 20px */
43232
+ .my-dialog {
43233
+ --bb-panel-p: 24px; /* horizontal + close-button inset */
43234
+ }
43235
+ .my-dialog .bb-dialog__header {
43236
+ padding-block: 10px 5px; /* the library halves the header's bottom padding */
43237
+ }
43238
+ .my-dialog .bb-dialog__body .bb-dialog__body-content {
43239
+ padding-block: 0 20px;
43240
+ }
43241
+ .my-dialog .bb-dialog__body:first-child .bb-dialog__body-content {
43242
+ padding-top: 20px;
43243
+ }
43244
+ .my-dialog .bb-dialog__footer {
43245
+ padding-block: 20px;
43246
+ }
43247
+ ```
43248
+
43249
+ Use `padding-block`, not the `padding` shorthand, so the horizontal half keeps
43250
+ tracking `--bb-panel-p`. Library selectors are `.bb-dialog .bb-dialog__header`
43251
+ (0,2,0), `.bb-dialog__body-content` (0,1,0),
43252
+ `.bb-dialog__body:first-child .bb-dialog__body-content` (0,2,1) and
43253
+ `.bb-dialog .bb-dialog__footer` (0,2,0) — the three-class app rules above beat
43254
+ all four.
43255
+
43256
+ ### `--dialog-gap`
43257
+
43258
+ If only the vertical rhythm _between parts_ differs, `--dialog-gap` does it
43259
+ without a padding rule — it replaces `--bb-panel-p` in the body's bottom padding
43260
+ and the footer's block padding:
43261
+
43262
+ ```css
43263
+ .my-dialog {
43264
+ --dialog-gap: 8px;
43265
+ }
43266
+ ```
43267
+
43268
+ It does **not** reach the header's top padding, any horizontal padding, or the
43269
+ body's top padding in the headerless case; those still read `--bb-panel-p`.
43270
+
43271
+ ### `.bb-confirm`
43272
+
43273
+ `confirm()` renders through the same `.bb-dialog__*` parts, so everything above
43274
+ reaches it. Its own button hooks changed — see
43275
+ [use-confirm.md](./use-confirm.md).
43276
+
42838
43277
  ---
42839
43278
 
42840
43279
  <!-- dist/ai/guides/migration/components/bb-dropdown-button.md -->
42841
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-dropdown-button.md -->
43280
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-dropdown-button.md -->
42842
43281
 
42843
43282
  # BbDropdownButton — v2 → v3
42844
43283
 
@@ -42870,7 +43309,7 @@ v2's `BbDialog` was a re-export of `BaseDialog`; v3 makes it first-class
42870
43309
  ---
42871
43310
 
42872
43311
  <!-- dist/ai/guides/migration/components/bb-dropdown.md -->
42873
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-dropdown.md -->
43312
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-dropdown.md -->
42874
43313
 
42875
43314
  # BbDropdown / `v-bb-dropdown` — v2 → v3
42876
43315
 
@@ -42907,7 +43346,7 @@ custom names register via the plugin's `dropdownItemVariants` option
42907
43346
  ---
42908
43347
 
42909
43348
  <!-- dist/ai/guides/migration/components/bb-dropzone.md -->
42910
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-dropzone.md -->
43349
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-dropzone.md -->
42911
43350
 
42912
43351
  # BbDropzone — v2 → v3
42913
43352
 
@@ -42935,7 +43374,7 @@ renders no hidden input at all.
42935
43374
  ---
42936
43375
 
42937
43376
  <!-- dist/ai/guides/migration/components/bb-icon.md -->
42938
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-icon.md -->
43377
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-icon.md -->
42939
43378
 
42940
43379
  # BbIcon — v2 → v3
42941
43380
 
@@ -42960,6 +43399,48 @@ Provider prefixes now require the matching `@iconify-json/<prefix>` dev
42960
43399
  dependency, and names must be **literal strings** (the build scans statically;
42961
43400
  dynamically assembled names won't be bundled).
42962
43401
 
43402
+ ## Your own icon plugin is probably redundant now
43403
+
43404
+ Most v2 apps hand-rolled one, because v2 shipped no registry:
43405
+
43406
+ ```ts
43407
+ // resources/js/plugins/icons.ts — the v2 shape: glob the SVG folder yourself,
43408
+ // build a name → loader map, hand it to BbIcon under the string key.
43409
+ const icons = import.meta.glob('../../assets/icons/*.svg', { as: 'raw' });
43410
+ app.provide('icons', toRegistry(icons));
43411
+ ```
43412
+
43413
+ v3 does both halves itself. The **build** plugin scans `iconDir` recursively
43414
+ and turns every `.svg` into `local:<basename>`; the **runtime** plugin provides
43415
+ that registry to `BbIcon`. If your plugin scanned the same folder and provided
43416
+ it under `'icons'`, it is now duplicating both.
43417
+
43418
+ ⚠ It is not merely duplicate — it is **inert for `BbIcon`, silently**.
43419
+ `bitbossUiPlugin` provides the registry under a Symbol
43420
+ (`bitboss-ui:icons`) _and_ under the legacy string key `'icons'`, and `BbIcon`
43421
+ reads `inject(Symbol.for('bitboss-ui:icons')) ?? inject('icons')`. The Symbol wins,
43422
+ so your map is shadowed rather than merged. Any glyph that exists only in your
43423
+ map stops rendering and nothing warns.
43424
+
43425
+ Check before you delete — the plugin is dead only if both hold:
43426
+
43427
+ 1. **Every SVG it registers also lives under `iconDir`.** Diff the two folders.
43428
+ If the plugin globbed a different directory (`resources/js/icons`,
43429
+ a package's `assets/`), move those files into `iconDir` first — otherwise
43430
+ deletion and non-deletion both leave you with missing icons.
43431
+ 2. **Nothing else injects `'icons'`.** `rg -n "inject\(['\"]icons['\"]\)"` — an
43432
+ app component reading the map to render `<img>` or to enumerate names is
43433
+ real usage, not `BbIcon` usage. It now receives whichever provide ran last
43434
+ (yours, if you `app.use()` your plugin after `bitbossUiPlugin`), which is a
43435
+ coin-flip you do not want to keep. Rewrite those call sites to import your
43436
+ map as a plain module export — or, if what they want is the library's
43437
+ registry, `inject(Symbol.for('bitboss-ui:icons'))`. The key is not exported
43438
+ from the package, but it is a `Symbol.for`, so recreating it resolves the
43439
+ same symbol.
43440
+
43441
+ The string key `'icons'` is a compatibility shim kept for one release. Do not
43442
+ build on it.
43443
+
42963
43444
  ## Prefix your own icons with `local:` while you are in there
42964
43445
 
42965
43446
  A v2 `type` naming an icon from your own `iconDir` was bare — `type="user-circle"`.
@@ -42989,7 +43470,7 @@ The same goes for any component prop that takes an icon (`prepend:icon`,
42989
43470
  ---
42990
43471
 
42991
43472
  <!-- dist/ai/guides/migration/components/bb-intersection.md -->
42992
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-intersection.md -->
43473
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-intersection.md -->
42993
43474
 
42994
43475
  # BbIntersection — v2 → v3
42995
43476
 
@@ -43060,7 +43541,7 @@ depended on the stricter behaviour, pass `{ threshold: 1 }` and check
43060
43541
  ---
43061
43542
 
43062
43543
  <!-- dist/ai/guides/migration/components/bb-offcanvas.md -->
43063
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-offcanvas.md -->
43544
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-offcanvas.md -->
43064
43545
 
43065
43546
  # BbOffCanvas — v2 → v3
43066
43547
 
@@ -43070,6 +43551,9 @@ depended on the stricter behaviour, pass `{ threshold: 1 }` and check
43070
43551
  | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43071
43552
  | `direction?: 'left' \| 'top' \| 'right' \| 'bottom'` (default `'left'`) | `side?: 'left' \| 'top' \| 'right' \| 'bottom'` (default `'left'`) | **⚠ silent** — a leftover `direction` is ignored and the panel falls back to `'left'` |
43072
43553
  | `showClose?: boolean` (default `true`) | `hideClose?: boolean` (default `false`) | rename, polarity inverted |
43554
+ | `description?: string` | removed | put the copy in the default slot or own the header via `#header`; the `aria-describedby` wiring goes with it |
43555
+ | `#description` slot | removed | same — nothing links body copy to the panel automatically now |
43556
+ | `#close` slot | removed | a custom close control goes inside `#header`, see below |
43073
43557
  | `size?: 'sm' \| 'md' \| 'lg'` | `size?: Responsive<'xs'…'2xl' \| 'auto' \| CSS length>` | widened — **defaults changed**, same trap as BbDialog: v2 `{ sm: 384, md: 652, lg: 896 }` → v3 `{ xs: 320, sm: 384, md: 448, lg: 512, xl: 576, '2xl': 672 }`. Pin via `offCanvasDefaultSizes` if needed. |
43074
43558
  | — | `draggable`, `stack` / `stackGap`, `persistent`, `disabled`, `fullscreen`, `eager`, `focusTarget`, `title` | additive |
43075
43559
 
@@ -43121,10 +43605,41 @@ Pure rename — the `Partial<BbOffCanvasProps>` object is unchanged.
43121
43605
 
43122
43606
  Also silent on upgrade; `eslint --fix` auto-renames it.
43123
43607
 
43608
+ ## `description` and `#close` are gone
43609
+
43610
+ v3's slot set is `header | title | default | footer` — no `#close`, no
43611
+ `#description`, and no `description` prop. Same removal as
43612
+ [BbDialog](./bb-dialog.md).
43613
+
43614
+ `description` copy moves into the default slot. Nothing wires
43615
+ `aria-describedby` for you any more; add it yourself if a screen reader needs
43616
+ the panel described rather than just labelled.
43617
+
43618
+ **`#header` replaces the ✕, so `hide-close` is redundant beside it.** The
43619
+ default header renders `#title` + a `CloseButton`; providing `#header` replaces
43620
+ both, which is why a custom close control belongs inside it. The slot receives
43621
+ `{ titleId, close, title }` — put `titleId` on your title element to keep
43622
+ `aria-labelledby` intact, and call `close` from your own control.
43623
+
43624
+ ```diff
43625
+ - <BbOffCanvas v-model="open" title="Edit" :show-close="true">
43626
+ - <template #close><SaveGuardIcon /></template>
43627
+ - </BbOffCanvas>
43628
+ + <BbOffCanvas v-model="open" title="Edit">
43629
+ + <template #header="{ titleId, close, title }">
43630
+ + <span :id="titleId" class="bb-offcanvas__title">{{ title }}</span>
43631
+ + <button @click="guardUnsaved(close)"><SaveGuardIcon /></button>
43632
+ + </template>
43633
+ + </BbOffCanvas>
43634
+ ```
43635
+
43636
+ A header renders when `title` is set **or** `#header` is provided, so the slot
43637
+ alone is enough — you do not also need a `title`.
43638
+
43124
43639
  ---
43125
43640
 
43126
43641
  <!-- dist/ai/guides/migration/components/bb-pagination.md -->
43127
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-pagination.md -->
43642
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-pagination.md -->
43128
43643
 
43129
43644
  # BbPagination — v2 → v3
43130
43645
 
@@ -43163,7 +43678,7 @@ The `total-items` / `total-pages` **props** are unchanged.
43163
43678
  ---
43164
43679
 
43165
43680
  <!-- dist/ai/guides/migration/components/bb-popover.md -->
43166
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-popover.md -->
43681
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-popover.md -->
43167
43682
 
43168
43683
  # BbPopover — v2 → v3
43169
43684
 
@@ -43216,7 +43731,7 @@ opt out with `:adaptive="false"` if the floating panel must persist.
43216
43731
  ---
43217
43732
 
43218
43733
  <!-- dist/ai/guides/migration/components/bb-rating.md -->
43219
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-rating.md -->
43734
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-rating.md -->
43220
43735
 
43221
43736
  # BbRating — v2 → v3
43222
43737
 
@@ -43238,10 +43753,61 @@ opt out with `:adaptive="false"` if the floating panel must persist.
43238
43753
  Adding `clearable` is recommended: the old "re-click to clear" folklore is
43239
43754
  now a visible affordance.
43240
43755
 
43756
+ `legend` is required here too, in v2 as in v3 — see the note in
43757
+ [bb-checkbox-group.md](./bb-checkbox-group.md); a missing one is a pre-existing
43758
+ bug, not an upgrade break.
43759
+
43760
+ ## DOM
43761
+
43762
+ The block name is unchanged; the star's state modifier is not.
43763
+
43764
+ | v2 | v3 |
43765
+ | -------------------------------------- | ------------------------------------------------------- |
43766
+ | `.bb-base-rating__option--highlighted` | `.bb-base-rating__option--filled` / `--empty` |
43767
+ | — | `.bb-base-rating__option--disabled`, `--readonly` (new) |
43768
+ | — | `.bb-base-rating--warnings`, `--forced-clear` (new) |
43769
+
43770
+ v2 styled unfilled stars with `:not(--highlighted)`; v3 gives them a positive
43771
+ name. Everything else survives unchanged: `.bb-base-rating`, `--disabled`,
43772
+ `--errors`, `--readonly`, `--has-value`, `__inner-container`, `__option`,
43773
+ `__label-text`. Nothing warns — a `--highlighted` rule just stops matching.
43774
+
43775
+ `BbRating` is not part of the checkbox/radio/switch `bb-cr-container` tree, and
43776
+ was not in v2 either despite v2 shipping rules that said otherwise (below).
43777
+ Target `.bb-base-rating__inner-container` and `.bb-base-rating__option`.
43778
+
43779
+ ## `input-position="right"` / `"center"` now does something
43780
+
43781
+ v2 shipped the alignment rules under `.bb-rating … .bb-cr-container__container`
43782
+ — a class `BbRating` never rendered (its container was already
43783
+ `.bb-base-rating__inner-container`). So the prop was accepted and typed on both
43784
+ versions but was a **no-op in v2**. v3 repoints the rules at the element the
43785
+ component actually renders.
43786
+
43787
+ If your app worked around the dead prop with a hand-rolled alignment rule, that
43788
+ rule now fights the library's. Delete the workaround and keep the prop.
43789
+
43790
+ ## Tokens
43791
+
43792
+ `--bb-rating-size` was a `:root` global in v2, read as
43793
+ `.bb-base-rating { --size: var(--bb-rating-size) }`. v3 drops the global and
43794
+ hard-defaults the local:
43795
+
43796
+ ```diff
43797
+ - :root { --bb-rating-size: 32px; }
43798
+ + .bb-base-rating { --size: 32px; }
43799
+ ```
43800
+
43801
+ Set it on `.bb-base-rating` itself (or on a rule that also matches it) — the
43802
+ local is declared on that block, so a value inherited from an ancestor is
43803
+ overridden before it is read. `--spacing` (star gap) is the companion local;
43804
+ v2's `--bb-base-rating-spacing` was referenced but never declared anywhere in
43805
+ the shipped sheet, so it never worked.
43806
+
43241
43807
  ---
43242
43808
 
43243
43809
  <!-- dist/ai/guides/migration/components/bb-select-popover.md -->
43244
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-select-popover.md -->
43810
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-select-popover.md -->
43245
43811
 
43246
43812
  # BbSelectPopover — v2 → v3
43247
43813
 
@@ -43303,7 +43869,7 @@ See `bb-select.md` § "DOM: listbox option classes lose their v2 `autocomplete-`
43303
43869
  ---
43304
43870
 
43305
43871
  <!-- dist/ai/guides/migration/components/bb-select.md -->
43306
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-select.md -->
43872
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-select.md -->
43307
43873
 
43308
43874
  # BbSelect — v2 → v3
43309
43875
 
@@ -43396,10 +43962,58 @@ Applies to every surface built on the select engine — `BbSelect`,
43396
43962
  `autocomplete-option` in your CSS and test selectors**; nothing warns, the rules
43397
43963
  simply stop matching.
43398
43964
 
43965
+ ## Tokens: `--bb-select-option-px` / `--bb-select-option-py` are gone
43966
+
43967
+ v2 declared both on `:root` (`16px` / `8px`) and read them in the option row, the
43968
+ group header and the no-results row. v3 declares neither, and — unlike most of
43969
+ the retired component globals — **nothing replaced them with a local**.
43970
+ `.bb-listbox__option` hard-codes its inline padding:
43971
+
43972
+ ```css
43973
+ /* src/components/BbSelectPopover/index.css */
43974
+ .bb-listbox__option {
43975
+ height: var(--option-h);
43976
+ padding-left: 0.5rem;
43977
+ padding-right: 0.5rem;
43978
+ }
43979
+ ```
43980
+
43981
+ So `--bb-select-option-px`, and the `--px` local you might expect by analogy with
43982
+ `BbButton` or `BbDialog`, are both silent no-ops — no rule in the select tree
43983
+ reads either. Override with a plain declaration instead. The library's rule is
43984
+ six classes deep, so a two-class app rule loses the cascade; match the chain or
43985
+ force it:
43986
+
43987
+ ```css
43988
+ /* specificity-matched — needs your sheet to load after the library's */
43989
+ .bb-select-popover
43990
+ .bb-listbox
43991
+ .bb-listbox__outer-container
43992
+ .bb-listbox__inner-container
43993
+ span[role='listbox']
43994
+ .bb-listbox__option {
43995
+ padding-inline: 1rem;
43996
+ }
43997
+
43998
+ /* or, order-independent */
43999
+ .bb-listbox__option {
44000
+ padding-inline: 1rem !important;
44001
+ }
44002
+ ```
44003
+
44004
+ Row **height** is not yours to set in CSS. `--option-h` reads `--option-height`,
44005
+ which the component writes inline because the same number feeds the
44006
+ virtualizer's `estimateSize`; a CSS override desynchronises the paint from the
44007
+ scroll math and the list mispositions. Use `item-height` / `header-height` (or
44008
+ `compact`) instead — they move both halves together.
44009
+
44010
+ The panel is teleported, so a wrapper class on your `<BbSelect>` does not reach
44011
+ it — scope on `.bb-select-popover` or on the surrounding page/theme class.
44012
+
43399
44013
  ---
43400
44014
 
43401
44015
  <!-- dist/ai/guides/migration/components/bb-slider.md -->
43402
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-slider.md -->
44016
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-slider.md -->
43403
44017
 
43404
44018
  # BbSlider — v2 → v3
43405
44019
 
@@ -43425,7 +44039,7 @@ silently degrade to `'outside'` — expected, not a bug.
43425
44039
  ---
43426
44040
 
43427
44041
  <!-- dist/ai/guides/migration/components/bb-smooth-height.md -->
43428
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-smooth-height.md -->
44042
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-smooth-height.md -->
43429
44043
 
43430
44044
  # BbSmoothHeight — v2 → v3
43431
44045
 
@@ -43471,7 +44085,7 @@ matches when `tag="span"`.
43471
44085
  ---
43472
44086
 
43473
44087
  <!-- dist/ai/guides/migration/components/bb-spinner.md -->
43474
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-spinner.md -->
44088
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-spinner.md -->
43475
44089
 
43476
44090
  # BbSpinner — v2 → v3
43477
44091
 
@@ -43513,21 +44127,103 @@ have made the spinner invisible and removed the page's only busy signal.
43513
44127
  ---
43514
44128
 
43515
44129
  <!-- dist/ai/guides/migration/components/bb-table.md -->
43516
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-table.md -->
44130
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-table.md -->
43517
44131
 
43518
44132
  # BbTable — v2 → v3
43519
44133
 
43520
44134
  ## Changes
43521
44135
 
43522
- | v2 | v3 | Kind |
43523
- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
43524
- | `allowSelectAll?: boolean` (default `true`) | `disableSelectAll?: boolean` (default `false`) | rename, polarity inverted |
43525
- | refetch with rows → skeleton | refetch keeps rows visible (dimmed + progress bar) | **behavior** — the skeleton only shows when there is nothing to display |
43526
- | rows interactive while loading | header + rows go `inert` while loading | **behavior** — opt out with `interactive-while-loading` |
43527
- | `#loading` slot fired on every load | fires on the **first load only** (it replaces the skeleton, so it follows the skeleton's rule) | **⚠ silent** for anyone styling refetches through it |
43528
- | `#no-data` replaced the empty-state `<tr>` | fills the table's own full-width `<td>` (colspan counted, centred) | **behavior** — pass content, not a row |
43529
- | — | `enforceCoherence` (prunes row-keyed models after each load; incompatible with pagination-via-`dependencies`), `expandedItems` + `#expand`, `sort`, `keyboardNavigation`, `highlighted`, `rowClass` | additive |
43530
- | `useBbTableContext(id).total` _(v3 alphas ≤ 4)_ | `useBbTableContext(id).totalItems` | **hard rename** on the composable handle — see below |
44136
+ | v2 | v3 | Kind |
44137
+ | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
44138
+ | `allowSelectAll?: boolean` (default `true`) | `disableSelectAll?: boolean` (default `false`) | rename, polarity inverted |
44139
+ | refetch with rows → skeleton | refetch keeps rows visible (dimmed + progress bar) | **behavior** — the skeleton only shows when there is nothing to display |
44140
+ | rows interactive while loading | header + rows go `inert` while loading | **behavior** — opt out with `interactive-while-loading` |
44141
+ | `#loading` slot fired on every load | fires on the **first load only** (it replaces the skeleton, so it follows the skeleton's rule) | **⚠ silent** for anyone styling refetches through it |
44142
+ | `#no-data` replaced the empty-state `<tr>` | fills the table's own full-width `<td>` (colspan counted, centred) | **behavior** — pass content, not a row |
44143
+ | — | props `enforceCoherence` (prunes row-keyed models after each load; incompatible with pagination-via-`dependencies`), `expandedItems` + `#expand`, `sort`, `keyboardNavigation`, `highlighted`, `rowClass` | additive |
44144
+ | — | **column** fields `sortable?: boolean`, `rowClass?: ColumnClasses` | additive, **collides** — see below |
44145
+ | `useBbTableContext(id).total` _(v3 alphas ≤ 4)_ | `useBbTableContext(id).totalItems` | **hard rename** on the composable handle — see below |
44146
+ | `--bb-table-*` custom properties | same names without the `bb-` prefix | **⚠ silent** for nested-table layouts — see below |
44147
+
44148
+ ## Columns gained `sortable` and `rowClass` — check yours first
44149
+
44150
+ v2's `BbTableColumn` had neither field (`grep sortable` over the whole 2.1.135
44151
+ `BbTable/types.d.ts` exits empty), and extra fields on a column object were
44152
+ inert — so apps parked their own data there. A backend sort key named
44153
+ `sortable`. A per-column class named `rowClass`. Both are now real library
44154
+ fields, and both break in two directions at once: the **type** stops compiling
44155
+ and the **behaviour** silently changes.
44156
+
44157
+ ### The type collision
44158
+
44159
+ Your column type is almost certainly an intersection:
44160
+
44161
+ ```ts
44162
+ type Column = BbTableColumn<Patient> & { sortable?: string }; // backend sort key
44163
+ ```
44164
+
44165
+ The intersection makes `sortable` `boolean & string` — i.e. `never`, whose
44166
+ optional form TypeScript reports as `undefined`. Every column literal in your
44167
+ app fails, and the message never mentions `BbTableColumn`:
44168
+
44169
+ ```
44170
+ error TS2322: Type 'string' is not assignable to type 'undefined'.
44171
+ ```
44172
+
44173
+ If your field is **required** (`{ sortable: string }`, no `?`) the whole object
44174
+ type collapses to `never` and you get one error per property per literal:
44175
+
44176
+ ```
44177
+ error TS2322: Type '"id"' is not assignable to type 'never'.
44178
+ error TS2322: Type 'string' is not assignable to type 'never'.
44179
+ ```
44180
+
44181
+ That fan-out is why a single shared column type produced 166 errors in one real
44182
+ migration. Fix it at the type, not at the call sites:
44183
+
44184
+ ```diff
44185
+ - type Column = BbTableColumn<Patient> & { sortable?: string };
44186
+ + type Column = BbTableColumn<Patient> & { sortKey?: string };
44187
+
44188
+ // or keep the name and take the field off the library type
44189
+ + type Column = Omit<BbTableColumn<Patient>, 'sortable'> & { sortable?: string };
44190
+ ```
44191
+
44192
+ `Omit` compiles, but read the next section before choosing it.
44193
+
44194
+ ### The behaviour half — a truthy `sortable` turns on the built-in sort UI
44195
+
44196
+ `BbTable` coerces the field (`sortable: !!column.sortable` in `BbTable.vue`), so
44197
+ **any** truthy value counts — including a sort-key string like
44198
+ `'patients.created_at'`. The header stops rendering a plain label and renders a
44199
+ `.bb-table-sort` button instead, and the `<th>` gains `aria-sort="none"`. An app
44200
+ that already ships its own sortable-header UI now draws two.
44201
+
44202
+ The damage is bounded: `sort` is a controlled model (`v-model:sort`), so the
44203
+ extra button emits and does nothing else — it cannot reorder your rows behind
44204
+ your back. But it is visible, focusable, and announced.
44205
+
44206
+ So `Omit` silences the compiler and keeps the bug. Rename the field, or strip
44207
+ it at the `BbTable` boundary:
44208
+
44209
+ ```ts
44210
+ const tableColumns = computed(() =>
44211
+ columns.value.map(({ sortable, ...rest }) => rest)
44212
+ );
44213
+ ```
44214
+
44215
+ Column `rowClass` is the same shape of trap without the type error, because
44216
+ `ColumnClasses` already accepts `string`: a leftover `rowClass: 'w-40'` you
44217
+ meant for the column typechecks fine and is now merged onto the whole row's
44218
+ `<tr>`, alongside the table-level `rowClass` prop. A non-`Classes` shape
44219
+ (`rowClass?: { colspan: number }`) does error, with the same `undefined`
44220
+ wording.
44221
+
44222
+ Grep both before you start:
44223
+
44224
+ ```sh
44225
+ rg -n 'sortable|rowClass' --type ts --type vue # keep the hits on column objects
44226
+ ```
43531
44227
 
43532
44228
  ## Edits
43533
44229
 
@@ -43614,10 +44310,74 @@ unrelated and does not change.
43614
44310
  `BbPagination` consumers see the same rename through `table-id`; see
43615
44311
  [BbPagination](./bb-pagination.md).
43616
44312
 
44313
+ ## The custom properties the table _publishes_ lost the `bb-` prefix
44314
+
44315
+ These are not theming inputs — they are values `BbTable` measures at runtime and
44316
+ writes onto its own DOM, for the table's CSS and for descendants to read. You
44317
+ reach for them for exactly one reason: aligning a nested table's columns to its
44318
+ parent's. They all dropped the prefix.
44319
+
44320
+ | v2 | v3 |
44321
+ | ------------------------------------------- | ------------------------------------------- |
44322
+ | `--bb-table-{id}-track-{key}` | `--table-{id}-track-{key}` |
44323
+ | `--bb-table-offset-start` / `-end` | `--offset-start` / `--offset-end` |
44324
+ | `--bb-table-offset-internal-start` / `-end` | `--offset-internal-start` / `-internal-end` |
44325
+ | `--bb-table-offset-external-start` / `-end` | `--offset-external-start` / `-external-end` |
44326
+ | `--bb-table-fill` | `--fill` |
44327
+ | `--bb-table-natural-width` | `--natural-width` |
44328
+ | `--bb-table-cell-h` | `--cell-h` |
44329
+
44330
+ Only the track bridge keeps a `table-` segment, because it crosses component
44331
+ boundaries; the rest are plain locals on `.bb-table`. `--padding-x`,
44332
+ `--padding-y` and `--actions-spacing` were already unprefixed in v2 and keep
44333
+ their names (their _values_ tightened: `16px`/`8px` → `12px`/`6px`, and the cell
44334
+ height `42px` → `36px`).
44335
+
44336
+ **Every one of these fails silently.** They are read as
44337
+ `var(--offset-internal-start)` with a fallback, so a v2 override lands on a name
44338
+ nothing consumes and the layout quietly falls back to its default. The worst
44339
+ case is code that _scans_ for them — a nested-table helper matching
44340
+ `/--bb-table-[\w-]+-track/` over `element.style` matches nothing at all, no
44341
+ error, just columns that no longer line up:
44342
+
44343
+ ```diff
44344
+ - const track = /--bb-table-[\w-]+-track/;
44345
+ + const track = /--table-[\w-]+-track/;
44346
+ ```
44347
+
44348
+ Grep your app CSS and any style-scanning JS for `--bb-table-` — no name with
44349
+ that prefix exists in v3.
44350
+
44351
+ The table is not the only component this happened to. Four more published
44352
+ variables lost the prefix the same way: `--bb-select-popover-width` → `--w` on
44353
+ `.bb-select-popover` (and the supported path is now the `width` prop, not a CSS
44354
+ override), `--bb-icon-dimensions-w` → `--w` on `.bb-icon`,
44355
+ `--bb-breadcrumbs-flex-basis` / `-flex-grow` → `--flex-basis` / `--flex-grow`,
44356
+ and `--bb-picker-preview-color` → `--picker-preview-color`. Widen the grep
44357
+ accordingly: `--bb-select-popover-`, `--bb-icon-dimensions-`, `--bb-breadcrumbs-`,
44358
+ `--bb-picker-`. See
44359
+ [design-tokens.md](../../design-tokens.md#migration-from-the-old-tokens) for the
44360
+ theming-token side of the same rename.
44361
+
44362
+ ## `caption` names the selection fieldset when `legend` is absent
44363
+
44364
+ A `selectable` table wraps itself in a `<fieldset>` and needs an accessible
44365
+ name. v3 resolves it as `legend`, then `caption`, then a generic localized
44366
+ string — so a table that already has a `caption` is correctly named and does not
44367
+ need a `legend`.
44368
+
44369
+ `npx bitboss-ui check` (and the ESLint rule behind it) still asks for `legend`
44370
+ specifically, so `<BbTable selectable caption="Patients" />` fails the check
44371
+ even though its fieldset is named. That one is a **quality nudge, not a broken
44372
+ affordance** — unlike the other partner-prop pairs, ignoring it does not leave
44373
+ anything silently doing nothing. Pass a `legend` when it says something the
44374
+ caption does not ("select invoices to export" vs "Invoices"); otherwise suppress
44375
+ the rule at the call site rather than duplicating the caption into a `<legend>`.
44376
+
43617
44377
  ---
43618
44378
 
43619
44379
  <!-- dist/ai/guides/migration/components/bb-tabs.md -->
43620
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-tabs.md -->
44380
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-tabs.md -->
43621
44381
 
43622
44382
  # BbTabs — v2 → v3
43623
44383
 
@@ -43635,6 +44395,8 @@ aliases survive as deprecated re-exports, the tag name does not).
43635
44395
  | pane slot `#<key>` (raw) | `#<slotKey(key)>` | normalization |
43636
44396
  | URL value = raw key | URL value = normalized slug | **⚠ breaking for deep links** |
43637
44397
  | tab/panel DOM ids from raw key | normalized (also fixes invalid ids for spaced keys) | check E2E selectors |
44398
+ | `.bb-tab*` CSS | `.bb-tabs*` / `.bb-tabs-list*` / `.bb-tabs-panes` | **⚠ silent** — the whole BEM tree renamed, see below |
44399
+ | — | `block` (full-width, equal-width triggers) | additive — replaces hand-rolled full-width classes |
43638
44400
 
43639
44401
  `slotKey(key)` lowercases and replaces every non-word run with `_`:
43640
44402
  `"In Review"` → `in_review`, `"2024-03-15"` → `2024_03_15`. Keys that are
@@ -43662,10 +44424,86 @@ the normalized slug (`?section=in_review`, not `?section=In%20Review`).
43662
44424
  Existing bookmarks with raw values fall back to the default tab — add a
43663
44425
  redirect if those links matter.
43664
44426
 
44427
+ ## CSS: every class renamed with the component
44428
+
44429
+ The component rename took the whole BEM tree with it. v2 kept everything under
44430
+ one block, `.bb-tab`; v3 splits it into `.bb-tabs` (root) plus two blocks of
44431
+ their own, `.bb-tabs-list` (the strip) and `.bb-tabs-panes` (the pane wrapper).
44432
+ **No `.bb-tab*` selector survives**, and nothing warns — your v2 tab CSS just
44433
+ stops matching and the shipped styling shows through.
44434
+
44435
+ | v2 | v3 |
44436
+ | ----------------------------------------- | ----------------------------------------------------------------- |
44437
+ | `.bb-tab` | `.bb-tabs` |
44438
+ | `.bb-tab--horizontal` | `.bb-tabs--horizontal` (+ `.bb-tabs-list--horizontal`) |
44439
+ | `.bb-tab--vertical` | `.bb-tabs--vertical` (+ `.bb-tabs-list--vertical`) |
44440
+ | `.bb-tab--disabled` | `.bb-tabs--disabled` (+ `.bb-tabs-list--disabled`) |
44441
+ | `.bb-tab__label-boundary` | `.bb-tabs-list` |
44442
+ | `.bb-tab__label-container` | `.bb-tabs-list__tablist` |
44443
+ | `.bb-tab__label-container--no-transition` | `.bb-tabs-list__tablist--no-transition` |
44444
+ | `.bb-tab__btn` | `.bb-tabs__trigger` |
44445
+ | `.bb-tab__btn--active` | `.bb-tabs__trigger--active` |
44446
+ | `.bb-tab__label` | `.bb-tabs__trigger-label`, wrapped in `.bb-tabs__trigger-content` |
44447
+ | `.bb-tab__panes-container` | `.bb-tabs-panes` |
44448
+ | `.bb-tab__pane` | `.bb-tabs__pane` |
44449
+ | `.bb-tab__pane--shown` | `.bb-tabs__pane--shown` |
44450
+
44451
+ New in v3 with no v2 counterpart: `.bb-tabs--block` / `.bb-tabs-list--block`,
44452
+ `.bb-tabs--compact` / `.bb-tabs-list--compact`, `.bb-tabs-list__prepend` and
44453
+ `__append`, `.bb-tabs-list__tablist--measuring`.
44454
+
44455
+ Four things to know while porting rules:
44456
+
44457
+ - **The nesting did not change.** `.bb-tab__label-container` and
44458
+ `.bb-tabs-list__tablist` are the same `<ul>`. The sliding active pill is
44459
+ still that element's `::before`, still positioned from `--left` / `--top` /
44460
+ `--width` / `--height` set inline on it. A v2 pill override moves by name
44461
+ only, not up or down a level.
44462
+ - **`.bb-tabs--horizontal` is rendered.** The direction modifier is applied
44463
+ unconditionally (`bb-tabs--${direction}`, default `'horizontal'`), so a
44464
+ renamed v2 rule keeps working. The library ships no _rule_ for the horizontal
44465
+ modifier — only `--vertical` is styled — but that does not affect your
44466
+ selectors. Do **not** rewrite these as `:not(.bb-tabs--vertical)`.
44467
+ - **The `<ul>` is no longer an only child.** `.bb-tabs-list` is a grid holding
44468
+ `__prepend`, the tablist, and `__append`. A v2 rule written as
44469
+ `.bb-tab__label-boundary > *` now over-matches; target
44470
+ `.bb-tabs-list__tablist` by name.
44471
+ - **Block naming is not uniform** between the two new blocks: the pane wrapper
44472
+ is `.bb-tabs-panes`, but the pane inside it is an element of the root,
44473
+ `.bb-tabs__pane`. There is no `.bb-tabs-panes__pane`.
44474
+
44475
+ Grep for survivors — this catches every v2 name and no v3 one:
44476
+
44477
+ ```sh
44478
+ rg -n 'bb-tab(__|--)|\.bb-tab\b'
44479
+ ```
44480
+
44481
+ ## `block` replaces hand-rolled full-width tabs
44482
+
44483
+ v2 shipped no full-width tab list, so apps minted their own class — commonly a
44484
+ `bb-tab--full-width` / `bb-tabs--full-width` rule stretching the `<ul>` and
44485
+ setting `flex: 1` on each button. v3 has the prop:
44486
+
44487
+ ```diff
44488
+ - <BbTab :items="items" class="bb-tabs--full-width">
44489
+ + <BbTabs :items="items" block>
44490
+ ```
44491
+
44492
+ `block` sets `.bb-tabs--block` and `.bb-tabs-list--block`: the strip stretches
44493
+ to 100% and every trigger takes an equal share of the free space, floored at
44494
+ the widest trigger's measured width (published as `--eq` on the tablist) so the
44495
+ triggers stay equal even when the strip overflows and scrolls. Horizontal only
44496
+ — the vertical strip skips the measurement.
44497
+
44498
+ Delete the hand-rolled rule when you switch. Left in place it fights `--block`
44499
+ over the same `flex` and `min-width` declarations, and because your class name
44500
+ is now unrecognised (`bb-tabs--full-width` collides with nothing the library
44501
+ ships) neither the checker nor ESLint will tell you which one won.
44502
+
43665
44503
  ---
43666
44504
 
43667
44505
  <!-- dist/ai/guides/migration/components/bb-tag.md -->
43668
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-tag.md -->
44506
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-tag.md -->
43669
44507
 
43670
44508
  # BbTag — v2 → v3
43671
44509
 
@@ -43695,7 +44533,7 @@ state and bind a mapped `string[]`, syncing on `update:modelValue`.
43695
44533
  ---
43696
44534
 
43697
44535
  <!-- dist/ai/guides/migration/components/bb-text-input.md -->
43698
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-text-input.md -->
44536
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-text-input.md -->
43699
44537
 
43700
44538
  # BbTextInput — v2 → v3
43701
44539
 
@@ -43730,6 +44568,28 @@ Also relevant here: the shared input wrapper's CSS block was renamed — any CSS
43730
44568
  or test selectors on `.bb-common-input-inner-container*` must move to
43731
44569
  `.common-input-wrapper--*` (see [main guide §5](../v2-to-v3.md)).
43732
44570
 
44571
+ ## `floating` is not a prop — it is a value of `label-mode`
44572
+
44573
+ ```diff
44574
+ - <BbTextInput v-model="email" label="Email" floating />
44575
+ + <BbTextInput v-model="email" label="Email" label-mode="floating" />
44576
+ ```
44577
+
44578
+ `labelMode?: 'outside' | 'floating' | 'inside'` — same in v2 as in v3. A bare
44579
+ `floating` was never a `BbTextInput` prop on either version; it falls through
44580
+ `$attrs` onto the root element and does nothing, silently. Applies to the whole
44581
+ input family, which shares `labelMode`.
44582
+
44583
+ `bitboss-ui check` flags it, but points the wrong way:
44584
+
44585
+ > prop `floating` is not in BbTextInput's API — `floating` belongs to
44586
+ > BbDatePicker, BbDatePickerInput, not BbTextInput
44587
+
44588
+ That is true and irrelevant. Those two do declare a real `floating` boolean, and
44589
+ it means something else entirely there (emit plain calendar strings instead of
44590
+ zoned ISO instants). The answer is `label-mode="floating"` on the component you
44591
+ already have.
44592
+
43733
44593
  ## DOM: `--hidden-label` layout class removed (2026-08-07)
43734
44594
 
43735
44595
  `BbBaseInputContainer` — the wrapper behind every labelled control in the
@@ -43742,7 +44602,7 @@ target `--reverse`.
43742
44602
  ---
43743
44603
 
43744
44604
  <!-- dist/ai/guides/migration/components/bb-toast.md -->
43745
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-toast.md -->
44605
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-toast.md -->
43746
44606
 
43747
44607
  # BbToast / `toast()` — v2 → v3
43748
44608
 
@@ -43763,24 +44623,58 @@ plugin's `toastPosition` config (default `bottom-right`).
43763
44623
 
43764
44624
  ## `toast()` options
43765
44625
 
43766
- | v2 | v3 | Kind |
43767
- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
43768
- | `theme?: string` | `variant?: ToastVariantType` (built-ins `default, success, info, warning, destructive`) | typed rename |
43769
- | `persistent` | unchanged | |
43770
- | `title`, `text`, `icon` | unchanged | |
43771
- | — | `duration`, `hideClose`, `position`, `loading`, `id` + `updateToast()`, `dismissToast(reason)`, `portal` / `portalProps`, `actions: ActionButtonConfig[]` | additive |
43772
-
43773
- `toast('plain string')` still works. The `BbToastMessage` type export is gone
43774
- — type options as `BbToastOptions`.
44626
+ | v2 | v3 | Kind |
44627
+ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------- |
44628
+ | `theme?: string` | `variant?: ToastVariantType` (built-ins `default, success, info, warning, destructive`) | typed rename |
44629
+ | `timeout?: number` | `duration?: number` | rename |
44630
+ | `showClose?: boolean` (default `true`) | `hideClose?: boolean` (default `false`) | rename, polarity inverted |
44631
+ | `persistent` | unchanged | |
44632
+ | `title`, `text`, `icon` | unchanged (`text` is now optional) | |
44633
+ | — | `position`, `loading`, `id` + `updateToast()`, `dismissToast(reason)`, `portal` / `portalProps`, `actions` | additive |
44634
+
44635
+ `timeout` → `duration` is the one to grep for. It is an options-object key, not
44636
+ a template attribute, so neither `bitboss-ui check` nor the eslint plugin sees
44637
+ it; TS catches a literal `toast({ timeout: 8000 })` on excess-property grounds,
44638
+ but a JS call site — or options built up in a variable — just drops the key and
44639
+ takes the default, which also shortened: **v2 6000ms, v3 4000ms**. Same rule at
44640
+ both ends: a non-positive value throws. `rg -n 'timeout' --type ts --type js`
44641
+ over your toast call sites.
44642
+
44643
+ `showClose` inverts, it does not disappear: `showClose: false` → `hideClose: true`,
44644
+ and `showClose: true` was the default so **delete it**.
43775
44645
 
43776
44646
  ```diff
43777
- - toast({ title: 'Deleted', theme: 'success' });
43778
- + toast({ title: 'Deleted', variant: 'success' });
44647
+ - toast({ title: 'Deleted', theme: 'success', timeout: 4000, showClose: false });
44648
+ + toast({ title: 'Deleted', variant: 'success', duration: 4000, hideClose: true });
43779
44649
 
43780
44650
  // the hand-rolled undo pattern collapses to:
43781
44651
  + toast({ title: 'Deleted', actions: [{ text: 'Undo', onClick: restore }] });
43782
44652
  ```
43783
44653
 
44654
+ `toast('plain string')` still works. The `BbToastMessage` type export is gone
44655
+ — type options as `BbToastOptions` (and a partial update as `BbToastUpdate`).
44656
+
44657
+ ## What `useToast()` returns
44658
+
44659
+ | v2 | v3 |
44660
+ | -------------------------------- | ------------------------------------------------------------ |
44661
+ | `{ toast, dismiss, dismissAll }` | `{ toast, dismiss, update }` |
44662
+ | `dismiss(id)` — id required | `dismiss(id?, reason?)` — **no argument dismisses them all** |
44663
+ | `dismissAll()` | removed — call `dismiss()` with no argument |
44664
+ | — | `update(id, options)` |
44665
+
44666
+ ```diff
44667
+ - const { toast, dismissAll } = useToast();
44668
+ - dismissAll();
44669
+ + const { toast, dismiss } = useToast();
44670
+ + dismiss();
44671
+ ```
44672
+
44673
+ Nothing catches this for you — `dismissAll` is not a template attribute, so
44674
+ `bitboss-ui check` and the eslint plugin never see it. Grep:
44675
+ `rg -n 'dismissAll' --type ts --type js --type vue`. Hits on a `useConfirm()`
44676
+ destructure are fine: `dismissAll` is **new** there, and unrelated.
44677
+
43784
44678
  Notes on `actions` (additive but with rules): handlers run with a spinner and
43785
44679
  sibling-disable, and hold the dismiss timer; portal toasts ignore `actions`.
43786
44680
  Styling hooks moved from `theme` classes to `bb-toast-message--<variant>` —
@@ -43790,7 +44684,7 @@ custom looks register through the plugin's `toastVariants` option
43790
44684
  ---
43791
44685
 
43792
44686
  <!-- dist/ai/guides/migration/components/bb-tooltip.md -->
43793
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-tooltip.md -->
44687
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-tooltip.md -->
43794
44688
 
43795
44689
  # BbTooltip / `v-bb-tooltip` — v2 → v3
43796
44690
 
@@ -43850,7 +44744,7 @@ registered by the runtime plugin — drop any manual
43850
44744
  ---
43851
44745
 
43852
44746
  <!-- dist/ai/guides/migration/components/bb-tree.md -->
43853
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/bb-tree.md -->
44747
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/bb-tree.md -->
43854
44748
 
43855
44749
  # BbTree — v2 → v3
43856
44750
 
@@ -43928,7 +44822,7 @@ lines are deliberately not built in — draw them in your own CSS against the
43928
44822
  ---
43929
44823
 
43930
44824
  <!-- dist/ai/guides/migration/components/use-broadcast-channel-instance.md -->
43931
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/use-broadcast-channel-instance.md -->
44825
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/use-broadcast-channel-instance.md -->
43932
44826
 
43933
44827
  # `useBroadcastChannelInstance` — v2 → v3
43934
44828
 
@@ -43985,25 +44879,34 @@ composable that renders nothing and that no component in the library consumed.
43985
44879
  ---
43986
44880
 
43987
44881
  <!-- dist/ai/guides/migration/components/use-confirm.md -->
43988
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/components/use-confirm.md -->
44882
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/components/use-confirm.md -->
43989
44883
 
43990
44884
  # `confirm()` / `useConfirm` — v2 → v3
43991
44885
 
43992
44886
  ## Options
43993
44887
 
43994
- | v2 | v3 | Kind |
43995
- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
43996
- | `yes?: boolean`, `yesText?: string`, `onYes?: () => …` | `yes?: false \| string \| ConfirmButtonConfig` (`{ text, variant, size, icon?, onClick? }`) | collapsed — **`yes: false` still hides the button, unchanged from v2.** Leave it alone; only `yesText`/`onYes` move into the config |
43997
- | `no?: boolean`, `noText?: string`, `onNo?: () => …` | `no?: false \| string \| ConfirmButtonConfig` | collapsed — same: **`no: false` is a no-op migration**, it hides the button exactly as in v2 |
43998
- | `actions?: boolean` | `actions?: false \| ConfirmButtonConfig[]` | `false` hides the footer; an array replaces the yes/no pair with custom buttons |
43999
- | default labels `"OK"` / `"Annulla"` (hard-coded) | localized via the active `locale` | **⚠ silent** — English apps stop showing Italian "Annulla"; apps that _wanted_ the literals must pass them |
44000
- | footer buttons `lg` | footer buttons default `md` | visual |
44001
- | `size` | `size?: keyof DialogSizes` — forwards to `BbDialog` | see the dialog width change in [main guide §2](../v2-to-v3.md) |
44002
- | — | `hideClose`, `variant` (`default, destructive`), `portal` / `portalProps` (mutually exclusive with `text`) | additive |
44003
- | second `confirm()` while one is open: first promise **never settled** (hung forever) | the superseded promise resolves `false` (implicit dismiss, no callbacks) | **⚠ behavioral** (bugfix 2026-07-18; the replace-not-queue semantics are unchanged) |
44004
-
44005
- `autoClose`, `title`, `text`, and the returned promise semantics otherwise
44006
- carry over.
44888
+ | v2 | v3 | Kind |
44889
+ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
44890
+ | `yes?: boolean`, `yesText?: string`, `onYes?: () => …` | `yes?: false \| string \| ConfirmButtonConfig` (`{ text, variant, size, icon?, onClick? }`) | collapsed — **`yes: false` still hides the button, unchanged from v2.** Leave it alone; only `yesText`/`onYes` move into the config |
44891
+ | `no?: boolean`, `noText?: string`, `onNo?: () => …` | `no?: false \| string \| ConfirmButtonConfig` | collapsed — same: **`no: false` is a no-op migration**, it hides the button exactly as in v2 |
44892
+ | `actions?: boolean` | `actions?: false \| ConfirmButtonConfig[]` | `false` hides the footer; an array replaces the yes/no pair with custom buttons |
44893
+ | default labels `"OK"` / `"Annulla"` (hard-coded) | localized via the active `locale` | **⚠ silent** — English apps stop showing Italian "Annulla"; apps that _wanted_ the literals must pass them |
44894
+ | footer buttons `lg` | footer buttons default `md` | visual |
44895
+ | `size` | `size?: keyof DialogSizes` — forwards to `BbDialog` | see the dialog width change in [main guide §2](../v2-to-v3.md) |
44896
+ | `theme?: string` | `variant?: ConfirmVariantType` (built-ins `default`, `destructive`) | typed rename — unregistered names are a compile error; add yours via the plugin's `confirmVariants` |
44897
+ | `timeout?: number` | `duration?: number` | rename — auto-dismisses, resolves `false` quietly (no `no.onClick`), and implicitly forces `autoClose: true` |
44898
+ | `autoClose?: boolean` — **opt-in**, omitted means the dialog stays open | `autoClose?: boolean` — **default `true`** | **⚠ silent behavior flip** — a v2 multi-step flow that kept the dialog open after resolve must now pass `autoClose: false` |
44899
+ | `hideHeader?: boolean` (a union that made `title` required when `false`) | removed — `title` is plain optional; omit it and no header renders | removed |
44900
+ | `useConfirm().setLoading(value)` | removed — busy state is driven by the button's `onClick` promise | removed — `useConfirm()` now returns `{ confirm, close, dismissAll }` |
44901
+ | — | `hideClose`, `portal` / `portalProps` (mutually exclusive with `text`) | additive |
44902
+ | second `confirm()` while one is open: first promise **never settled** (hung forever) | the superseded promise resolves `false` (implicit dismiss, no callbacks) | **⚠ behavioral** (bugfix 2026-07-18; the replace-not-queue semantics are unchanged) |
44903
+
44904
+ `title`, `text`, and the returned promise semantics otherwise carry over.
44905
+ `autoClose` does **not** — see its row above.
44906
+
44907
+ Grep for the collapsed keys: `rg -n 'yesText|noText|onYes|onNo|hideHeader|setLoading|autoClose|timeout' --type ts --type js --type vue`.
44908
+ TypeScript catches all of them (excess-property checks on the options literal,
44909
+ a hard property error on `setLoading`); a JS app gets no signal at all.
44007
44910
 
44008
44911
  ## Edits
44009
44912
 
@@ -44037,10 +44940,36 @@ drops the whole footer. Both spellings survive v2 → v3 untouched.
44037
44940
  If you only ever called `confirm('Are you sure?')`, nothing changes except the
44038
44941
  (localized) button labels and their size.
44039
44942
 
44943
+ ## CSS hooks
44944
+
44945
+ | v2 | v3 |
44946
+ | ------------------------------------------- | ------------------------------------------------------------------------------------- |
44947
+ | `.bb-confirm__content`, `.bb-confirm__text` | unchanged — still the right targets for the body copy |
44948
+ | `.bb-confirm__no` / `.bb-confirm__yes` | **gone** — the footer buttons render as bare `BbButton`s with no confirm class |
44949
+ | `.bb-confirm .bb-base-dialog__*` | `.bb-confirm .bb-dialog__*` — plus `.bb-offcanvas__*`, the adaptive sheet's parts |
44950
+ | — | `.bb-confirm--<variant>` on the dialog root — the lever for per-variant confirm skins |
44951
+
44952
+ `.bb-confirm__text` survives, so a v2 rule targeting the body copy still lands;
44953
+ do not relocate it onto `.bb-dialog__body`.
44954
+
44955
+ The cancel button is the one that lost its hook. Restyle it per call instead of
44956
+ in CSS — v3 defaults it to `size: 'md', variant: 'outline'`, and the Yes button
44957
+ to `primary` (or `destructive` when the confirm `variant` is `destructive`):
44958
+
44959
+ ```diff
44960
+ - .bb-confirm .bb-base-dialog__footer .bb-confirm__no:hover:not(:disabled) {
44961
+ - --color: var(--bb-primary);
44962
+ - }
44963
+ + confirm({ title: '…', no: { text: 'Keep', variant: 'ghost' } });
44964
+ ```
44965
+
44966
+ For a whole-skin change, register a confirm variant and hang the rules off
44967
+ `.bb-confirm--<name>` rather than reaching for the old element classes.
44968
+
44040
44969
  ---
44041
44970
 
44042
44971
  <!-- dist/ai/guides/migration/v2-to-v3.md -->
44043
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/migration/v2-to-v3.md -->
44972
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/migration/v2-to-v3.md -->
44044
44973
 
44045
44974
  # Migrating from bitboss-ui 2.x to 3.0
44046
44975
 
@@ -44056,39 +44985,26 @@ the per-component guides under [`migration/components/`](./components/) —
44056
44985
  only components with **functional** changes have one; if a component has no
44057
44986
  guide there, nothing about it needs migration.
44058
44987
 
44988
+ **Which version to migrate to.** v3 is still a prerelease, so install it by
44989
+ dist-tag:
44990
+
44991
+ ```bash
44992
+ npm install bitboss-ui@beta
44993
+ ```
44994
+
44995
+ `@beta` is the current v3 channel. Do **not** pin an exact alpha: the alpha line
44996
+ is frozen and several of its version numbers were tagged but never published, so
44997
+ `npm install bitboss-ui@3.0.0-alpha.8` fails outright with `ETARGET`. And a bare
44998
+ `npm install bitboss-ui` resolves to **v2** — `@latest` stays on the 2.x line
44999
+ until v3 goes stable. Verify with `npm ls bitboss-ui` before you change a single
45000
+ file; if it reports `2.x`, nothing below applies.
45001
+
44059
45002
  **How to read the tables:** every row is a hard break — old names are gone,
44060
45003
  with no alias and no deprecation shim. Where the break is _silent_ (the app
44061
45004
  compiles and runs but behaves differently), the row is marked **⚠ silent**;
44062
45005
  these are the rows you cannot find by waiting for errors, so grep for them
44063
45006
  explicitly (section 8).
44064
45007
 
44065
- ## For agents: surface what this guide misses
44066
-
44067
- This guide is the contract for the upgrade. If during a migration you hit
44068
- something it does not cover, **do not silently work around it** — a quiet
44069
- workaround hides a gap that will bite the next project. Report it to the
44070
- user in your migration summary, clearly separated from the routine edits.
44071
-
44072
- Reportable findings, in order of severity:
44073
-
44074
- 1. **An undocumented break** — v2 code that compiled or behaved one way and
44075
- changes in v3 without a matching entry here or in the component's guide.
44076
- 2. **A mapping that doesn't hold** — you applied a documented old→new edit
44077
- and the result compiles but renders or behaves differently than the guide
44078
- promises.
44079
- 3. **An ambiguity you had to guess on** — two plausible migrations for the
44080
- same v2 usage and the guide doesn't say which is intended.
44081
- 4. **A pattern with no v3 home** — a v2 usage (prop combination, slot trick,
44082
- CSS hook) whose replacement you could not find at all.
44083
-
44084
- For each finding include: the component, the exact v2 usage (snippet), what
44085
- you expected from the guide, and what actually happened. Recommend that the
44086
- user forwards it to the bitboss-ui maintainers so the guide gets a row —
44087
- these reports are how this document stays truthful.
44088
-
44089
- Routine work — renames that behaved exactly as the tables say — belongs in
44090
- the normal summary, not in this report. The report is only for surprises.
44091
-
44092
45008
  ## 0. Upgrade strategy — do it in this order
44093
45009
 
44094
45010
  1. **Swap the setup** (section 1): install the build plugin and the runtime
@@ -44174,11 +45090,54 @@ Migration deltas, one by one:
44174
45090
 
44175
45091
  | v2 | v3 |
44176
45092
  | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44177
- | `import 'bitboss-ui/styles.css'` in the entry file | **Delete it.** The runtime plugin auto-injects styles (`injectStyles` defaults `true`). Keep a manual import only if you set `injectStyles: false`. |
45093
+ | `import 'bitboss-ui/styles.css'` in the entry file | **Delete it.** The runtime plugin auto-injects styles (`injectStyles` defaults `true`). Keep a manual import only if you set `injectStyles: false` — see the Tailwind exception below. |
44178
45094
  | No build plugin | **Add `bitbossUi({ iconDir })`** to `vite.config.ts` (or the `bitboss-ui/nuxt` module). The app does not build without it — components import the virtual config module. |
44179
45095
  | `app.use(BbTooltipDirectivePlugin)` / `app.use(BbDropdownDirectivePlugin)` | **Delete both.** `bitbossUiPlugin` registers `v-bb-tooltip` and `v-bb-dropdown` itself (opt out with `injectDirectives: false` if the names collide). |
44180
45096
  | No reset stylesheet | Optional: `resetCss: true` or `import 'bitboss-ui/reset.css'`. Skip in Tailwind apps — Preflight already does this. |
44181
45097
  | Icons registered by augmenting the `IconRegistry` type | **Gone** (the `IconRegistry` export no longer exists — see section 5). Drop `.svg` files into `iconDir` and reference them as `local:<name>`; provider icons (`lucide:*`, `mdi:*`, …) need the matching `@iconify-json/*` dev dependency. |
45098
+ | A hand-rolled icon plugin doing `app.provide('icons', { … })` | **Delete it** and move the art into `iconDir`. See the shadowing warning below — this one is silent. |
45099
+
45100
+ ### ⚠ silent — a hand-rolled `provide('icons', …)` is shadowed, not merged
45101
+
45102
+ `bitbossUiPlugin` provides the icon registry under two keys: a Symbol
45103
+ (`bitboss-ui:icons`) and the legacy string key `'icons'` — the same key an app
45104
+ icon plugin typically uses. `BbIcon` reads the **Symbol first**
45105
+ (`inject(ICONS_INJECTION_KEY) ?? inject('icons')`), so once the library plugin
45106
+ is installed your own `provide('icons', …)` map never wins. Any glyph that
45107
+ existed only in that map stops resolving, and nothing warns. Move those SVGs
45108
+ into `iconDir` and reference them as `local:<name>`.
45109
+
45110
+ App code that still calls `inject('icons')` now receives the **library's**
45111
+ registry object, not yours. The string key is a one-release compatibility shim
45112
+ — migrate those call sites off it or delete them.
45113
+
45114
+ ### ⚠ Tailwind Preflight cannot load before the library when styles are injected
45115
+
45116
+ Auto-injection always puts the library sheet at the **top** of `<head>` — the
45117
+ guaranteed order is reset → library styles → every other stylesheet, because
45118
+ the injector inserts before the first existing `<style>` / `<link
45119
+ rel="stylesheet">`. Preflight is an app sheet like any other, so it lands
45120
+ _after_ the library and wins every specificity tie. There is no option to
45121
+ inject later.
45122
+
45123
+ If your v2 app depended on the opposite order (the manual
45124
+ `import 'bitboss-ui/styles.css'` placed after your Tailwind entry), keep the
45125
+ manual import and turn injection off:
45126
+
45127
+ ```ts
45128
+ // vite.config.ts
45129
+ bitbossUi({ iconDir: './src/assets/icons', injectStyles: false });
45130
+ ```
45131
+
45132
+ ```ts
45133
+ // main.ts — the order is yours again
45134
+ import './app.css'; // your Tailwind entry (@import 'tailwindcss')
45135
+ import 'bitboss-ui/styles.css'; // after Tailwind, so library rules win the ties
45136
+ ```
45137
+
45138
+ Leave `resetCss` at its `false` default here — Preflight already resets.
45139
+ `injectStyles` controls only whether the library injects its own `<style>`;
45140
+ nothing else about the plugin changes.
44182
45141
 
44183
45142
  ### Nuxt
44184
45143
 
@@ -44309,24 +45268,36 @@ scope instead.
44309
45268
  explicitly.
44310
45269
  - `--bb-radius` went `4px` → `8px`.
44311
45270
 
45271
+ Six more tokens changed value **without changing name**, so they are invisible
45272
+ to the rename map and to every grep below — see
45273
+ ["Tokens that kept their name but changed"](#tokens-that-kept-their-name-but-changed).
45274
+
44312
45275
  ### Token rename map
44313
45276
 
44314
- | v2 override | v3 replacement |
44315
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44316
- | `--bb-primary-base` | deleted — set `--bb-primary` directly |
44317
- | `--bb-contrasting` / `--bb-contrasting-dark` | `--bb-primary-fg` |
44318
- | `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
44319
- | `--bb-hint`, `--bb-icon-color`, `--bb-placeholder`, `--bb-prefix-color`, `--bb-muted-color` | `--bb-text-muted` |
44320
- | `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
44321
- | `--bb-input-font-size`, `--bb-input-mobile-font-size` | `--bb-input-fs`, `--bb-input-fs-mobile` |
44322
- | `--bb-overlay-color` + `--bb-overlay-opacity` | `--bb-overlay` (one color with alpha) |
44323
- | `--bb-overlay-z-index` | `--bb-z-overlay` |
44324
- | `--bb-easing-bezier` | `--bb-ease` |
44325
- | `--bb-dialog-px/pt/pb/gap` | `--bb-panel-p` (surfaces share one padding token) |
44326
- | `--bb-button-h`, `--bb-button-icon`, `--bb-tabs-*`, `--bb-checkbox-*`, `--bb-radio-*`, `--bb-switch-*`, `--bb-rating-*`, `--bb-slider-*`, `--bb-table-cell-h`, `--bb-select-option-*` | **no longer global.** Component dimensions hang off `--bb-control-h`, `--bb-fs`, `--bb-radius`; anything finer is an unprefixed local token on the component's own class. |
44327
-
44328
- **When a v2 global becomes a component token, resize only what you use.** The
44329
- row above is the trap: a v2 global like `--bb-button-icon` had ONE value for
45277
+ | v2 override | v3 replacement |
45278
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45279
+ | `--bb-primary-base` | deleted — set `--bb-primary` directly |
45280
+ | `--bb-contrasting` / `--bb-contrasting-dark` | `--bb-primary-fg` |
45281
+ | `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
45282
+ | `--bb-hint`, `--bb-icon-color`, `--bb-placeholder`, `--bb-prefix-color`, `--bb-muted-color` | `--bb-text-muted` |
45283
+ | `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
45284
+ | `--bb-input-font-size`, `--bb-input-mobile-font-size` | `--bb-input-fs`, `--bb-input-fs-mobile` |
45285
+ | `--bb-overlay-color` + `--bb-overlay-opacity` | `--bb-overlay` (one color with alpha) |
45286
+ | `--bb-overlay-z-index` | `--bb-z-overlay` |
45287
+ | _(no v2 equivalent)_ | `--bb-ease` — new in v3. v2 hard-coded seven different `cubic-bezier()` curves and exposed no easing token, so there is nothing to rename; set `--bb-ease` once if you want a house curve. |
45288
+ | `--bb-dialog-px`, `--bb-dialog-py` | `--bb-panel-p` — **one value on both axes**, for dialogs and off-canvas alike. If your v2 px ≠ py, see "Asymmetric surface padding" below. |
45289
+ | `--bb-dialog-close` | **delete it** — do not map it onto `.bb-close-button { --size }`; v3's default already reproduces the v2 control. See below. |
45290
+ | `--bb-dialog-title-size` | the local `--dialog-title-fs` on `.bb-dialog` / `.bb-offcanvas` (default `calc(var(--bb-fs) + 2px)`) |
45291
+ | `--bb-dialog-title-weight` | **no knob** — the title is hardcoded `font-weight: 500`. Override with a CSS rule, see below. |
45292
+ | `--bb-button-h`, `--bb-button-icon`, `--bb-checkbox-*`, `--bb-radio-*`, `--bb-switch-*`, `--bb-rating-*`, `--bb-slider-*`, `--bb-table-cell-h`, `--bb-select-option-*` | **no longer global.** Component dimensions hang off `--bb-control-h`, `--bb-fs`, `--bb-radius`; anything finer is an unprefixed local on the component's own class — with one exception: `--bb-select-option-px/-py` have **no** v3 local, the listbox option's inline padding is a literal (`.bb-listbox__option { padding-inline: … }` in your own CSS). |
45293
+
45294
+ **`--bb-input-*` and `--bb-label-*` are NOT in the row above.** They are still
45295
+ global v3 theme tokens, declared in `variables.css` and read across the whole
45296
+ form family — do not delete them. Several changed their default value; the
45297
+ drift table below lists which.
45298
+
45299
+ **When a v2 global becomes a component token, resize only what you use.** That
45300
+ last row is the trap: a v2 global like `--bb-button-icon` had ONE value for
44330
45301
  every button because v2 had no size scale. v3 does, so the same value now lives
44331
45302
  in a per-size family (`--icon-size-xs … --icon-size-2xl`) that the size class
44332
45303
  re-points. Two mistakes follow, and both were made on a real migration:
@@ -44344,11 +45315,173 @@ doctrine in `design-tokens.md` § "Overriding one from a consumer app". And chec
44344
45315
  first whether the v2 value was a real customisation: if it merely restated v2's
44345
45316
  default, the migration is to **delete it** and inherit the v3 scale.
44346
45317
 
45318
+ **Asymmetric surface padding.** `--bb-panel-p` is a single value used on both
45319
+ axes. If your v2 theme set `--bb-dialog-px` ≠ `--bb-dialog-py`, set
45320
+ `--bb-panel-p` to the **horizontal** value (it also positions the close button)
45321
+ and override the vertical with a normal `padding` rule on the part classes.
45322
+ There is no `--bb-panel-px` / `--bb-panel-py` pair.
45323
+
45324
+ ```css
45325
+ /* v2: --bb-dialog-px: 24px; header --bb-dialog-py: 10px; body --bb-dialog-py: 20px */
45326
+ .my-dialog {
45327
+ --bb-panel-p: 24px; /* horizontal + close-button inset */
45328
+ }
45329
+ .my-dialog .bb-dialog__header {
45330
+ padding-block: 10px 5px;
45331
+ }
45332
+ .my-dialog .bb-dialog__body .bb-dialog__body-content {
45333
+ padding-block: 0 20px;
45334
+ }
45335
+ .my-dialog .bb-dialog__body:first-child .bb-dialog__body-content {
45336
+ padding-top: 20px;
45337
+ }
45338
+ .my-dialog .bb-dialog__footer {
45339
+ padding-block: 20px;
45340
+ }
45341
+ ```
45342
+
45343
+ Use `padding-block`, not the `padding` shorthand, so the horizontal half keeps
45344
+ tracking `--bb-panel-p`. Specificity: the library rules are
45345
+ `.bb-dialog .bb-dialog__header` (0,2,0), `.bb-dialog__body-content` (0,1,0) and
45346
+ `.bb-dialog__body:first-child .bb-dialog__body-content` (0,2,1) — the
45347
+ three-class app selectors above beat all of them. `BbOffCanvas` is identical
45348
+ with `.bb-offcanvas__*`.
45349
+
45350
+ **`--px` and `--pt` are not dialog knobs.** `.bb-dialog` declares them, but
45351
+ nothing in the dialog or off-canvas CSS reads them, and every component that
45352
+ _does_ read `--px` (`BbButton`, `BbAlert`, `BbTooltip`, `BbBaseSlider`)
45353
+ re-declares it on its own root class, so an ancestor value never reaches those
45354
+ either. Setting `--px` on a dialog part is inert — it silently leaves the
45355
+ horizontal padding at whatever `--bb-panel-p` resolves to.
45356
+
45357
+ If only the **vertical rhythm between parts** differs, the local `--dialog-gap`
45358
+ (default `--bb-panel-p`) already does it without a padding rule: it drives the
45359
+ gap below the body content and the footer's block padding on `.bb-dialog` and
45360
+ `.bb-offcanvas`. It does **not** cover the header's top padding, or the body's
45361
+ top padding in the headerless case — those still take `--bb-panel-p`.
45362
+
45363
+ **`--bb-dialog-close` maps to nothing — delete it.** In v2 it was the close
45364
+ **icon** width (`12px`) and the button around it added `8px` of padding, so the
45365
+ control was 28px. v3 inverts the relationship: `.bb-close-button { --size }` is
45366
+ the whole control box and the icon is derived as `max(--size - 2 × --p, 10px)`.
45367
+ Inside a dialog v3 already ships `--size: 28px; --p: 7px` (a 14px glyph) — the
45368
+ same 28px control v2's default produced. Feeding v2's `12px` into `--size`
45369
+ yields a **12×12px** control, well under the WCAG 2.5.8 minimum target size. If
45370
+ you must match the old glyph exactly, widen the padding instead of shrinking
45371
+ the box:
45372
+
45373
+ ```css
45374
+ /* 12px glyph, 28px control */
45375
+ .my-dialog .bb-dialog__header > .bb-close-button {
45376
+ --p: 8px;
45377
+ }
45378
+ ```
45379
+
45380
+ That selector needs at least three classes to beat the library's
45381
+ `.bb-dialog .bb-dialog__header > .bb-close-button` (0,3,0).
45382
+
45383
+ **`--bb-dialog-title-weight` has no v3 home.** The title is `font-weight: 500`,
45384
+ hardcoded. Restore v2's `600` with a rule that outweighs the library's
45385
+ `.bb-dialog .bb-dialog__header .bb-dialog__title` (0,3,0):
45386
+
45387
+ ```css
45388
+ .my-dialog .bb-dialog__header .bb-dialog__title {
45389
+ font-weight: 600;
45390
+ }
45391
+ ```
45392
+
44347
45393
  A complete v3 theme is typically two small blocks of ~10 declarations
44348
45394
  (`--bb-primary`, `--bb-radius`, `--bb-control-h`, `--bb-panel`, `--bb-text`,
44349
45395
  `--bb-border`, `--bb-danger`, …) — one in `:root, .light`, the changed knobs
44350
45396
  again in `.dark`. If your v2 theme was long, most of it maps to "delete".
44351
45397
 
45398
+ ### Tokens that kept their name but changed
45399
+
45400
+ The rename map above is keyed by _the name you must change_. These tokens kept
45401
+ their v2 name and stayed global, so they appear in no rename row and no grep
45402
+ finds them — the only symptom is that the app renders differently.
45403
+
45404
+ **Values that drifted.** Same name, same meaning, new default:
45405
+
45406
+ | token | v2 | v3 |
45407
+ | --------------------- | ------------------------------------- | ----------------------------------------------------------------------------- |
45408
+ | `--bb-input-h` | `36px` | `32px` (via `--bb-control-h`) |
45409
+ | `--bb-input-py` | `4px` | `3px` |
45410
+ | `--bb-input-prefix-w` | `40px` | `20px` |
45411
+ | `--bb-label-weight` | `400` | `500` |
45412
+ | `--bb-ring-size` | `4px` | `2px` |
45413
+ | `--bb-leading` | `24px` (`--bb-input-font-size` × 1.5) | `21px` (`--bb-input-fs` × 1.5, and `--bb-input-fs` is now `14px`, not `16px`) |
45414
+
45415
+ The consequence cuts both ways, which is what makes it easy to get wrong: an
45416
+ app that **carried its v2 theme forward** keeps the v2 look (36px inputs, 4px
45417
+ rings) and never sees v3's proportions; an app that **deleted its theme as
45418
+ redundant** silently shifts to shorter inputs, half-width prefix columns and a
45419
+ half-thickness focus ring. Re-check each of these six against the v3 default
45420
+ and delete only the declarations that merely restated a **v2** default.
45421
+
45422
+ **A type that narrowed.** `--bb-ring-opacity` survives by name but v3 registers
45423
+ it with `@property … syntax: '<percentage>'`:
45424
+
45425
+ | token | v2 form | v3 form | symptom |
45426
+ | ------------------- | -------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45427
+ | `--bb-ring-opacity` | unitless `0.25` / `0.4`, multiplied by `100%` internally | `25%` / `40%` — a `<percentage>` | ⚠ silent: a unitless override is invalid at computed-value time, so it is discarded and the token falls back to the registered `initial-value: 25%`. v3's own defaults are `15%` light / `40%` dark. |
45428
+
45429
+ Nothing reports this — not `vue-tsc`, not the eslint plugin, not
45430
+ `npx bitboss-ui check` (none of them read CSS), and not the browser console:
45431
+ an invalid registered-property value is not a parse error, so it logs nothing.
45432
+ Grep your stylesheets for `--bb-ring-opacity` and confirm every value carries a
45433
+ `%`.
45434
+
45435
+ Nine tokens are `@property`-registered in v3 — the eight color knobs
45436
+ (`syntax: '<color>'`) plus `--bb-ring-opacity`. Each rejects values outside its
45437
+ declared syntax the same silent way, so treat any future registration as the
45438
+ same class of break.
45439
+
45440
+ ### Button and variant skin colors moved the other way
45441
+
45442
+ The "no longer global" row sends per-component values _down_ into unprefixed
45443
+ locals. The button **skin** colors went the opposite direction — up into global
45444
+ theme tokens. In v2 a skin was four locals set on your own modifier class:
45445
+
45446
+ ```css
45447
+ /* v2 theming.css */
45448
+ .bb-button--outline {
45449
+ --color: #fff;
45450
+ --border-color: #d1d5db;
45451
+ --text-color: #111827;
45452
+ /* hover / pressed / ring were derived from --color automatically */
45453
+ }
45454
+ ```
45455
+
45456
+ v3 reads none of those — the old locals are inert, so a skin ported verbatim
45457
+ silently reverts to the library defaults. Each variant has its own `--bb-*`
45458
+ family, declared in `variables.css` and overridable globally or on any scope:
45459
+
45460
+ | v2 local on `.bb-button` | v3 token family (per variant) |
45461
+ | -------------------------------------------- | ----------------------------------- |
45462
+ | `--color` (fill) | `--bb-<variant>` |
45463
+ | `--text-color` | `--bb-<variant>-fg` |
45464
+ | `--border-color` | `--bb-<variant>-border` |
45465
+ | `--ring-color` | `--bb-<variant>-ring` |
45466
+ | _derived_ `mix(--color 90%, black)` on hover | `--bb-<variant>-hover` (explicit) |
45467
+ | _derived_ `mix(--color 80%, black)` on press | `--bb-<variant>-pressed` (explicit) |
45468
+
45469
+ `<variant>` is one of `primary`, `secondary`, `outline`, `ghost`,
45470
+ `destructive`, `link`. Note the last two rows: v2 _derived_ hover and pressed
45471
+ from the fill, v3 states them. Porting only `--bb-outline` leaves the hover
45472
+ state on the library's default.
45473
+
45474
+ ```css
45475
+ /* v3 equivalent of the block above */
45476
+ :root {
45477
+ --bb-outline: #fff;
45478
+ --bb-outline-border: #d1d5db;
45479
+ --bb-outline-fg: #111827;
45480
+ --bb-outline-hover: #f3f4f6;
45481
+ --bb-outline-pressed: #e5e7eb;
45482
+ }
45483
+ ```
45484
+
44352
45485
  ## 4. Variants: from `theme` strings and class overrides to typed registries
44353
45486
 
44354
45487
  ### What changed
@@ -44365,6 +45498,19 @@ v3 replaces all of that with per-component `variant` props whose types are
44365
45498
  registers. An unregistered name is a **compile error** (v2's silent
44366
45499
  render-unstyled behavior is gone by design), and the `theme` prop is gone.
44367
45500
 
45501
+ **⚠ v2 shipped no variant CSS at all, so adopting a variant is additive.** Grep
45502
+ the v2 stylesheet and the only `bb-button--*` rules are `group`, `icon` and
45503
+ `loading`; there are no `bb-alert--*`, no appearance-level `bb-badge--*`, no
45504
+ `bb-tooltip--*` and no `bb-toast-message--*` rules. v2's components _emitted_
45505
+ those names (`bb-alert--${theme}`, `bb-tooltip--${theme}`,
45506
+ `bb-toast-message--${theme}`) and the library never styled them. In v2 the
45507
+ class and the `theme` string meant **"whatever your app's CSS says, and nothing
45508
+ else."** In v3 every one of those names is a real skin, so converting hands the
45509
+ library a styling role it did not have — wherever your CSS does not override
45510
+ every property v3 sets (background, border, foreground, hover, pressed, focus
45511
+ ring), the appearance drifts. Diff a screenshot; do not assume. Section 7
45512
+ covers the class-to-prop rewrite itself.
45513
+
44368
45514
  Built-in sets and the class each name maps to:
44369
45515
 
44370
45516
  | Plugin option | Built-in variants | Class hook |
@@ -44378,6 +45524,54 @@ Built-in sets and the class each name maps to:
44378
45524
  | `dropdownItemVariants` | `default, destructive` | `bb-dropdown__item--variant-<name>` |
44379
45525
  | `tooltipVariants` | `default, destructive` | `bb-tooltip--<name>` |
44380
45526
 
45527
+ ### Picking a v3 name for a v2 `theme` value
45528
+
45529
+ v2's `theme` was free-form, so most apps carry names that are not in the table
45530
+ above (`error`, `danger`, `info`, `success`, `blue`, …). Work through it in
45531
+ this order:
45532
+
45533
+ 1. **Name is in the table, and your app never styled it** → use the built-in
45534
+ `variant`. This is the only genuinely free conversion.
45535
+ 2. **Name is in the table, but your app styled it itself** → v3's built-in now
45536
+ paints underneath your rules. Decide explicitly: accept v3's look and delete
45537
+ your CSS, or register the name (`alertVariants: ['warning']`) so your CSS
45538
+ stays the sole styling source.
45539
+ 3. **Name is not in the table** → register it and keep your CSS. **Do not remap
45540
+ onto the "nearest" built-in.** `error → destructive`, `danger →
45541
+ destructive`, `info → primary` all compile, all look plausible in review,
45542
+ and all silently replace your palette with the library's. Remap only as a
45543
+ deliberate redesign decision, never as a mechanical migration step.
45544
+ 4. **Name was never styled by anyone** → see the next subsection.
45545
+
45546
+ Registering is cheap — one array entry per name — and it is the
45547
+ behaviour-preserving default. Prefer it whenever you are not sure.
45548
+
45549
+ ### ⚠ silent — a `theme` value that did nothing in v2 may do something in v3
45550
+
45551
+ Because v2 shipped no variant CSS, unless your app defined the rule the `theme`
45552
+ prop was **inert**: every value rendered the same default surface. In v3 the
45553
+ same string is a live skin, and whether it stays inert depends entirely on
45554
+ whether the name happens to collide with a v3 built-in:
45555
+
45556
+ ```
45557
+ both of these rendered an identical plain alert in v2:
45558
+
45559
+ <BbAlert theme="success" /> v3: not an alert built-in → compile error, you WILL notice
45560
+ <BbAlert theme="warning" /> v3: built-in → silently becomes an amber alert, you will NOT
45561
+ ```
45562
+
45563
+ So two identical-looking v2 call sites migrate to opposite outcomes. Before
45564
+ converting any `theme=` value, check whether _anything_ styled it in v2 — your
45565
+ stylesheet, since the library did not. If nothing did, the value was
45566
+ decoration:
45567
+
45568
+ - register it as an empty custom variant (`alertVariants: ['success', 'blue']`)
45569
+ to hold v2's appearance exactly, or
45570
+ - drop the prop entirely and let the v3 default apply, or
45571
+ - convert it and accept the new look **on purpose**.
45572
+
45573
+ What you must not do is convert it mechanically and assume nothing moved.
45574
+
44381
45575
  ### Custom variants are first-class
44382
45576
 
44383
45577
  If your v2 app invented its own look (a `theme="brand"` or a hand-written
@@ -44406,6 +45600,12 @@ works with a visible cast (`variant="brand" as ButtonVariantType` won't
44406
45600
  compile — cast the value, not the prop), but the registry is the intended
44407
45601
  path.
44408
45602
 
45603
+ **A name you keep as a plain class should not stay in the `bb-*` namespace.**
45604
+ An app-minted `bb-button--pressed` or `bb-table--borderless` is squatting on
45605
+ the library's prefix and collides the day the library ships that name. Either
45606
+ register it as a variant, or rename it into your own prefix
45607
+ (`app-button--pressed`). Section 7 has the decision table.
45608
+
44409
45609
  ## 5. API-wide changes
44410
45610
 
44411
45611
  These patterns repeat across many components. The per-component guides list
@@ -44413,20 +45613,22 @@ every occurrence; this section explains each pattern once.
44413
45613
 
44414
45614
  ### 5.1 Components renamed, split, or removed
44415
45615
 
44416
- | v2 | v3 | What to do |
44417
- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44418
- | `BbBadge` (floating count/dot anchored to an element) | **`BbIndicator`** | `content` → `text`, `color` → `variant`, `floating` removed. New `max` prop renders `${max}+`. |
44419
- | `BbChip` | **removed** — use `BbBadge` | v3 `BbBadge` is the inline pill/token: same `click:clear` event and `clearableLabel`, but `clearable` is now **opt-in** (v2 chips defaulted clearable). |
44420
- | — | **`BbBadge`** (repurposed) | The v3 badge is a new component in an old name: inline label with `variant`, `icon`, `loading`, `clearable`. Any v2 `<BbBadge>` you have is an _indicator_ — rename it. |
44421
- | `BbTab` | **`BbTabs`** | Component and types renamed (`BbTabProps` → `BbTabsProps`; deprecated type aliases remain, the component name does not). |
44422
- | `Base*` internals (`BaseButton`, `BaseDialog`, `BaseCheckbox`, `BaseTag`, …) | **`BbBase*`** | If you imported any base primitive or targeted its BEM block in CSS (`.base-btn` → `.bb-base-button`), rename. `BbBaseButton` is now a supported public export. |
44423
- | `BbCheckboxIcon` / `BbRadioIcon` / `BbSwitchIcon` | **`BbBaseCheckboxIcon`** / **`BbBaseRadioIcon`** / **`BbBaseSwitchIcon`** | Rename import + BEM block (`.bb-checkbox-icon` → `.bb-base-checkbox-icon`). |
44424
- | `BbBase*` input controls + `CommonInputWrapper` / `CommonInputOuterContainer` | **no longer exported** — use the full public component | v2 exposed the styled base inputs (`BbBaseTextInput`, `BbBaseSelect`, `BbBaseSwitch`, `BbBaseDatePickerInput`, …) and the input-chrome wrappers because a few full inputs were missing. v3 ships a full public component for every input, so these are internal now. Use `BbTextInput` / `BbSelect` / `BbSwitch` / … ; for a bare/inline control with no label gap, pass **`hideLabel`**. Still exported: `BbBaseButton` and the glyphs (`BbBaseCheckboxIcon` / `BbBaseRadioIcon` / `BbBaseSwitchIcon`). |
44425
- | `useWizard` (+ `Step`, `WizardState`, `wizardInjectionKey`) | **removed** | No replacement — it was unused scaffolding. Copy the v2 implementation into your app if you depended on it. |
44426
- | `BbIntersection` (+ `BbIntersectionProps` / `BbIntersectionEvents`) | **removed** | No replacement — it wrapped `IntersectionObserver` in ~40 lines and carried no styling. Use `@vueuse/core`'s `useIntersectionObserver`, or copy the v2 component. Full mapping of the `shown`/`hidden`/`intersected` events and the slot props: [components/bb-intersection.md](./components/bb-intersection.md). |
44427
- | `useBroadcastChannelInstance` | **removed** | Same reasoning: a typed wrapper over a browser API, no markup, no styling, used by no component here. Cross-tab messaging is app architecture. Use `useBroadcastChannel` from `@vueuse/core` — already in your tree, since this library depends on it. **Two behaviours do not carry over** (handler auto-removal on unmount, and the refcount the v2 docs described but never had): [components/use-broadcast-channel-instance.md](./components/use-broadcast-channel-instance.md). |
44428
- | `BbToastMessage` type export | **removed** | Use `BbToastOptions` (the `toast()` options shape). |
44429
- | `IconRegistry` / `IconNameFromRegistry` exports | **removed** | Icon names are plain strings now. Local art goes in `iconDir` as `local:<name>`; delete your `IconRegistry` augmentation. |
45616
+ | v2 | v3 | What to do |
45617
+ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45618
+ | `BbBadge` (floating count/dot anchored to an element) | **`BbIndicator`** | `content` → `text`, `color` → `variant`, `floating` removed. New `max` prop renders `${max}+`. |
45619
+ | `BbChip` | **removed** — use `BbBadge` | v3 `BbBadge` is the inline pill/token: same `click:clear` event and `clearableLabel`, but `clearable` is now **opt-in** (v2 chips defaulted clearable). |
45620
+ | — | **`BbBadge`** (repurposed) | The v3 badge is a new component in an old name: inline label with `variant`, `icon`, `loading`, `clearable`. Any v2 `<BbBadge>` you have is an _indicator_ — rename it. |
45621
+ | `BbTab` | **`BbTabs`** | Component and types renamed (`BbTabProps` → `BbTabsProps`; deprecated type aliases remain, the component name does not). |
45622
+ | `Base*` internals (`BaseButton`, `BaseDialog`, `BaseCheckbox`, `BaseTag`, …) | **`BbBase*`** | If you imported any base primitive or targeted its BEM block in CSS (`.base-btn` → `.bb-base-button`), rename. `BbBaseButton` is now a supported public export. |
45623
+ | `BbCheckboxIcon` / `BbRadioIcon` / `BbSwitchIcon` | **`BbBaseCheckboxIcon`** / **`BbBaseRadioIcon`** / **`BbBaseSwitchIcon`** | Rename import + BEM block (`.bb-base-checkbox-container__icon` → `.bb-base-checkbox-icon`; see §5.6). |
45624
+ | `BbBase*` input controls + `CommonInputWrapper` / `CommonInputOuterContainer` | **no longer exported** — use the full public component | v2 exposed the styled base inputs (`BbBaseTextInput`, `BbBaseSelect`, `BbBaseSwitch`, `BbBaseDatePickerInput`, …) and the input-chrome wrappers because a few full inputs were missing. v3 ships a full public component for every input, so these are internal now. Use `BbTextInput` / `BbSelect` / `BbSwitch` / … ; for a bare/inline control with no label gap, pass **`hideLabel`**. Still exported: `BbBaseButton` and the glyphs (`BbBaseCheckboxIcon` / `BbBaseRadioIcon` / `BbBaseSwitchIcon`).<br><br>**These primitives were also restructured, not just un-exported.** The outer+inner pair merged into one `CommonInputWrapper` (`.common-input-wrapper` / `__inner`), `BbBaseInputContainer` gained an inner `__layout` element that now carries the orientation modifiers, and `__input` became `__control`. Anything anchored to the old DOM — app CSS, test selectors, JS positioning a floating panel off `.bb-base-input-container__input` — fails silently: nothing throws, the element is simply never found. See §5.6.<br><br>**Built a custom control on the chrome?** If your app composed `BaseInputContainer` + `CommonInputOuterContainer` + `CommonInputInnerContainer` around an element the library does not ship (a highlight-overlay textarea, a field with a bespoke floating panel), there is no public v3 component to swap in — those wrappers exist in v3 but are internal. The sanctioned fallback is to **copy** the chrome into your app from the implementation source that ships with the package: `node_modules/bitboss-ui/dist/ai/source/BbBaseInputContainer.md` and `dist/ai/source/CommonInputWrapper.md` carry the complete `.vue` template, `types.ts` and `index.css` for each (89 components have one). Those files are banner-marked "Reference Only - Internal" — that bans _importing_ the internal component, not copying its template and CSS into a component of your own, which is the supported route here. The class names a copied template emits are styled by the public `bitboss-ui/styles.css`, so it inherits library chrome for free. Check first whether v3 already ships what you built — `BbTimePickerInput` and `BbTimePicker` are new in v3. |
45625
+ | `useWizard` (+ `Step`, `WizardState`, `wizardInjectionKey`) | **removed** | No replacement — it was unused scaffolding. Copy the v2 implementation into your app if you depended on it. |
45626
+ | `BbIntersection` (+ `BbIntersectionProps` / `BbIntersectionEvents`) | **removed** | No replacement — it wrapped `IntersectionObserver` in ~40 lines and carried no styling. Use `@vueuse/core`'s `useIntersectionObserver`, or copy the v2 component. Full mapping of the `shown`/`hidden`/`intersected` events and the slot props: [components/bb-intersection.md](./components/bb-intersection.md). |
45627
+ | `useBroadcastChannelInstance` | **removed** | Same reasoning: a typed wrapper over a browser API, no markup, no styling, used by no component here. Cross-tab messaging is app architecture. Use `useBroadcastChannel` from `@vueuse/core` — already in your tree, since this library depends on it. **Two behaviours do not carry over** (handler auto-removal on unmount, and the refcount the v2 docs described but never had): [components/use-broadcast-channel-instance.md](./components/use-broadcast-channel-instance.md). |
45628
+ | `BbRatio` (+ `BbRatioProps`, `.bb-ratio`) | **removed** | No replacement — it was a one-rule wrapper. Use the `aspect-ratio` CSS property, or copy the v2 component. |
45629
+ | `useQueue`, `useQuery` | **removed** | Both were public v2 exports and neither has a v3 equivalent; the import fails at build. Copy the v2 implementations out of the 2.1.135 tarball (`dist/composables/useQueue.d.ts`, `useQuery.d.ts`) into your app, or replace them with `@vueuse/core` equivalents. |
45630
+ | `BbToastMessage` type export | **removed** | Use `BbToastOptions` (the `toast()` options shape). |
45631
+ | `IconRegistry` / `IconNameFromRegistry` exports | **removed** | Icon names are plain strings now. Local art goes in `iconDir` as `local:<name>`; delete your `IconRegistry` augmentation. |
44430
45632
 
44431
45633
  ### 5.2 The polarity wave: booleans now read as opt-in
44432
45634
 
@@ -44448,7 +45650,7 @@ to the new name (`hide-close`).
44448
45650
 
44449
45651
  | Component(s) | v2 prop (default) | v3 prop (default) |
44450
45652
  | -------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44451
- | `BbAlert`, `BbDialog`, `BbOffCanvas` | `showClose` (`true`) | `hideClose` (`false`) — also new on `toast()` / `confirm()` options |
45653
+ | `BbAlert`, `BbDialog`, `BbOffCanvas` | `showClose` (`true`) | `hideClose` (`false`) — also on `toast()` options, where v2 had `showClose` too and the same inversion applies (`showClose: false` → `hideClose: true`, `showClose: true` → delete). Genuinely **new** on `confirm()` options. |
44452
45654
  | `BbTable` | `allowSelectAll` (`true`) | `disableSelectAll` (`false`) |
44453
45655
  | `BbTabs` | `animateX` / `animateY` (`true`) | `disableAnimateX` / `disableAnimateY` (`false`) |
44454
45656
  | `BbButton` | `autoLoading` (`false`) | `disableAutoLoading` (`false`) — **⚠ silent behavior flip:** async `@click` handlers now show the loading spinner and disable the button automatically. Pass `disable-auto-loading` to keep v2 behavior. |
@@ -44463,16 +45665,18 @@ to the new name (`hide-close`).
44463
45665
 
44464
45666
  ### 5.3 Straight renames
44465
45667
 
44466
- | Component | v2 | v3 |
44467
- | -------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44468
- | `BbPagination`, `BbTabs` | `querykey` | `queryKey` — **⚠ silent**: a leftover `querykey` falls through to the DOM and the component uses the default key (`'page'` / `'tab'`), quietly moving your URL params. |
44469
- | `BbTooltip` | `timeout` | `delay` |
44470
- | `BbIcon` | `type` (required) | `icon` |
44471
- | `BbColorPalette`, `BbColorInput` | `picker` | `eyeDropper` |
44472
- | `BbCheckboxGroup`, `BbRadioGroup`, `BbSwitchGroup` | `labelPosition` | `legendPosition` |
44473
- | `BbSelect` | `prefill: 'focus'` | `prefill: 'interaction'` (value rename; same default) |
44474
- | `BbToast` (host) | `placement` (e.g. `bottom-start`) | `position` (corner set: `top-left` … `bottom-right`) |
44475
- | `BbAlert`, `BbTooltip`, `toast()` | `theme` | `variant` (typed — see section 4) |
45668
+ | Component | v2 | v3 |
45669
+ | -------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
45670
+ | `BbPagination`, `BbTabs` | `querykey` | `queryKey` — **⚠ silent**: a leftover `querykey` falls through to the DOM and the component uses the default key (`'page'` / `'tab'`), quietly moving your URL params. |
45671
+ | `BbTooltip` | `timeout` | `delay` |
45672
+ | `toast()`, `confirm()` options | `timeout` | `duration` — same meaning (ms before auto-dismiss). `toast()` throws on `duration <= 0`; on `confirm()` it resolves the promise `false` quietly and implies `autoClose: true`. |
45673
+ | `useToast()` | `dismissAll()` | `dismiss()` with **no argument**. The v3 return shape is `{ toast, dismiss, update }`. |
45674
+ | `BbIcon` | `type` (required) | `icon` |
45675
+ | `BbColorPalette`, `BbColorInput` | `picker` | `eyeDropper` |
45676
+ | `BbCheckboxGroup`, `BbRadioGroup`, `BbSwitchGroup` | `labelPosition` | `legendPosition` |
45677
+ | `BbSelect` | `prefill: 'focus'` | `prefill: 'interaction'` (value rename; same default) |
45678
+ | `BbToast` (host) | `placement` (e.g. `bottom-start`) | `position` (corner set: `top-left` … `bottom-right`) |
45679
+ | `BbAlert`, `BbTooltip`, `toast()`, `confirm()` | `theme` | `variant` (typed — see section 4). On `confirm()` the v2 type was a free-form `theme?: string`; v3's is `ConfirmVariantType` (`default`, `destructive`). |
44476
45680
 
44477
45681
  ### 5.4 Removed props (no replacement needed — behavior became default or moved)
44478
45682
 
@@ -44483,7 +45687,18 @@ to the new name (`hide-close`).
44483
45687
  - **`color` everywhere**: gone from `BbAvatar`, `BbIcon`, `BbSpinner`,
44484
45688
  `BbCheckbox`, `BbRadio`, `BbSwitch`, `BbSlider`, `BbRating` and all three
44485
45689
  groups. Components inherit `currentColor` / theme tokens; style via CSS or
44486
- the token knobs.
45690
+ the token knobs. `BbAvatar`'s knobs are named: set **both** `--bg-color` and
45691
+ `--text-color` on `.bb-avatar` (plus `--default-icon-color` for the built-in
45692
+ icon fallback) — setting only the background leaves the initials painted
45693
+ `--bb-primary-fg` on top of it.
45694
+
45695
+ ```css
45696
+ .team-avatar .bb-avatar {
45697
+ --bg-color: #7c3aed;
45698
+ --text-color: #fff;
45699
+ }
45700
+ ```
45701
+
44487
45702
  - **`BbSelect`**: `showChevron` and `updateOnAnimationFrame` removed; the
44488
45703
  `#chevron` slot and the four `#options:prepend/append(:outer)` slots are
44489
45704
  gone (use the new `#header` / `#footer` panel slots). Same options-slot
@@ -44507,7 +45722,15 @@ to the new name (`hide-close`).
44507
45722
  - **`BbPopover`**: `showClose`, `closeLabel`, `restoreFocus`, `block` removed —
44508
45723
  render your own close affordance in the content (`#default="{ close }"`).
44509
45724
  - **`BbDialog`**: `description`, `hideHeader`, `compact`, `overlayClasses`,
44510
- `panelClasses` removed (use the `header` slot and normal classes).
45725
+ `panelClasses` removed (use the `header` slot and normal classes). The
45726
+ `#description` and `#close` **slots** are gone too — v3's slot set is
45727
+ `header | title | default | footer`, and a custom close control goes inside
45728
+ `#header`, which replaces the whole default header including the ✕ (so
45729
+ `hide-close` is not needed alongside it).
45730
+ - **`BbOffCanvas`**: the same `description` prop and the same `#description` /
45731
+ `#close` slots were removed, with the same replacements. The
45732
+ `aria-describedby` wiring `description` used to provide is gone — put the
45733
+ copy in the default slot.
44511
45734
  - **`BbBreadcrumbs`**: string `divider` removed (slot-driven now);
44512
45735
  `dividerWidth` default changed `5` → `16`.
44513
45736
  - **`BbPagination`**: `loading` removed.
@@ -44544,21 +45767,161 @@ to the new name (`hide-close`).
44544
45767
  - **`direction`** on inputs: the exported `InputDirection` union — keyword
44545
45768
  typos now fail to compile; two-word ratio strings (`'1 2'`) still work.
44546
45769
 
44547
- ### 5.6 Renamed CSS blocks
45770
+ ### 5.5b Fields added to public object types
45771
+
45772
+ An **added** optional field breaks a consumer as hard as a removed one, and the
45773
+ error text does not say so. If your app intersects a library object type to
45774
+ carry its own metadata — `BbTableColumn & { sortable?: string }` for a backend
45775
+ sort key, say — the intersection does not widen the property, it **collapses**
45776
+ it: `string & undefined` is `undefined`, and every column literal then fails
45777
+ with
45778
+
45779
+ ```
45780
+ error TS2322: Type 'string' is not assignable to type 'undefined'.
45781
+ ```
45782
+
45783
+ which names neither the field nor the library. That one line is worth
45784
+ searching your build log for. The fix is to rename your field (`sortKey`) or to
45785
+ subtract the library's first:
45786
+ `Omit<BbTableColumn, 'sortable'> & { sortable?: string }`.
44548
45787
 
44549
- If app CSS or test selectors target library internals, these blocks moved:
45788
+ The collision-prone additions in v3:
44550
45789
 
44551
- | v2 | v3 |
44552
- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
44553
- | `.bb-common-input-inner-container` (+ `--clearable`, `__prefix`, `__prepend-icon`, …) | `.common-input-wrapper` — modifiers `--active --compact --disabled --errors --loading --readonly --clearable` (note: no `bb-` prefix) |
44554
- | `.base-btn`, `.base-btn--block`, … | `.bb-base-button*` |
44555
- | `.bb-checkbox-icon` / `.bb-radio-icon` / `.bb-switch-icon` | `.bb-base-checkbox-icon` / `.bb-base-radio-icon` / `.bb-base-switch-icon` |
44556
- | `.bb-chip*` | `.bb-badge*` (component replaced) |
44557
- | `.bb-badge*` used as a floating count/dot | `.bb-indicator*` (component renamed) |
45790
+ | type | added in v3 | hazard |
45791
+ | ----------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45792
+ | `BbTableColumn` | `sortable?: boolean` | intersection collapse (above), **and** a behavior flip: v3 reads it as `!!column.sortable`, so a truthy sort-key string switches on the built-in header sort button. An app with its own sort UI then renders two. Strip the field at the `BbTable` boundary or rename it. |
45793
+ | `BbTableColumn` | `rowClass?: ColumnClasses<Item>` | same collapse. Note this is the **column-level** field — the table-level `rowClass` prop is separate and both accumulate. |
45794
+ | `BbDropdown` item | `description?: string` | collapse, plus it now renders as a second line on the item. |
45795
+ | `BbDropdown` item | `group?: never`, `selectable?: never`, `multiple?: never` | harder than a collapse: these discriminate item-from-group, so `group: 'admin'` on a plain item literal is a **direct** type error, no intersection required. Rename the field or nest your metadata under one custom key. |
45796
+
45797
+ Everything else added in v3 lands on `*Props` / `*Slots` / internal projections
45798
+ — shapes you pass or read rather than intersect — so this list is deliberately
45799
+ short.
45800
+
45801
+ ### 5.6 Renamed CSS blocks
45802
+
45803
+ If app CSS or test selectors target library internals, these blocks moved. This
45804
+ is the complete set — a class-by-class diff of the v2 stylesheet against v3
45805
+ leaves nothing else orphaned. All of it is **silent**: a selector on a class
45806
+ nobody emits matches nothing and throws nothing.
45807
+
45808
+ **Input chrome**
45809
+
45810
+ | v2 | v3 |
45811
+ | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45812
+ | `.bb-common-input-inner-container` (+ `--clearable`, `__prefix`, `__suffix`, `__prepend-icon`, `__append-icon`) | `.common-input-wrapper` (+ the same suffixes) — modifiers `--active --compact --disabled --errors --loading --readonly --clearable --warnings` (note: no `bb-` prefix) |
45813
+ | `.bb-common-input-outer-container` | merged away — the v2 outer/inner pair is one `.common-input-wrapper`, whose inner span is `.common-input-wrapper__inner` |
45814
+ | `.bb-base-input-outer-container` (+ `--compact`) | merged into `.bb-base-input-container` (+ `--compact`) |
45815
+ | `.bb-base-input-container__input` (+ `--left --center --right`) | `.bb-base-input-container__control` (+ the same modifiers) |
45816
+ | `.bb-base-input-container--horizontal` / `--vertical` / `--reverse` (on the root) | `.bb-base-input-container__layout--horizontal` / `--vertical` / `--reverse` — v3 inserts an inner `__layout` element and the orientation modifiers moved onto it; the root keeps only state modifiers (`--disabled --errors --warnings --loading --readonly --compact --floating-label --inside-label` …) |
45817
+ | `.bb-base-input-container--label-mode-floating` | `.bb-base-input-container--floating-label` |
45818
+ | `.bb-base-input-container__errors-outer` | `.bb-base-input-container__errors-wrap` |
45819
+ | `.bb-base-input-container__hint-container` | `.bb-base-input-container__hint-wrap` |
45820
+ | `.base-btn`, `.base-btn--block`, … | `.bb-base-button*` |
45821
+ | `.bb-base-checkbox-container` / `.bb-base-radio-container` / `.bb-base-switch-container` | `.bb-base-checkbox` / `.bb-base-radio` / `.bb-base-switch` (the `--disabled` modifier survives) |
45822
+ | `.bb-base-checkbox-container__icon` / `.bb-base-radio-container__icon` / `.bb-base-switch-container__icon` (+ `__icon-thumb`) | `.bb-base-checkbox-icon` / `.bb-base-radio-icon` / `.bb-base-switch-icon` (+ `.bb-base-switch-icon__thumb`) |
45823
+ | `.bb-cr-container*` — the shared checkbox/radio/switch group tree (root, `--vertical`, `__container`, `-option`, `-option__text`) | split into per-control blocks: `.bb-base-checkbox-group*` / `.bb-base-radio-group*` / `.bb-base-switch-group*`, same suffixes (`--warnings`, `__loading-container`, `__no-data-container`, `-option--selected` are new). **`BbRating` no longer shares the block** — use `.bb-base-rating__inner-container` / `.bb-base-rating__option`. |
45824
+
45825
+ **Dialogs, off-canvas, popovers**
45826
+
45827
+ | v2 | v3 |
45828
+ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45829
+ | `.bb-base-dialog` (+ `--deny-close --fullscreen --open`, `__header __title __body __body-content __footer`) | `.bb-dialog` / `.bb-dialog__*` — 1:1 on every one of those |
45830
+ | `.bb-base-dialog__close` / `__close-icon`, `.bb-offcanvas__close` / `__close-icon`, `.bb-popover__close` | `.bb-close-button` — one shared button, used by the dialog, the off-canvas and the popover alike |
45831
+ | `.bb-common-popover--scrollable` | `.bb-popover__panel--scrollable` (the rest of `.bb-common-popover*` is unchanged) |
45832
+ | `.bb-confirm__no` | **removed** — the cancel button is a plain `BbButton` with no class of its own. Restyle it through `no: { variant: … }` on the call, or through the root's `.bb-confirm--<variant>` modifier. `.bb-confirm__text` and `.bb-confirm__content` are unchanged and still the right targets for body copy. |
45833
+
45834
+ **Tabs** — the whole tree was renamed, and v2's block name was `bb-tab`, not
45835
+ `bb-tabs`, so nothing of a v2 tab theme survives untouched:
45836
+
45837
+ | v2 | v3 |
45838
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45839
+ | `.bb-tab` | `.bb-tabs` |
45840
+ | `.bb-tab--vertical` / `--horizontal` | `.bb-tabs--vertical` / `--horizontal` (+ `.bb-tabs-list--vertical` / `--horizontal` on the strip) |
45841
+ | `.bb-tab__label-boundary` | `.bb-tabs-list` |
45842
+ | `.bb-tab__label-container` (+ `--no-transition`) | `.bb-tabs-list__tablist` (+ `--no-transition`) — the sliding active pill is still this element's `::before`, so a v2 `.bb-tab__label-boundary .bb-tab__label-container:before` rule becomes the single-class `.bb-tabs-list__tablist:before` |
45843
+ | `.bb-tab__btn` (+ `--active`) | `.bb-tabs__trigger` (+ `--active`), with new inner `.bb-tabs__trigger-content` / `.bb-tabs__trigger-label` |
45844
+ | `.bb-tab__pane` (+ `--shown`) | `.bb-tabs__pane` (+ `--shown`) |
45845
+ | `.bb-tab__panes-container` | `.bb-tabs-panes` |
45846
+
45847
+ v2 had no full-width tab strip, so apps hand-rolled one (commonly a
45848
+ `bb-tabs--full-width` class). v3 ships it: `<BbTabs block>` sets
45849
+ `.bb-tabs--block` / `.bb-tabs-list--block` and gives the triggers equal widths.
45850
+ Delete the hand-rolled modifier rather than porting it.
45851
+
45852
+ **Everything else**
45853
+
45854
+ | v2 | v3 |
45855
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45856
+ | `.bb-chip*` | `.bb-badge*` (component replaced) |
45857
+ | `.bb-badge*` used as a floating count/dot (`--dot --floating --positioned --left --bottom --empty`, `__wrapper`, `__content-container`) | `.bb-indicator*` (component renamed) |
45858
+ | `.bb-color-palette__*` (canvas, canvas-thumb, canvas-wrap, controls, eyedropper, preview, preview--alpha, preview-wrap, slider-thumb, slider-track, slider-wrap, slider-wrap--alpha, sliders, swatch, swatch--active, swatch-group, swatches) | `.bb-base-color-palette__*` — 1:1 on every one |
45859
+ | `.bb-toast--top-start` / `--top` / `--top-end` / `--bottom-start` / `--bottom` / `--bottom-end` | `.bb-toast--top-left` / `--top-center` / `--top-right` / `--bottom-left` / `--bottom-center` / `--bottom-right` (matching the `position` prop's corner set) |
45860
+ | `.bb-toast-message__icon-container` | **removed** — style `.bb-toast-message__icon` directly |
45861
+ | `.bb-tree--open` | `.bb-tree__node--expanded` |
45862
+ | `.bb-tree-row` | `.bb-tree__row` |
45863
+ | `.bb-tree-main-content` | `.bb-tree__content` |
45864
+ | `.bb-dropdown-button__dropdown-chevron` | `.bb-dropdown-button__chevron` |
45865
+ | `.bb-dropdown-button--loading` / `__content` / `__icon` / `__prepend-icon` / `__append-icon` | **removed** — the split button delegates to `BbButton`'s internals; target `.bb-button__*` inside it |
45866
+ | `.bb-dropdown__wrapper` | **removed** — the extra wrapper element is gone |
45867
+ | `.bb-select-popover__search-spinner` | `.bb-select-popover__search-icon` (one element, spinner state included) |
45868
+ | `.bb-base-date-picker__header` (the tinted banner holding the month/year controls) | `.bb-base-date-picker__controls` (+ the inner `__nav-center`) — the banner is gone; the controls sit on the calendar surface. `__header-container` and `__header-cell` are unrelated and unchanged: they are the weekday row. |
45869
+ | `.bb-base-date-picker__control` / `__year-control` | `.bb-base-date-picker__nav-btn` |
45870
+ | `.bb-base-date-picker__month-button` / `__year-button` | `.bb-base-date-picker__heading-btn` (+ `--active`) |
45871
+ | `.bb-base-date-picker__weekday` | `.bb-base-date-picker__header-cell` |
45872
+ | `.bb-base-date-picker__monthday` | `.bb-base-date-picker__date` (+ the inner `__date-button`) |
45873
+ | `.bb-base-date-picker__year-container` | `.bb-base-date-picker__year-selector` |
45874
+ | `.bb-base-date-picker__header--hidden`, `__selected-day-label` | **removed** |
45875
+ | `.bb-commabox-item--has-comma` | the select's comma list renders `.bb-base-select__comma-item` (`.bb-commabox-item` itself survives; `--has-comma` and `.bb-chipsbox-item--focused` do not) |
45876
+
45877
+ The date **input**'s own field tree (`.bb-base-date-picker-input__field`,
45878
+ `__fields`, `__placeholder`, `__separator`, `__calendar-btn`) moved to a shared
45879
+ `.bb-segmented-field__*` block — full table in
45880
+ [bb-date-picker-input.md](./components/bb-date-picker-input.md).
45881
+
45882
+ **Removed with no counterpart:** `.bb-base-dialog--compact`,
45883
+ `.bb-offcanvas--compact`, `.bb-base-textarea--compact` (compact now lives on
45884
+ `.common-input-wrapper--compact`), `.bb-base-select--active` / `--compact` /
45885
+ `__inner-wrapper` / `__input-container` (the select activator was rebuilt —
45886
+ nearest hooks are `.bb-base-select__activator`, `__value`, `__display-value`;
45887
+ `--shown` survives), `.bb-base-rating__option--highlighted` (v3 uses
45888
+ `--filled` / `--empty`), `.bb-progress--horizontal` (`.bb-progress` /
45889
+ `.bb-progress-bar` are the blocks), `.bb-ratio` (component removed).
45890
+
45891
+ ### 5.7 Renamed custom properties the library WRITES
45892
+
45893
+ Section 3 covers the tokens you set. These are variables the library **writes
45894
+ onto the DOM** at runtime for you to read — the usual reason to touch them is
45895
+ nested-table column alignment. They all dropped the `bb-` prefix, and every one
45896
+ of them is consumed through `var(name, fallback)`, so a v2 name silently
45897
+ resolves to the fallback instead of failing.
45898
+
45899
+ | v2 | v3 |
45900
+ | ----------------------------------------------------- | -------------------------------------------------------------------------- |
45901
+ | `--bb-table-{id}-track-{key}` | `--table-{id}-track-{key}` |
45902
+ | `--bb-table-offset-start` / `-end` | `--offset-start` / `--offset-end` |
45903
+ | `--bb-table-offset-internal-start` / `-end` | `--offset-internal-start` / `--offset-internal-end` |
45904
+ | `--bb-table-fill` | `--fill` |
45905
+ | `--bb-table-natural-width` | `--natural-width` |
45906
+ | `--bb-select-popover-width` (on `.bb-select-popover`) | `--w` — and the supported path is now the `width` prop, not a CSS override |
45907
+ | `--bb-icon-dimensions-w` (on `.bb-icon`) | `--w` |
45908
+ | `--bb-breadcrumbs-flex-basis` / `-flex-grow` | `--flex-basis` / `--flex-grow` |
45909
+ | `--bb-picker-preview-color` | `--picker-preview-color` |
45910
+
45911
+ A regex matching the old shape (`/--bb-table-[\w-]+-track/`) matches nothing in
45912
+ v3. Search your app for `--bb-table-`, `--bb-select-popover-`,
45913
+ `--bb-icon-dimensions-`, `--bb-breadcrumbs-` and `--bb-picker-` — none of those
45914
+ names exist in v3 and all of them fail quietly. `--available-height`,
45915
+ `--inner-width` and `--overlay-min-height` were dropped outright.
45916
+
45917
+ If you re-published track variables by hand to align a deeply nested table,
45918
+ delete that code: `inherit-column-widths` takes a **table id**
45919
+ (`inherit-column-widths="<ancestor-id>"`), not just `true`, and walks the whole
45920
+ ancestor chain — it is not limited to the immediate parent.
44558
45921
 
44559
45922
  ## 6. Behavior changes you will feel at runtime
44560
45923
 
44561
- No rename to grep for — these change what existing, still-compiling code does.
45924
+ Nothing here fails to compile — these change what existing code does.
44562
45925
 
44563
45926
  1. **`BbButton` tracks async click handlers by default.** An `async @click`
44564
45927
  now disables the button and shows a spinner until it settles. Opt out per
@@ -44584,7 +45947,14 @@ No rename to grep for — these change what existing, still-compiling code does.
44584
45947
  progress bar) instead of flashing to skeleton — and the rows and header go
44585
45948
  `inert` during the refetch. Escape hatch: `interactive-while-loading`. The
44586
45949
  `#loading` slot fires on the **first load only** now.
44587
- 6. **Dynamic slot names are normalized** via
45950
+ 6. **A truthy column `sortable` switches on v3's own sort UI.** `BbTable` reads
45951
+ the field as `!!column.sortable`, so a column carrying a backend sort key
45952
+ (`sortable: 'created_at'`) renders the built-in header sort button and sets
45953
+ `aria-sort` — an app with its own sort control then shows two. Sorting stays
45954
+ a controlled model (`v-model:sort`), so nothing reorders on its own; the
45955
+ button appears and emits. Strip or rename the field at the `BbTable`
45956
+ boundary. The compile-time half of the same collision is in §5.5b.
45957
+ 7. **Dynamic slot names are normalized** via
44588
45958
  `slotKey(key) = key.split(/\W+/g).join('_').toLowerCase()` in
44589
45959
  `BbBreadcrumbs`, `BbTree`, `BbTabs`, and the date-picker day slots.
44590
45960
  `#Order History` → `#order_history`. **⚠ silent**: a stale raw-key slot
@@ -44593,15 +45963,20 @@ No rename to grep for — these change what existing, still-compiling code does.
44593
45963
  normalized slug (`?tab=In%20Review` → `?tab=in_review` — update deep links
44594
45964
  and server code), and tab/panel DOM ids are normalized. Only `v-model` /
44595
45965
  item `key` stay raw.
44596
- 7. **Dialogs, popovers, dropdowns and selects open as bottom sheets on mobile**
45966
+ 8. **Dialogs, popovers, dropdowns and selects open as bottom sheets on mobile**
44597
45967
  by default (`adaptive`, default `true`). Set `adaptive: false` in the
44598
45968
  plugin, or per component, for v2-like floating behavior.
44599
- 8. **`confirm()` buttons**: default labels are localized (no more hard-coded
45969
+ 9. **`confirm()` buttons**: default labels are localized (no more hard-coded
44600
45970
  "OK"/"Annulla") and footer buttons default to `md`.
44601
- 9. **Invalid enum-ish prop values** (`size`, `direction`, `BbSlider.labelMode`)
44602
- now warn in dev and fall back to the documented default instead of
44603
- rendering broken or throwing.
44604
- 10. **`BbTree` is a disclosure list, not an ARIA tree**: the unfulfilled
45971
+ 10. **`confirm()` closes itself now.** `autoClose` was opt-in in v2 — after
45972
+ `resolve`/`reject` the dialog stayed up until you called `close()`. In v3 it
45973
+ defaults to `true`. ⚠ silent: the promise settles exactly as before, the
45974
+ dialog just disappears. A multi-step flow that kept it open between steps
45975
+ has to pass `autoClose: false`.
45976
+ 11. **Invalid enum-ish prop values** (`size`, `direction`, `BbSlider.labelMode`)
45977
+ now warn in dev and fall back to the documented default instead of
45978
+ rendering broken or throwing.
45979
+ 12. **`BbTree` is a disclosure list, not an ARIA tree**: the unfulfilled
44605
45980
  `role="tree"` / `treeitem` / `aria-level` markup is gone, and a collapsed
44606
45981
  branch's children are now `inert` (out of the Tab order). Tests or CSS
44607
45982
  keyed on the removed roles match nothing — see
@@ -44625,6 +46000,23 @@ These keep _rendering_ in v3 (the class hooks still exist), but they are now
44625
46000
  the wrong layer: they bypass the typed registry, silently drift when the
44626
46001
  library renames internals, and duplicate what a prop expresses. Convert them.
44627
46002
 
46003
+ **But the conversion is additive, not neutral.** v2 shipped no variant CSS
46004
+ (§4): those class names carried your rules and nothing else. In v3 each one is
46005
+ a real skin, so every rewrite below slides the library's rules _underneath_
46006
+ yours — background, border, foreground, hover, pressed, focus ring. Wherever
46007
+ your CSS does not override all of them, the appearance drifts. Diff a
46008
+ screenshot; do not assume. Two consequences before you start:
46009
+
46010
+ - **Leaving the class alone is not a safe holding position.** `variant`
46011
+ defaults to `primary`, so in v3 `<BbButton class="bb-button--outline">`
46012
+ renders `bb-button--primary bb-button--outline` — the built-in skin and your
46013
+ class fighting in the cascade. For a pure class override, pass
46014
+ `variant="none"`; it is the one value that emits no variant class.
46015
+ - **A name that collides with a v3 built-in is the dangerous case, not the easy
46016
+ one.** If your app owned the look of `warning` / `destructive` / `secondary`,
46017
+ converting to the same-named `variant` hands that look to the library.
46018
+ Register the name as a custom variant (§4) to keep your CSS the only source.
46019
+
44628
46020
  ### Finding them
44629
46021
 
44630
46022
  Run in the consumer project:
@@ -44635,34 +46027,54 @@ rg -n 'class="[^"]*bb-[a-z-]+--' --type vue
44635
46027
  rg -n ":class=\"[^\"]*'bb-[a-z-]+--" --type vue
44636
46028
  # free-form theme props (removed in v3)
44637
46029
  rg -n '(:?theme)=' --type vue
46030
+ # the same prop as an option-object key — toast()/confirm() live in .ts
46031
+ rg -n 'theme' --type ts --type js
44638
46032
  # raw component markup rebuilt from classes (no <BbX> tag on the same line)
44639
46033
  rg -n 'class="bb-(button|badge|alert|toast-message|tooltip|indicator)\b' --type vue
44640
46034
  ```
44641
46035
 
44642
46036
  ### Correcting them
44643
46037
 
44644
- | Found | Rewrite as |
44645
- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
44646
- | `<BbButton class="bb-button--outline">` | `<BbButton variant="outline">` (same for `secondary`, `ghost`, `destructive`, `link`) |
44647
- | `<BbAlert class="bb-alert--warning">` / v2 `theme="warning"` | `<BbAlert variant="warning">` |
44648
- | `class="bb-badge bb-badge--secondary"` on a `<span>` | `<BbBadge variant="secondary">` — the real component, not markup cosplay |
44649
- | `bb-toast-message--success` styling / v2 `theme` on toasts | `toast({ variant: 'success' })` |
44650
- | `bb-tooltip--destructive` | `<BbTooltip variant="destructive">` / `v-bb-tooltip="{ text, variant: 'destructive' }"` |
44651
- | A **custom** modifier (`bb-button--brand`, any name not in section 4's table) | Register it: `buttonVariants: ['brand']` in the plugin, keep your CSS, pass `variant="brand"`. |
44652
- | A class that overrides _layout_, not look (widths, margins) | Leave it — classes remain the right tool for layout; variants own _appearance semantics_. |
46038
+ | Found | Rewrite as |
46039
+ | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46040
+ | `<BbButton class="bb-button--outline">` | `<BbButton variant="outline">` (same for `secondary`, `ghost`, `destructive`, `link`) |
46041
+ | `<BbAlert class="bb-alert--warning">` / v2 `theme="warning"` | `<BbAlert variant="warning">` |
46042
+ | `class="bb-badge bb-badge--secondary"` on a `<span>` | `<BbBadge variant="secondary">` — the real component, not markup cosplay |
46043
+ | `bb-toast-message--success` styling / v2 `theme` on toasts | `toast({ variant: 'success' })` |
46044
+ | `bb-tooltip--destructive` | `<BbTooltip variant="destructive">` / `v-bb-tooltip="{ text, variant: 'destructive' }"` |
46045
+ | A **custom** modifier (`bb-button--brand`, any name not in section 4's table) | Register it: `buttonVariants: ['brand']` in the plugin, keep your CSS, pass `variant="brand"`. |
46046
+ | A modifier your app **styled itself**, whatever its name | Decide before you convert: accept v3's built-in look and delete your CSS, or register the name (§4) so your CSS stays the sole styling source. Converting silently does the former. |
46047
+ | A **state** modifier that stacks on a variant (`bb-button--pressed` alongside `bb-button--outline`) | Not convertible — `variant` holds exactly one value. Keep it a class and rename it out of `bb-*` (`app-button--pressed`). If it marks a real toggle, `v-model` on `BbButton` owns the state: it sets `aria-pressed` and emits `bb-button--active`, which the library deliberately leaves unstyled for you. |
46048
+ | A class that overrides _layout_, not look (widths, margins) | Leave it — classes remain the right tool for layout; variants own _appearance semantics_. |
44653
46049
 
44654
46050
  Rule of thumb: if the class name encodes a **meaning** (state, emphasis,
44655
46051
  severity), it should be a variant; if it encodes **geometry**, it stays a
44656
46052
  class.
44657
46053
 
46054
+ Whatever stays a class should not stay in the `bb-*` namespace. An app-minted
46055
+ `bb-table--borderless` or `bb-avatar--square` is squatting on the library's
46056
+ prefix — neither name has ever shipped, and the day one does, your rule and the
46057
+ library's collide silently. Rename them into your own prefix.
46058
+
44658
46059
  ## 8. The silent-failure audit
44659
46060
 
44660
46061
  Everything the compiler cannot catch, in one grep pass. Run each in the
44661
46062
  consumer project; every hit needs a decision, not necessarily a change.
44662
46063
 
46064
+ Two surfaces have no other net. **Composable option objects live in `.ts`** —
46065
+ `toast({ … })` and `confirm({ … })` inside stores, interceptors and helpers —
46066
+ and neither gate reaches them: `npx bitboss-ui check` opens only `.vue` and
46067
+ `.md`, and the eslint plugin walks template ASTs. **Nothing reads CSS at all**,
46068
+ not the CLI, not eslint, not `vue-tsc`, not the browser console. The
46069
+ `--type ts --type js` lines and the token/class lines below are the whole
46070
+ safety net for those two. (ripgrep's `js` type already covers `*.vue`, so
46071
+ adding `--type vue` to them is redundant.)
46072
+
46073
+ **Props, slots and markup**
46074
+
44663
46075
  ```bash
44664
46076
  rg -in 'querykey' # → queryKey (URL params silently move)
44665
- rg -n 'show-close|showClose' # → hide-close (inverted) ONLY on BbAlert/BbDialog/BbOffCanvas. On BbPopover and BbTooltip the close button is GONE: delete the prop, never rename it
46077
+ rg -n 'show-close|showClose' # → hide-close (inverted) on BbAlert/BbDialog/BbOffCanvas and in toast() options, which had showClose in v2 too. On BbPopover and BbTooltip the close button is GONE: delete the prop, never rename it
44666
46078
  rg -n 'allow-select-all|allowSelectAll' # → disable-select-all, inverted
44667
46079
  rg -n 'auto-loading|autoLoading' # BbButton behavior flip / rename
44668
46080
  rg -n 'disabled-while-loading|disabledWhileLoading' # removed
@@ -44672,11 +46084,11 @@ rg -n ':flip=|flip="' # → disable-flip on palette/select-p
44672
46084
  rg -n 'hide-arrow|show-arrow|arrow-padding|hideArrow|showArrow|arrowPadding' # removed off tooltips
44673
46085
  rg -n '<BbChip' # component removed → BbBadge clearable
44674
46086
  rg -n '<BbBadge' # v2 badges are v3 indicators — review each
44675
- rg -n '<BbTab[ >]' # → BbTabs
46087
+ rg -n '<BbTab\b' # → BbTabs (\b so a tag name alone on its line still matches, and <BbTabs does not)
44676
46088
  rg -n '#label-' # BbTabs label slot → #label:<slotKey>
44677
- rg -n 'reverse' --type vue # BbRadio/BbSwitch default flipped
44678
- rg -n ':timeout=' --type vue # BbTooltip → delay (BbAvatar keeps timeout)
44679
- rg -n '<BbIcon[^>]*type=' # → icon=, and prefix your own icons `local:` (bb-icon.md)
46089
+ rg -n 'reverse' --type ts --type js # BbRadio/BbSwitch default flipped
46090
+ rg -n 'timeout' --type vue # BbTooltip → delay (BbAvatar keeps timeout); no ':' so static attrs match too
46091
+ rg -nU '<BbIcon[^>]*type=' # → icon=, and prefix your own icons `local:` (bb-icon.md). -U so a multi-line tag matches
44680
46092
  rg -n '(icon|type)="[a-z][a-z0-9-]*"' # bare icon names in templates → `local:<name>` so the Iconify extension previews them
44681
46093
  rg -n 'allow-writing|allowWriting' # → disable-writing, inverted + new tokens
44682
46094
  rg -n 'show-chevron|showChevron|updateOnAnimationFrame|update-on-animation-frame' # removed
@@ -44684,17 +46096,48 @@ rg -n 'label-position|labelPosition' # groups → legend-position
44684
46096
  rg -n 'prefill="focus"|prefill: .focus.' # → 'interaction'
44685
46097
  rg -n ':prefill="false"|prefill: false' # now search-first, not load-on-open
44686
46098
  rg -n 'role="tree|treeitem' # BbTree ARIA roles removed
44687
- rg -n ':filter-by="[^\x27\[]' # function/string filterBy → string[]
46099
+ rg -n 'filter-by|filterBy' # function/string filterBy → string[] (camelCase spelling lives in .ts option objects)
44688
46100
  rg -n 'option:prepend|option:append|options:prepend|options:append|#chevron' # removed slots
44689
46101
  rg -n '<Bb(Checkbox|Radio|Switch|Slider|Rating|Avatar|Icon|Spinner)[^>]*color=' # removed color props
44690
46102
  rg -n 'disabled:' --type ts --type vue # item.disabled near option components → selectable
44691
- rg -n 'picker' --type vue # color input/palette → eye-dropper
44692
- rg -n 'bb-common-input-inner-container' # CSS block → .common-input-wrapper
44693
- rg -n 'IconRegistry|BbToastMessage|useWizard|BaseButtonProps|BaseDialog' # removed exports
46103
+ rg -n 'picker' --type ts --type js # color input/palette → eye-dropper
46104
+ ```
46105
+
46106
+ **Composable option objects and imports (`.ts` / `.js`)**
46107
+
46108
+ ```bash
46109
+ rg -n 'timeout' --type ts --type js # toast()/confirm() options → duration
46110
+ rg -n 'theme' --type ts --type js # toast()/confirm() options → variant
46111
+ rg -n 'dismissAll' # useToast().dismissAll() → dismiss() with no argument. useConfirm() has a NEW dismissAll — check which one each hit is
46112
+ rg -n 'autoClose|auto-close' # confirm(): opt-in in v2, default true in v3 (section 6)
46113
+ rg -n 'yesText|noText|onYes|onNo|hideHeader|setLoading' --type ts --type js # confirm() options collapsed into the yes/no configs
46114
+ rg -n 'BbTableColumn\s*&|DropdownItem\s*&' --type ts # intersections that collapse to `undefined` (§5.5b)
46115
+ rg -n "provide\('icons'|inject\('icons'" # a hand-rolled icon registry is shadowed, not merged, by the plugin's (section 1)
46116
+ rg -n 'IconRegistry|BbToastMessage|useWizard|useQueue|useQuery|useBroadcastChannelInstance|BaseButtonProps|BaseDialog' # removed exports
44694
46117
  rg -n 'setConfig' # only locale survives at runtime
44695
- rg -n -- '--bb-(contrasting|primary-base|hint|icon-color|input-|overlay-|button-|dialog-p)' # retired tokens
44696
46118
  ```
44697
46119
 
46120
+ **CSS: tokens and class names**
46121
+
46122
+ ```bash
46123
+ rg -n -- '--bb-(contrasting|hint|icon-color|primary-(base|dark)|panel-(light|dark|disabled)|border-(light|dark|hover)|text-(light|dark|secondary|tertiary)|input-(color|compact-|font-size|inner-h|prefix(-px)?([^-a-zA-Z0-9]|$))|label-(size|compact-spacing-y)|overlay-(color|opacity|z-index)|dialog-|button-|badge-|checkbox-w|radio-space|switch-|rating-size|base-rating-spacing|slider-|table-|select-option-|select-popover-|progress-track|toast-m|breadcrumbs-|picker-|icon-dimensions-)' # every retired v2 token — v3 declares none of them (sections 3 and 5.7)
46124
+ rg -n -- '--bb-ring-opacity' # survives by name; every value must now carry a % (section 3)
46125
+ rg -n 'bb-base-dialog|bb-tab__|bb-tab--|bb-cr-container|bb-common-input-outer-container|bb-common-input-inner-container|bb-base-input-container__input|bb-base-(checkbox|radio|switch)-container|bb-tree-row|bb-tree-main-content|bb-confirm__no|bb-dropdown__wrapper|bb-offcanvas__close|bb-popover__close|bb-ratio' # renamed or deleted CSS blocks — nothing in v3 emits any of these (§5.6)
46126
+ rg -n 'bb-color-palette__' # → .bb-base-color-palette__*; only __popover survives under the old name
46127
+ rg -n 'bb-tabs--' # v3's own modifiers are --horizontal --vertical --block --compact --disabled; anything else is app-minted, and a hand-rolled full-width tab list is now <BbTabs block>
46128
+ ```
46129
+
46130
+ ⚠ That first pattern is exact on purpose, and the near-misses are the point: a
46131
+ looser one (`input-`, `overlay-`, `dialog-p`) sweeps up tokens that are still
46132
+ live globals, and deleting those overrides shifts the layout silently. All of
46133
+ these survive in v3 under the same name — do not remove them because a pattern
46134
+ matched: `--bb-input-h`, `-px`, `-py`, `-icon`, `-spacing`, `-prefix-w`,
46135
+ `--bb-leading`, `--bb-label-spacing-x` / `-spacing-y`, `--bb-label-weight`,
46136
+ `--bb-ring-size`, `--bb-ring-opacity`, `--bb-arrow`, `--bb-radius`,
46137
+ `--bb-primary`, `--bb-panel`, `--bb-text`, `--bb-border`, `--bb-danger`,
46138
+ `--bb-ring`, `--bb-surface-hover` (and `--bb-overlay-blur`, new in v3). Six of
46139
+ them changed **default value** — the drift table in section 3 says which.
46140
+
44698
46141
  Also review, with no greppable signature: dialog/off-canvas **widths**
44699
46142
  (section 2), the **primary color** (section 3), `adaptive` bottom sheets on
44700
46143
  mobile (section 6), and any UX that relied on second-click-to-clear selects.
@@ -44704,7 +46147,10 @@ mobile (section 6), and any UX that relied on second-click-to-clear selects.
44704
46147
  Detailed old→new tables and edit outlines live in
44705
46148
  [`migration/components/`](./components/). Components not listed have **no
44706
46149
  functional changes** — nothing to migrate beyond the family-wide notes above.
44707
- That no-migration set is: `BbNumberInput`, `BbTextarea`, `BbProgress`.
46150
+ That no-migration set is: `BbNumberInput`, `BbTextarea`, `BbProgress` — with
46151
+ one caveat: the family-wide CSS renames in §5.6 still reach them
46152
+ (`.bb-base-textarea--compact`, `.bb-progress--horizontal` and the input-chrome
46153
+ blocks all moved), so check your stylesheet even for these three.
44708
46154
 
44709
46155
  `BbSmoothHeight` is **not** in it: its `tag` narrowed from any non-void element
44710
46156
  to `'div' | 'span'`, so anything else is now a compile error — see
@@ -44723,11 +46169,12 @@ to `'div' | 'span'`, so anything else is now a compile error — see
44723
46169
  | Selects | [BbSelect](./components/bb-select.md) · [BbSelectPopover](./components/bb-select-popover.md) |
44724
46170
  | Choice controls | [BbCheckbox / BbRadio / BbSwitch](./components/bb-checkbox.md) · [the three groups](./components/bb-checkbox-group.md) |
44725
46171
  | Other inputs | [BbSlider](./components/bb-slider.md) · [BbRating](./components/bb-rating.md) · [BbTag](./components/bb-tag.md) · [BbDropzone](./components/bb-dropzone.md) |
46172
+ | Removed | [BbIntersection](./components/bb-intersection.md) · [useBroadcastChannelInstance](./components/use-broadcast-channel-instance.md) |
44726
46173
 
44727
46174
  ---
44728
46175
 
44729
46176
  <!-- dist/ai/guides/options-items-playbook.md -->
44730
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/options-items-playbook.md -->
46177
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/options-items-playbook.md -->
44731
46178
 
44732
46179
  # Options and Items Playbook
44733
46180
 
@@ -44950,7 +46397,7 @@ When an item is a link, the component navigates — never your code. `BbDropdown
44950
46397
  ---
44951
46398
 
44952
46399
  <!-- dist/ai/guides/page-shell.md -->
44953
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/page-shell.md -->
46400
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/page-shell.md -->
44954
46401
 
44955
46402
  # Page Shell
44956
46403
 
@@ -45216,7 +46663,7 @@ Two things in that rule are load-bearing if you change it:
45216
46663
  ---
45217
46664
 
45218
46665
  <!-- dist/ai/guides/validated-forms.md -->
45219
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/guides/validated-forms.md -->
46666
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/guides/validated-forms.md -->
45220
46667
 
45221
46668
  # Validated Forms
45222
46669
 
@@ -45410,7 +46857,7 @@ rules in `fixtures/vue/app/validators.ts`), asserted by
45410
46857
  ---
45411
46858
 
45412
46859
  <!-- dist/ai/GuidesComponent.md -->
45413
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/GuidesComponent.md -->
46860
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/GuidesComponent.md -->
45414
46861
 
45415
46862
  # GuidesComponent
45416
46863
 
@@ -45437,7 +46884,7 @@ rules in `fixtures/vue/app/validators.ts`), asserted by
45437
46884
  ---
45438
46885
 
45439
46886
  <!-- dist/ai/index.md -->
45440
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/index.md -->
46887
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/index.md -->
45441
46888
 
45442
46889
  # AI Knowledge Base
45443
46890
 
@@ -45656,40 +47103,40 @@ Do not use these components directly in consumer code unless the user explicitly
45656
47103
  - [Installation and Plugin Setup](./guides/installation-and-plugin-setup.md) - Canonical installation, style handling, and full plugin option reference for Vite, Nuxt, and Inertia consumers.
45657
47104
  - [Migration v2 → v3](./guides/migration/v2-to-v3.md) - Complete upgrade guide from bitboss-ui 2.x to 3.0 — plugin-based setup, token overhaul, variant registries, global behavioral changes, and the silent-failure audit playbook.
45658
47105
  - [Migration v2→v3: BbAlert](./guides/migration/components/bb-alert.md) - showClose inverted to hideClose, theme replaced by typed variant.
45659
- - [Migration v2→v3: BbAvatar](./guides/migration/components/bb-avatar.md) - The color prop is removed — the initials fallback is always primary.
47106
+ - [Migration v2→v3: BbAvatar](./guides/migration/components/bb-avatar.md) - The color prop is removed — retheme the fallback through the --bg-color / --text-color locals.
45660
47107
  - [Migration v2→v3: BbBadge (and where your v2 badges went)](./guides/migration/components/bb-badge.md) - The v2 BbBadge (floating count/dot) is now BbIndicator; the v3 BbBadge is a new inline pill that also replaces BbChip.
45661
47108
  - [Migration v2→v3: BbBreadcrumbs](./guides/migration/components/bb-breadcrumbs.md) - Item keys must be strings, the text divider prop is gone, per-item slot names are normalized.
45662
47109
  - [Migration v2→v3: BbButton](./guides/migration/components/bb-button.md) - Async click handlers now auto-track loading (behavior flip), built-in tooltip props removed, variant locked to the registry.
45663
47110
  - [Migration v2→v3: BbCheckbox / BbRadio / BbSwitch](./guides/migration/components/bb-checkbox.md) - reverse polarity inverted (explicit values flip, default layout unchanged), checked narrowed to boolean, color removed, BbRadio's name no longer required.
45664
- - [Migration v2→v3: BbCheckboxGroup / BbRadioGroup / BbSwitchGroup](./guides/migration/components/bb-checkbox-group.md) - labelPosition renamed legendPosition, per-item disabled is now inert (use selectable), color and the option affix slots removed, BbRadioGroup's name no longer required.
47111
+ - [Migration v2→v3: BbCheckboxGroup / BbRadioGroup / BbSwitchGroup](./guides/migration/components/bb-checkbox-group.md) - labelPosition renamed legendPosition, per-item disabled is now inert (use selectable), color and the option affix slots removed, BbRadioGroup's name no longer required, and the shared bb-cr-container CSS block split into one per group.
45665
47112
  - [Migration v2→v3: BbChip (removed)](./guides/migration/components/bb-chip.md) - BbChip is gone — BbBadge with clearable is the replacement; non-clearable chips were badges all along.
45666
47113
  - [Migration v2→v3: BbCollapsible](./guides/migration/components/bb-collapsible.md) - The tag prop is removed; the root is always a div.
45667
47114
  - [Migration v2→v3: BbColorInput](./guides/migration/components/bb-color-input.md) - picker renamed to eyeDropper.
45668
47115
  - [Migration v2→v3: BbColorPalette](./guides/migration/components/bb-color-palette.md) - flip inverted to disableFlip, showArrow removed, picker renamed eyeDropper, update:open dropped, model updates never emit null.
45669
47116
  - [Migration v2→v3: BbDatePickerInput](./guides/migration/components/bb-date-picker-input.md) - allowWriting inverted to disableWriting and hidePopover renamed to disableCalendar, both with a changed token set; the width prop is removed; adaptive mobile sheet by default; non-floating output carries the local offset instead of UTC `Z` (new `utc` prop restores v2 output).
45670
- - [Migration v2→v3: BbDialog](./guides/migration/components/bb-dialog.md) - showClose inverted to hideClose, several presentation props removed, default widths changed, adaptive bottom sheet on mobile by default.
47117
+ - [Migration v2→v3: BbDialog](./guides/migration/components/bb-dialog.md) - showClose inverted to hideClose, several presentation props and the #close/#description slots removed, default widths changed, adaptive bottom sheet on mobile by default, --bb-dialog-* tokens replaced by --bb-panel-p plus locals.
45671
47118
  - [Migration v2→v3: BbDropdown](./guides/migration/components/bb-dropdown.md) - Items now expect text as the label and can carry variants/submenus; arrowPadding removed; adaptive mobile sheet on by default.
45672
47119
  - [Migration v2→v3: BbDropdownButton](./guides/migration/components/bb-dropdown-button.md) - autoLoading renamed to disableAutoLoading, disabledWhileLoading and theme removed.
45673
47120
  - [Migration v2→v3: BbDropzone](./guides/migration/components/bb-dropzone.md) - modelValue is typed, accept no longer throws and takes bare tokens, and the hidden native-submit input only renders when name is set.
45674
47121
  - [Migration v2→v3: BbIcon](./guides/migration/components/bb-icon.md) - The required prop is renamed type→icon, color is removed, and the xxl size key becomes 2xl.
45675
47122
  - [Migration v2→v3: BbIntersection](./guides/migration/components/bb-intersection.md) - Removed with no replacement — it wrapped IntersectionObserver and carried no styling; use @vueuse/core's useIntersectionObserver.
45676
- - [Migration v2→v3: BbOffCanvas](./guides/migration/components/bb-offcanvas.md) - direction→side; showClose→hideClose; size defaults change; adaptive drawer configs unify on offCanvasProps.
47123
+ - [Migration v2→v3: BbOffCanvas](./guides/migration/components/bb-offcanvas.md) - direction→side; showClose→hideClose; description and the #close/#description slots removed; size defaults change; adaptive drawer configs unify on offCanvasProps.
45677
47124
  - [Migration v2→v3: BbPagination](./guides/migration/components/bb-pagination.md) - querykey renamed to queryKey; the loading prop is removed.
45678
47125
  - [Migration v2→v3: BbPopover](./guides/migration/components/bb-popover.md) - Built-in close button, restoreFocus, theme, and the arrow are gone; close affordances move into your content.
45679
- - [Migration v2→v3: BbRating](./guides/migration/components/bb-rating.md) - color removed; name no longer required.
47126
+ - [Migration v2→v3: BbRating](./guides/migration/components/bb-rating.md) - color removed; name no longer required; the star state modifier renamed --highlighted to --filled/--empty; --bb-rating-size demoted to a local --size; input-position finally works.
45680
47127
  - [Migration v2→v3: BbSelect](./guides/migration/components/bb-select.md) - allowWriting inverted to disableWriting, filterBy narrowed to string[], prefill value renamed, chevron/options slots removed, and two behavior changes (second click, mount-time coherence).
45681
47128
  - [Migration v2→v3: BbSelectPopover](./guides/migration/components/bb-select-popover.md) - flip and allowWriting inverted, arrow props removed, filterBy narrowed, options slots replaced by header/footer; shares BbSelect's behavior changes.
45682
47129
  - [Migration v2→v3: BbSlider](./guides/migration/components/bb-slider.md) - thumbTranslate inverted to disableThumbTranslate; string model values and color removed.
45683
47130
  - [Migration v2→v3: BbSmoothHeight](./guides/migration/components/bb-smooth-height.md) - The `tag` prop is narrowed to `div | span`, and the inner measuring element now mirrors it instead of always being a `<div>`.
45684
47131
  - [Migration v2→v3: BbSpinner](./guides/migration/components/bb-spinner.md) - The color prop is removed; spinners follow currentColor.
45685
- - [Migration v2→v3: BbTable](./guides/migration/components/bb-table.md) - allowSelectAll inverted to disableSelectAll; loading no longer blanks populated tables and now inerts rows; #loading is first-load-only; #no-data fills a full-width table cell instead of replacing the row.
45686
- - [Migration v2→v3: BbTabs (was BbTab)](./guides/migration/components/bb-tabs.md) - Component renamed; querykey→queryKey; animations default on; label slots renamed and all dynamic names/URL slugs normalized.
47132
+ - [Migration v2→v3: BbTable](./guides/migration/components/bb-table.md) - allowSelectAll inverted to disableSelectAll; loading no longer blanks populated tables and now inerts rows; #loading is first-load-only; #no-data fills a full-width table cell instead of replacing the row; columns gained sortable + rowClass, which collide with same-named consumer fields.
47133
+ - [Migration v2→v3: BbTabs (was BbTab)](./guides/migration/components/bb-tabs.md) - Component renamed; querykey→queryKey; animations default on; label slots renamed and all dynamic names/URL slugs normalized; the entire .bb-tab BEM tree renamed to .bb-tabs / .bb-tabs-list / .bb-tabs-panes.
45687
47134
  - [Migration v2→v3: BbTag](./guides/migration/components/bb-tag.md) - modelValue is strictly string[] now, multiple is removed, and duplicates are rejected case-insensitively by default.
45688
- - [Migration v2→v3: BbTextInput](./guides/migration/components/bb-text-input.md) - The type prop narrows to the text-like input types — number/date values must move to their dedicated components.
47135
+ - [Migration v2→v3: BbTextInput](./guides/migration/components/bb-text-input.md) - The type prop narrows to the text-like input types — number/date values must move to their dedicated components; a bare `floating` attribute was never a prop, the spelling is label-mode="floating".
45689
47136
  - [Migration v2→v3: BbToast and toast()](./guides/migration/components/bb-toast.md) - Host placement becomes position with a corner-based value set; toast() options rewritten (variant, duration, actions, portal).
45690
47137
  - [Migration v2→v3: BbTooltip](./guides/migration/components/bb-tooltip.md) - timeout renamed to delay; theme replaced by typed variant; hideArrow is now a declared prop; block removed; modelValue is new.
45691
47138
  - [Migration v2→v3: BbTree](./guides/migration/components/bb-tree.md) - Item model rewritten — payload moves under meta, open becomes expanded, identifier becomes itemValue, expansion is a v-model.
45692
- - [Migration v2→v3: confirm() / useConfirm](./guides/migration/components/use-confirm.md) - yesText/onYes/noText/onNo collapse into yes/no configs, actions becomes an array-or-false, labels are localized, footer buttons default md.
47139
+ - [Migration v2→v3: confirm() / useConfirm](./guides/migration/components/use-confirm.md) - yesText/onYes/noText/onNo collapse into yes/no configs, actions becomes an array-or-false, theme→variant, timeout→duration, autoClose flips to default-true, labels are localized, footer buttons default md.
45693
47140
  - [Migration v2→v3: useBroadcastChannelInstance](./guides/migration/components/use-broadcast-channel-instance.md) - Removed — cross-tab messaging is not a UI concern; use useBroadcastChannel from @vueuse/core.
45694
47141
  - [Options and Items Playbook](./guides/options-items-playbook.md) - How item-driven components turn raw domain items into options — mapping, value identity, model seeding, and the supported ways to disable choices.
45695
47142
  - [Page Shell](./guides/page-shell.md) - App-side page layout — topbar, content, and a right details sidebar driven by a stack-based context (push a record to peek it, pop to go back). The implementation ships in the per-platform Layout Scaffold recipe; this guide covers when and how to use it.
@@ -45698,7 +47145,7 @@ Do not use these components directly in consumer code unless the user explicitly
45698
47145
  ---
45699
47146
 
45700
47147
  <!-- dist/ai/ListBox.md -->
45701
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/ListBox.md -->
47148
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/ListBox.md -->
45702
47149
 
45703
47150
  # ListBox
45704
47151
 
@@ -45745,7 +47192,7 @@ Do not use these components directly in consumer code unless the user explicitly
45745
47192
  ---
45746
47193
 
45747
47194
  <!-- dist/ai/OptionsContainer.md -->
45748
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/OptionsContainer.md -->
47195
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/OptionsContainer.md -->
45749
47196
 
45750
47197
  # OptionsContainer
45751
47198
 
@@ -45787,7 +47234,7 @@ Do not use these components directly in consumer code unless the user explicitly
45787
47234
  ---
45788
47235
 
45789
47236
  <!-- dist/ai/recipes/inertia/approvals-inbox.md -->
45790
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/approvals-inbox.md -->
47237
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/approvals-inbox.md -->
45791
47238
 
45792
47239
  # Recipe: Approvals Inbox (Inertia)
45793
47240
 
@@ -47439,7 +48886,7 @@ function` — identical call sites to the Vue/Nuxt recipes,
47439
48886
  ---
47440
48887
 
47441
48888
  <!-- dist/ai/recipes/inertia/command-palette.md -->
47442
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/command-palette.md -->
48889
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/command-palette.md -->
47443
48890
 
47444
48891
  # Recipe: Command Palette (Inertia)
47445
48892
 
@@ -47751,7 +49198,7 @@ const { open } = useCommandPalette();
47751
49198
  ---
47752
49199
 
47753
49200
  <!-- dist/ai/recipes/inertia/inline-edit-workspace.md -->
47754
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/inline-edit-workspace.md -->
49201
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/inline-edit-workspace.md -->
47755
49202
 
47756
49203
  # Recipe: Inline-Edit Workspace (Inertia)
47757
49204
 
@@ -49563,7 +51010,7 @@ const longDate = (value: string) =>
49563
51010
  ---
49564
51011
 
49565
51012
  <!-- dist/ai/recipes/inertia/layout-scaffold.md -->
49566
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/layout-scaffold.md -->
51013
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/layout-scaffold.md -->
49567
51014
 
49568
51015
  <!--
49569
51016
  GENERATED FILE — do not edit by hand.
@@ -50301,7 +51748,7 @@ the current page (mobile nav, global filters).
50301
51748
  ---
50302
51749
 
50303
51750
  <!-- dist/ai/recipes/inertia/onboarding.md -->
50304
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/onboarding.md -->
51751
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/onboarding.md -->
50305
51752
 
50306
51753
  # Recipe: Workspace Onboarding (Inertia)
50307
51754
 
@@ -52093,7 +53540,7 @@ OnboardingState::for($request->user())])`) and drop the workspace-wide
52093
53540
  ---
52094
53541
 
52095
53542
  <!-- dist/ai/recipes/inertia/ownership-atlas.md -->
52096
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/ownership-atlas.md -->
53543
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/ownership-atlas.md -->
52097
53544
 
52098
53545
  # Recipe: Ownership Atlas (Inertia)
52099
53546
 
@@ -53483,7 +54930,7 @@ export interface OwnerDetail extends Owner {
53483
54930
  ---
53484
54931
 
53485
54932
  <!-- dist/ai/recipes/inertia/record-form.md -->
53486
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/record-form.md -->
54933
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/record-form.md -->
53487
54934
 
53488
54935
  # Recipe: Record Form (Inertia)
53489
54936
 
@@ -54973,7 +56420,7 @@ const currency = new Intl.NumberFormat('en-US', {
54973
56420
  ---
54974
56421
 
54975
56422
  <!-- dist/ai/recipes/inertia/records-workspace.md -->
54976
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/records-workspace.md -->
56423
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/records-workspace.md -->
54977
56424
 
54978
56425
  # Recipe: Records Workspace (Inertia)
54979
56426
 
@@ -56616,7 +58063,7 @@ If a page genuinely has no peek, delete the shell wiring rather than leaving
56616
58063
  ---
56617
58064
 
56618
58065
  <!-- dist/ai/recipes/inertia/upload-center.md -->
56619
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/upload-center.md -->
58066
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/upload-center.md -->
56620
58067
 
56621
58068
  # Recipe: Upload Center (Inertia)
56622
58069
 
@@ -58628,7 +60075,7 @@ function` — identical call sites to the Vue/Nuxt recipes,
58628
60075
  ---
58629
60076
 
58630
60077
  <!-- dist/ai/recipes/inertia/wizard-form.md -->
58631
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/inertia/wizard-form.md -->
60078
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/inertia/wizard-form.md -->
58632
60079
 
58633
60080
  # Recipe: Wizard Form (Inertia)
58634
60081
 
@@ -60129,7 +61576,7 @@ const longDate = (value: string) =>
60129
61576
  ---
60130
61577
 
60131
61578
  <!-- dist/ai/recipes/nuxt/approvals-inbox.md -->
60132
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/approvals-inbox.md -->
61579
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/approvals-inbox.md -->
60133
61580
 
60134
61581
  # Recipe: Approvals Inbox (Nuxt)
60135
61582
 
@@ -61718,7 +63165,7 @@ const currency = new Intl.NumberFormat('en-US', {
61718
63165
  ---
61719
63166
 
61720
63167
  <!-- dist/ai/recipes/nuxt/command-palette.md -->
61721
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/command-palette.md -->
63168
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/command-palette.md -->
61722
63169
 
61723
63170
  # Recipe: Command Palette (Nuxt)
61724
63171
 
@@ -62019,7 +63466,7 @@ const { open } = useCommandPalette();
62019
63466
  ---
62020
63467
 
62021
63468
  <!-- dist/ai/recipes/nuxt/inline-edit-workspace.md -->
62022
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/inline-edit-workspace.md -->
63469
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/inline-edit-workspace.md -->
62023
63470
 
62024
63471
  # Recipe: Inline-Edit Workspace (Nuxt)
62025
63472
 
@@ -63773,7 +65220,7 @@ const longDate = (value: string) =>
63773
65220
  ---
63774
65221
 
63775
65222
  <!-- dist/ai/recipes/nuxt/layout-scaffold.md -->
63776
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/layout-scaffold.md -->
65223
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/layout-scaffold.md -->
63777
65224
 
63778
65225
  <!--
63779
65226
  GENERATED FILE — do not edit by hand.
@@ -64511,7 +65958,7 @@ the current page (mobile nav, global filters).
64511
65958
  ---
64512
65959
 
64513
65960
  <!-- dist/ai/recipes/nuxt/onboarding.md -->
64514
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/onboarding.md -->
65961
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/onboarding.md -->
64515
65962
 
64516
65963
  # Recipe: Workspace Onboarding (Nuxt)
64517
65964
 
@@ -66236,7 +67683,7 @@ const previews: Record<StepId, StepPreview> = {
66236
67683
  ---
66237
67684
 
66238
67685
  <!-- dist/ai/recipes/nuxt/record-form.md -->
66239
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/record-form.md -->
67686
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/record-form.md -->
66240
67687
 
66241
67688
  # Recipe: Record Form (Nuxt)
66242
67689
 
@@ -67739,7 +69186,7 @@ const currency = new Intl.NumberFormat('en-US', {
67739
69186
  ---
67740
69187
 
67741
69188
  <!-- dist/ai/recipes/nuxt/records-workspace.md -->
67742
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/records-workspace.md -->
69189
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/records-workspace.md -->
67743
69190
 
67744
69191
  # Recipe: Records Workspace (Nuxt)
67745
69192
 
@@ -69300,7 +70747,7 @@ If a page genuinely has no peek, delete the shell wiring rather than leaving
69300
70747
  ---
69301
70748
 
69302
70749
  <!-- dist/ai/recipes/nuxt/upload-center.md -->
69303
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/upload-center.md -->
70750
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/upload-center.md -->
69304
70751
 
69305
70752
  # Recipe: Upload Center (Nuxt)
69306
70753
 
@@ -71195,7 +72642,7 @@ void ctx.rows.value; … })` re-fetches its own JSON detail for free — the
71195
72642
  ---
71196
72643
 
71197
72644
  <!-- dist/ai/recipes/nuxt/wizard-form.md -->
71198
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/nuxt/wizard-form.md -->
72645
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/nuxt/wizard-form.md -->
71199
72646
 
71200
72647
  # Recipe: Wizard Form (Nuxt)
71201
72648
 
@@ -72705,7 +74152,7 @@ const longDate = (value: string) =>
72705
74152
  ---
72706
74153
 
72707
74154
  <!-- dist/ai/recipes/vue/approvals-inbox.md -->
72708
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/approvals-inbox.md -->
74155
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/approvals-inbox.md -->
72709
74156
 
72710
74157
  # Recipe: Approvals Inbox (Vue)
72711
74158
 
@@ -74212,7 +75659,7 @@ const currency = new Intl.NumberFormat('en-US', {
74212
75659
  ---
74213
75660
 
74214
75661
  <!-- dist/ai/recipes/vue/command-palette.md -->
74215
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/command-palette.md -->
75662
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/command-palette.md -->
74216
75663
 
74217
75664
  # Recipe: Command Palette (Vue)
74218
75665
 
@@ -74510,7 +75957,7 @@ const { open } = useCommandPalette();
74510
75957
  ---
74511
75958
 
74512
75959
  <!-- dist/ai/recipes/vue/inline-edit-workspace.md -->
74513
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/inline-edit-workspace.md -->
75960
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/inline-edit-workspace.md -->
74514
75961
 
74515
75962
  # Recipe: Inline-Edit Workspace (Vue)
74516
75963
 
@@ -76156,7 +77603,7 @@ const longDate = (value: string) =>
76156
77603
  ---
76157
77604
 
76158
77605
  <!-- dist/ai/recipes/vue/layout-scaffold.md -->
76159
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/layout-scaffold.md -->
77606
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/layout-scaffold.md -->
76160
77607
 
76161
77608
  <!--
76162
77609
  GENERATED FILE — do not edit by hand.
@@ -76894,7 +78341,7 @@ the current page (mobile nav, global filters).
76894
78341
  ---
76895
78342
 
76896
78343
  <!-- dist/ai/recipes/vue/onboarding.md -->
76897
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/onboarding.md -->
78344
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/onboarding.md -->
76898
78345
 
76899
78346
  # Recipe: Workspace Onboarding (Vue)
76900
78347
 
@@ -78574,7 +80021,7 @@ const previews: Record<StepId, StepPreview> = {
78574
80021
  ---
78575
80022
 
78576
80023
  <!-- dist/ai/recipes/vue/record-form.md -->
78577
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/record-form.md -->
80024
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/record-form.md -->
78578
80025
 
78579
80026
  # Recipe: Record Form (Vue)
78580
80027
 
@@ -80161,7 +81608,7 @@ const currency = new Intl.NumberFormat('en-US', {
80161
81608
  ---
80162
81609
 
80163
81610
  <!-- dist/ai/recipes/vue/records-workspace.md -->
80164
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/records-workspace.md -->
81611
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/records-workspace.md -->
80165
81612
 
80166
81613
  # Recipe: Records Workspace (Vue)
80167
81614
 
@@ -81638,7 +83085,7 @@ If a page genuinely has no peek, delete the shell wiring rather than leaving
81638
83085
  ---
81639
83086
 
81640
83087
  <!-- dist/ai/recipes/vue/upload-center.md -->
81641
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/upload-center.md -->
83088
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/upload-center.md -->
81642
83089
 
81643
83090
  # Recipe: Upload Center (Vue)
81644
83091
 
@@ -83492,7 +84939,7 @@ const currency = new Intl.NumberFormat('en-US', {
83492
84939
  ---
83493
84940
 
83494
84941
  <!-- dist/ai/recipes/vue/wizard-form.md -->
83495
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/recipes/vue/wizard-form.md -->
84942
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/recipes/vue/wizard-form.md -->
83496
84943
 
83497
84944
  # Recipe: Wizard Form (Vue)
83498
84945
 
@@ -84934,7 +86381,7 @@ const longDate = (value: string) =>
84934
86381
  ---
84935
86382
 
84936
86383
  <!-- dist/ai/WarningIcon.md -->
84937
- <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@alpha/dist/ai/WarningIcon.md -->
86384
+ <!-- https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/WarningIcon.md -->
84938
86385
 
84939
86386
  # WarningIcon
84940
86387