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
@@ -17,39 +17,26 @@ the per-component guides under [`migration/components/`](./components/) —
17
17
  only components with **functional** changes have one; if a component has no
18
18
  guide there, nothing about it needs migration.
19
19
 
20
+ **Which version to migrate to.** v3 is still a prerelease, so install it by
21
+ dist-tag:
22
+
23
+ ```bash
24
+ npm install bitboss-ui@beta
25
+ ```
26
+
27
+ `@beta` is the current v3 channel. Do **not** pin an exact alpha: the alpha line
28
+ is frozen and several of its version numbers were tagged but never published, so
29
+ `npm install bitboss-ui@3.0.0-alpha.8` fails outright with `ETARGET`. And a bare
30
+ `npm install bitboss-ui` resolves to **v2** — `@latest` stays on the 2.x line
31
+ until v3 goes stable. Verify with `npm ls bitboss-ui` before you change a single
32
+ file; if it reports `2.x`, nothing below applies.
33
+
20
34
  **How to read the tables:** every row is a hard break — old names are gone,
21
35
  with no alias and no deprecation shim. Where the break is _silent_ (the app
22
36
  compiles and runs but behaves differently), the row is marked **⚠ silent**;
23
37
  these are the rows you cannot find by waiting for errors, so grep for them
24
38
  explicitly (section 8).
25
39
 
26
- ## For agents: surface what this guide misses
27
-
28
- This guide is the contract for the upgrade. If during a migration you hit
29
- something it does not cover, **do not silently work around it** — a quiet
30
- workaround hides a gap that will bite the next project. Report it to the
31
- user in your migration summary, clearly separated from the routine edits.
32
-
33
- Reportable findings, in order of severity:
34
-
35
- 1. **An undocumented break** — v2 code that compiled or behaved one way and
36
- changes in v3 without a matching entry here or in the component's guide.
37
- 2. **A mapping that doesn't hold** — you applied a documented old→new edit
38
- and the result compiles but renders or behaves differently than the guide
39
- promises.
40
- 3. **An ambiguity you had to guess on** — two plausible migrations for the
41
- same v2 usage and the guide doesn't say which is intended.
42
- 4. **A pattern with no v3 home** — a v2 usage (prop combination, slot trick,
43
- CSS hook) whose replacement you could not find at all.
44
-
45
- For each finding include: the component, the exact v2 usage (snippet), what
46
- you expected from the guide, and what actually happened. Recommend that the
47
- user forwards it to the bitboss-ui maintainers so the guide gets a row —
48
- these reports are how this document stays truthful.
49
-
50
- Routine work — renames that behaved exactly as the tables say — belongs in
51
- the normal summary, not in this report. The report is only for surprises.
52
-
53
40
  ## 0. Upgrade strategy — do it in this order
54
41
 
55
42
  1. **Swap the setup** (section 1): install the build plugin and the runtime
@@ -135,11 +122,54 @@ Migration deltas, one by one:
135
122
 
136
123
  | v2 | v3 |
137
124
  | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
138
- | `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`. |
125
+ | `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. |
139
126
  | 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. |
140
127
  | `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). |
141
128
  | No reset stylesheet | Optional: `resetCss: true` or `import 'bitboss-ui/reset.css'`. Skip in Tailwind apps — Preflight already does this. |
142
129
  | 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. |
130
+ | 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. |
131
+
132
+ ### ⚠ silent — a hand-rolled `provide('icons', …)` is shadowed, not merged
133
+
134
+ `bitbossUiPlugin` provides the icon registry under two keys: a Symbol
135
+ (`bitboss-ui:icons`) and the legacy string key `'icons'` — the same key an app
136
+ icon plugin typically uses. `BbIcon` reads the **Symbol first**
137
+ (`inject(ICONS_INJECTION_KEY) ?? inject('icons')`), so once the library plugin
138
+ is installed your own `provide('icons', …)` map never wins. Any glyph that
139
+ existed only in that map stops resolving, and nothing warns. Move those SVGs
140
+ into `iconDir` and reference them as `local:<name>`.
141
+
142
+ App code that still calls `inject('icons')` now receives the **library's**
143
+ registry object, not yours. The string key is a one-release compatibility shim
144
+ — migrate those call sites off it or delete them.
145
+
146
+ ### ⚠ Tailwind Preflight cannot load before the library when styles are injected
147
+
148
+ Auto-injection always puts the library sheet at the **top** of `<head>` — the
149
+ guaranteed order is reset → library styles → every other stylesheet, because
150
+ the injector inserts before the first existing `<style>` / `<link
151
+ rel="stylesheet">`. Preflight is an app sheet like any other, so it lands
152
+ _after_ the library and wins every specificity tie. There is no option to
153
+ inject later.
154
+
155
+ If your v2 app depended on the opposite order (the manual
156
+ `import 'bitboss-ui/styles.css'` placed after your Tailwind entry), keep the
157
+ manual import and turn injection off:
158
+
159
+ ```ts
160
+ // vite.config.ts
161
+ bitbossUi({ iconDir: './src/assets/icons', injectStyles: false });
162
+ ```
163
+
164
+ ```ts
165
+ // main.ts — the order is yours again
166
+ import './app.css'; // your Tailwind entry (@import 'tailwindcss')
167
+ import 'bitboss-ui/styles.css'; // after Tailwind, so library rules win the ties
168
+ ```
169
+
170
+ Leave `resetCss` at its `false` default here — Preflight already resets.
171
+ `injectStyles` controls only whether the library injects its own `<style>`;
172
+ nothing else about the plugin changes.
143
173
 
144
174
  ### Nuxt
145
175
 
@@ -270,24 +300,36 @@ scope instead.
270
300
  explicitly.
271
301
  - `--bb-radius` went `4px` → `8px`.
272
302
 
303
+ Six more tokens changed value **without changing name**, so they are invisible
304
+ to the rename map and to every grep below — see
305
+ ["Tokens that kept their name but changed"](#tokens-that-kept-their-name-but-changed).
306
+
273
307
  ### Token rename map
274
308
 
275
- | v2 override | v3 replacement |
276
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
277
- | `--bb-primary-base` | deleted — set `--bb-primary` directly |
278
- | `--bb-contrasting` / `--bb-contrasting-dark` | `--bb-primary-fg` |
279
- | `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
280
- | `--bb-hint`, `--bb-icon-color`, `--bb-placeholder`, `--bb-prefix-color`, `--bb-muted-color` | `--bb-text-muted` |
281
- | `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
282
- | `--bb-input-font-size`, `--bb-input-mobile-font-size` | `--bb-input-fs`, `--bb-input-fs-mobile` |
283
- | `--bb-overlay-color` + `--bb-overlay-opacity` | `--bb-overlay` (one color with alpha) |
284
- | `--bb-overlay-z-index` | `--bb-z-overlay` |
285
- | `--bb-easing-bezier` | `--bb-ease` |
286
- | `--bb-dialog-px/pt/pb/gap` | `--bb-panel-p` (surfaces share one padding token) |
287
- | `--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. |
288
-
289
- **When a v2 global becomes a component token, resize only what you use.** The
290
- row above is the trap: a v2 global like `--bb-button-icon` had ONE value for
309
+ | v2 override | v3 replacement |
310
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
311
+ | `--bb-primary-base` | deleted — set `--bb-primary` directly |
312
+ | `--bb-contrasting` / `--bb-contrasting-dark` | `--bb-primary-fg` |
313
+ | `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
314
+ | `--bb-hint`, `--bb-icon-color`, `--bb-placeholder`, `--bb-prefix-color`, `--bb-muted-color` | `--bb-text-muted` |
315
+ | `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
316
+ | `--bb-input-font-size`, `--bb-input-mobile-font-size` | `--bb-input-fs`, `--bb-input-fs-mobile` |
317
+ | `--bb-overlay-color` + `--bb-overlay-opacity` | `--bb-overlay` (one color with alpha) |
318
+ | `--bb-overlay-z-index` | `--bb-z-overlay` |
319
+ | _(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. |
320
+ | `--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. |
321
+ | `--bb-dialog-close` | **delete it** — do not map it onto `.bb-close-button { --size }`; v3's default already reproduces the v2 control. See below. |
322
+ | `--bb-dialog-title-size` | the local `--dialog-title-fs` on `.bb-dialog` / `.bb-offcanvas` (default `calc(var(--bb-fs) + 2px)`) |
323
+ | `--bb-dialog-title-weight` | **no knob** — the title is hardcoded `font-weight: 500`. Override with a CSS rule, see below. |
324
+ | `--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). |
325
+
326
+ **`--bb-input-*` and `--bb-label-*` are NOT in the row above.** They are still
327
+ global v3 theme tokens, declared in `variables.css` and read across the whole
328
+ form family — do not delete them. Several changed their default value; the
329
+ drift table below lists which.
330
+
331
+ **When a v2 global becomes a component token, resize only what you use.** That
332
+ last row is the trap: a v2 global like `--bb-button-icon` had ONE value for
291
333
  every button because v2 had no size scale. v3 does, so the same value now lives
292
334
  in a per-size family (`--icon-size-xs … --icon-size-2xl`) that the size class
293
335
  re-points. Two mistakes follow, and both were made on a real migration:
@@ -305,11 +347,173 @@ doctrine in `design-tokens.md` § "Overriding one from a consumer app". And chec
305
347
  first whether the v2 value was a real customisation: if it merely restated v2's
306
348
  default, the migration is to **delete it** and inherit the v3 scale.
307
349
 
350
+ **Asymmetric surface padding.** `--bb-panel-p` is a single value used on both
351
+ axes. If your v2 theme set `--bb-dialog-px` ≠ `--bb-dialog-py`, set
352
+ `--bb-panel-p` to the **horizontal** value (it also positions the close button)
353
+ and override the vertical with a normal `padding` rule on the part classes.
354
+ There is no `--bb-panel-px` / `--bb-panel-py` pair.
355
+
356
+ ```css
357
+ /* v2: --bb-dialog-px: 24px; header --bb-dialog-py: 10px; body --bb-dialog-py: 20px */
358
+ .my-dialog {
359
+ --bb-panel-p: 24px; /* horizontal + close-button inset */
360
+ }
361
+ .my-dialog .bb-dialog__header {
362
+ padding-block: 10px 5px;
363
+ }
364
+ .my-dialog .bb-dialog__body .bb-dialog__body-content {
365
+ padding-block: 0 20px;
366
+ }
367
+ .my-dialog .bb-dialog__body:first-child .bb-dialog__body-content {
368
+ padding-top: 20px;
369
+ }
370
+ .my-dialog .bb-dialog__footer {
371
+ padding-block: 20px;
372
+ }
373
+ ```
374
+
375
+ Use `padding-block`, not the `padding` shorthand, so the horizontal half keeps
376
+ tracking `--bb-panel-p`. Specificity: the library rules are
377
+ `.bb-dialog .bb-dialog__header` (0,2,0), `.bb-dialog__body-content` (0,1,0) and
378
+ `.bb-dialog__body:first-child .bb-dialog__body-content` (0,2,1) — the
379
+ three-class app selectors above beat all of them. `BbOffCanvas` is identical
380
+ with `.bb-offcanvas__*`.
381
+
382
+ **`--px` and `--pt` are not dialog knobs.** `.bb-dialog` declares them, but
383
+ nothing in the dialog or off-canvas CSS reads them, and every component that
384
+ _does_ read `--px` (`BbButton`, `BbAlert`, `BbTooltip`, `BbBaseSlider`)
385
+ re-declares it on its own root class, so an ancestor value never reaches those
386
+ either. Setting `--px` on a dialog part is inert — it silently leaves the
387
+ horizontal padding at whatever `--bb-panel-p` resolves to.
388
+
389
+ If only the **vertical rhythm between parts** differs, the local `--dialog-gap`
390
+ (default `--bb-panel-p`) already does it without a padding rule: it drives the
391
+ gap below the body content and the footer's block padding on `.bb-dialog` and
392
+ `.bb-offcanvas`. It does **not** cover the header's top padding, or the body's
393
+ top padding in the headerless case — those still take `--bb-panel-p`.
394
+
395
+ **`--bb-dialog-close` maps to nothing — delete it.** In v2 it was the close
396
+ **icon** width (`12px`) and the button around it added `8px` of padding, so the
397
+ control was 28px. v3 inverts the relationship: `.bb-close-button { --size }` is
398
+ the whole control box and the icon is derived as `max(--size - 2 × --p, 10px)`.
399
+ Inside a dialog v3 already ships `--size: 28px; --p: 7px` (a 14px glyph) — the
400
+ same 28px control v2's default produced. Feeding v2's `12px` into `--size`
401
+ yields a **12×12px** control, well under the WCAG 2.5.8 minimum target size. If
402
+ you must match the old glyph exactly, widen the padding instead of shrinking
403
+ the box:
404
+
405
+ ```css
406
+ /* 12px glyph, 28px control */
407
+ .my-dialog .bb-dialog__header > .bb-close-button {
408
+ --p: 8px;
409
+ }
410
+ ```
411
+
412
+ That selector needs at least three classes to beat the library's
413
+ `.bb-dialog .bb-dialog__header > .bb-close-button` (0,3,0).
414
+
415
+ **`--bb-dialog-title-weight` has no v3 home.** The title is `font-weight: 500`,
416
+ hardcoded. Restore v2's `600` with a rule that outweighs the library's
417
+ `.bb-dialog .bb-dialog__header .bb-dialog__title` (0,3,0):
418
+
419
+ ```css
420
+ .my-dialog .bb-dialog__header .bb-dialog__title {
421
+ font-weight: 600;
422
+ }
423
+ ```
424
+
308
425
  A complete v3 theme is typically two small blocks of ~10 declarations
309
426
  (`--bb-primary`, `--bb-radius`, `--bb-control-h`, `--bb-panel`, `--bb-text`,
310
427
  `--bb-border`, `--bb-danger`, …) — one in `:root, .light`, the changed knobs
311
428
  again in `.dark`. If your v2 theme was long, most of it maps to "delete".
312
429
 
430
+ ### Tokens that kept their name but changed
431
+
432
+ The rename map above is keyed by _the name you must change_. These tokens kept
433
+ their v2 name and stayed global, so they appear in no rename row and no grep
434
+ finds them — the only symptom is that the app renders differently.
435
+
436
+ **Values that drifted.** Same name, same meaning, new default:
437
+
438
+ | token | v2 | v3 |
439
+ | --------------------- | ------------------------------------- | ----------------------------------------------------------------------------- |
440
+ | `--bb-input-h` | `36px` | `32px` (via `--bb-control-h`) |
441
+ | `--bb-input-py` | `4px` | `3px` |
442
+ | `--bb-input-prefix-w` | `40px` | `20px` |
443
+ | `--bb-label-weight` | `400` | `500` |
444
+ | `--bb-ring-size` | `4px` | `2px` |
445
+ | `--bb-leading` | `24px` (`--bb-input-font-size` × 1.5) | `21px` (`--bb-input-fs` × 1.5, and `--bb-input-fs` is now `14px`, not `16px`) |
446
+
447
+ The consequence cuts both ways, which is what makes it easy to get wrong: an
448
+ app that **carried its v2 theme forward** keeps the v2 look (36px inputs, 4px
449
+ rings) and never sees v3's proportions; an app that **deleted its theme as
450
+ redundant** silently shifts to shorter inputs, half-width prefix columns and a
451
+ half-thickness focus ring. Re-check each of these six against the v3 default
452
+ and delete only the declarations that merely restated a **v2** default.
453
+
454
+ **A type that narrowed.** `--bb-ring-opacity` survives by name but v3 registers
455
+ it with `@property … syntax: '<percentage>'`:
456
+
457
+ | token | v2 form | v3 form | symptom |
458
+ | ------------------- | -------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
459
+ | `--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. |
460
+
461
+ Nothing reports this — not `vue-tsc`, not the eslint plugin, not
462
+ `npx bitboss-ui check` (none of them read CSS), and not the browser console:
463
+ an invalid registered-property value is not a parse error, so it logs nothing.
464
+ Grep your stylesheets for `--bb-ring-opacity` and confirm every value carries a
465
+ `%`.
466
+
467
+ Nine tokens are `@property`-registered in v3 — the eight color knobs
468
+ (`syntax: '<color>'`) plus `--bb-ring-opacity`. Each rejects values outside its
469
+ declared syntax the same silent way, so treat any future registration as the
470
+ same class of break.
471
+
472
+ ### Button and variant skin colors moved the other way
473
+
474
+ The "no longer global" row sends per-component values _down_ into unprefixed
475
+ locals. The button **skin** colors went the opposite direction — up into global
476
+ theme tokens. In v2 a skin was four locals set on your own modifier class:
477
+
478
+ ```css
479
+ /* v2 theming.css */
480
+ .bb-button--outline {
481
+ --color: #fff;
482
+ --border-color: #d1d5db;
483
+ --text-color: #111827;
484
+ /* hover / pressed / ring were derived from --color automatically */
485
+ }
486
+ ```
487
+
488
+ v3 reads none of those — the old locals are inert, so a skin ported verbatim
489
+ silently reverts to the library defaults. Each variant has its own `--bb-*`
490
+ family, declared in `variables.css` and overridable globally or on any scope:
491
+
492
+ | v2 local on `.bb-button` | v3 token family (per variant) |
493
+ | -------------------------------------------- | ----------------------------------- |
494
+ | `--color` (fill) | `--bb-<variant>` |
495
+ | `--text-color` | `--bb-<variant>-fg` |
496
+ | `--border-color` | `--bb-<variant>-border` |
497
+ | `--ring-color` | `--bb-<variant>-ring` |
498
+ | _derived_ `mix(--color 90%, black)` on hover | `--bb-<variant>-hover` (explicit) |
499
+ | _derived_ `mix(--color 80%, black)` on press | `--bb-<variant>-pressed` (explicit) |
500
+
501
+ `<variant>` is one of `primary`, `secondary`, `outline`, `ghost`,
502
+ `destructive`, `link`. Note the last two rows: v2 _derived_ hover and pressed
503
+ from the fill, v3 states them. Porting only `--bb-outline` leaves the hover
504
+ state on the library's default.
505
+
506
+ ```css
507
+ /* v3 equivalent of the block above */
508
+ :root {
509
+ --bb-outline: #fff;
510
+ --bb-outline-border: #d1d5db;
511
+ --bb-outline-fg: #111827;
512
+ --bb-outline-hover: #f3f4f6;
513
+ --bb-outline-pressed: #e5e7eb;
514
+ }
515
+ ```
516
+
313
517
  ## 4. Variants: from `theme` strings and class overrides to typed registries
314
518
 
315
519
  ### What changed
@@ -326,6 +530,19 @@ v3 replaces all of that with per-component `variant` props whose types are
326
530
  registers. An unregistered name is a **compile error** (v2's silent
327
531
  render-unstyled behavior is gone by design), and the `theme` prop is gone.
328
532
 
533
+ **⚠ v2 shipped no variant CSS at all, so adopting a variant is additive.** Grep
534
+ the v2 stylesheet and the only `bb-button--*` rules are `group`, `icon` and
535
+ `loading`; there are no `bb-alert--*`, no appearance-level `bb-badge--*`, no
536
+ `bb-tooltip--*` and no `bb-toast-message--*` rules. v2's components _emitted_
537
+ those names (`bb-alert--${theme}`, `bb-tooltip--${theme}`,
538
+ `bb-toast-message--${theme}`) and the library never styled them. In v2 the
539
+ class and the `theme` string meant **"whatever your app's CSS says, and nothing
540
+ else."** In v3 every one of those names is a real skin, so converting hands the
541
+ library a styling role it did not have — wherever your CSS does not override
542
+ every property v3 sets (background, border, foreground, hover, pressed, focus
543
+ ring), the appearance drifts. Diff a screenshot; do not assume. Section 7
544
+ covers the class-to-prop rewrite itself.
545
+
329
546
  Built-in sets and the class each name maps to:
330
547
 
331
548
  | Plugin option | Built-in variants | Class hook |
@@ -339,6 +556,54 @@ Built-in sets and the class each name maps to:
339
556
  | `dropdownItemVariants` | `default, destructive` | `bb-dropdown__item--variant-<name>` |
340
557
  | `tooltipVariants` | `default, destructive` | `bb-tooltip--<name>` |
341
558
 
559
+ ### Picking a v3 name for a v2 `theme` value
560
+
561
+ v2's `theme` was free-form, so most apps carry names that are not in the table
562
+ above (`error`, `danger`, `info`, `success`, `blue`, …). Work through it in
563
+ this order:
564
+
565
+ 1. **Name is in the table, and your app never styled it** → use the built-in
566
+ `variant`. This is the only genuinely free conversion.
567
+ 2. **Name is in the table, but your app styled it itself** → v3's built-in now
568
+ paints underneath your rules. Decide explicitly: accept v3's look and delete
569
+ your CSS, or register the name (`alertVariants: ['warning']`) so your CSS
570
+ stays the sole styling source.
571
+ 3. **Name is not in the table** → register it and keep your CSS. **Do not remap
572
+ onto the "nearest" built-in.** `error → destructive`, `danger →
573
+ destructive`, `info → primary` all compile, all look plausible in review,
574
+ and all silently replace your palette with the library's. Remap only as a
575
+ deliberate redesign decision, never as a mechanical migration step.
576
+ 4. **Name was never styled by anyone** → see the next subsection.
577
+
578
+ Registering is cheap — one array entry per name — and it is the
579
+ behaviour-preserving default. Prefer it whenever you are not sure.
580
+
581
+ ### ⚠ silent — a `theme` value that did nothing in v2 may do something in v3
582
+
583
+ Because v2 shipped no variant CSS, unless your app defined the rule the `theme`
584
+ prop was **inert**: every value rendered the same default surface. In v3 the
585
+ same string is a live skin, and whether it stays inert depends entirely on
586
+ whether the name happens to collide with a v3 built-in:
587
+
588
+ ```
589
+ both of these rendered an identical plain alert in v2:
590
+
591
+ <BbAlert theme="success" /> v3: not an alert built-in → compile error, you WILL notice
592
+ <BbAlert theme="warning" /> v3: built-in → silently becomes an amber alert, you will NOT
593
+ ```
594
+
595
+ So two identical-looking v2 call sites migrate to opposite outcomes. Before
596
+ converting any `theme=` value, check whether _anything_ styled it in v2 — your
597
+ stylesheet, since the library did not. If nothing did, the value was
598
+ decoration:
599
+
600
+ - register it as an empty custom variant (`alertVariants: ['success', 'blue']`)
601
+ to hold v2's appearance exactly, or
602
+ - drop the prop entirely and let the v3 default apply, or
603
+ - convert it and accept the new look **on purpose**.
604
+
605
+ What you must not do is convert it mechanically and assume nothing moved.
606
+
342
607
  ### Custom variants are first-class
343
608
 
344
609
  If your v2 app invented its own look (a `theme="brand"` or a hand-written
@@ -367,6 +632,12 @@ works with a visible cast (`variant="brand" as ButtonVariantType` won't
367
632
  compile — cast the value, not the prop), but the registry is the intended
368
633
  path.
369
634
 
635
+ **A name you keep as a plain class should not stay in the `bb-*` namespace.**
636
+ An app-minted `bb-button--pressed` or `bb-table--borderless` is squatting on
637
+ the library's prefix and collides the day the library ships that name. Either
638
+ register it as a variant, or rename it into your own prefix
639
+ (`app-button--pressed`). Section 7 has the decision table.
640
+
370
641
  ## 5. API-wide changes
371
642
 
372
643
  These patterns repeat across many components. The per-component guides list
@@ -374,20 +645,22 @@ every occurrence; this section explains each pattern once.
374
645
 
375
646
  ### 5.1 Components renamed, split, or removed
376
647
 
377
- | v2 | v3 | What to do |
378
- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
379
- | `BbBadge` (floating count/dot anchored to an element) | **`BbIndicator`** | `content` → `text`, `color` → `variant`, `floating` removed. New `max` prop renders `${max}+`. |
380
- | `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). |
381
- | — | **`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. |
382
- | `BbTab` | **`BbTabs`** | Component and types renamed (`BbTabProps` → `BbTabsProps`; deprecated type aliases remain, the component name does not). |
383
- | `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. |
384
- | `BbCheckboxIcon` / `BbRadioIcon` / `BbSwitchIcon` | **`BbBaseCheckboxIcon`** / **`BbBaseRadioIcon`** / **`BbBaseSwitchIcon`** | Rename import + BEM block (`.bb-checkbox-icon` → `.bb-base-checkbox-icon`). |
385
- | `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`). |
386
- | `useWizard` (+ `Step`, `WizardState`, `wizardInjectionKey`) | **removed** | No replacement — it was unused scaffolding. Copy the v2 implementation into your app if you depended on it. |
387
- | `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). |
388
- | `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). |
389
- | `BbToastMessage` type export | **removed** | Use `BbToastOptions` (the `toast()` options shape). |
390
- | `IconRegistry` / `IconNameFromRegistry` exports | **removed** | Icon names are plain strings now. Local art goes in `iconDir` as `local:<name>`; delete your `IconRegistry` augmentation. |
648
+ | v2 | v3 | What to do |
649
+ | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
650
+ | `BbBadge` (floating count/dot anchored to an element) | **`BbIndicator`** | `content` → `text`, `color` → `variant`, `floating` removed. New `max` prop renders `${max}+`. |
651
+ | `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). |
652
+ | — | **`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. |
653
+ | `BbTab` | **`BbTabs`** | Component and types renamed (`BbTabProps` → `BbTabsProps`; deprecated type aliases remain, the component name does not). |
654
+ | `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. |
655
+ | `BbCheckboxIcon` / `BbRadioIcon` / `BbSwitchIcon` | **`BbBaseCheckboxIcon`** / **`BbBaseRadioIcon`** / **`BbBaseSwitchIcon`** | Rename import + BEM block (`.bb-base-checkbox-container__icon` → `.bb-base-checkbox-icon`; see §5.6). |
656
+ | `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. |
657
+ | `useWizard` (+ `Step`, `WizardState`, `wizardInjectionKey`) | **removed** | No replacement — it was unused scaffolding. Copy the v2 implementation into your app if you depended on it. |
658
+ | `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). |
659
+ | `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). |
660
+ | `BbRatio` (+ `BbRatioProps`, `.bb-ratio`) | **removed** | No replacement — it was a one-rule wrapper. Use the `aspect-ratio` CSS property, or copy the v2 component. |
661
+ | `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. |
662
+ | `BbToastMessage` type export | **removed** | Use `BbToastOptions` (the `toast()` options shape). |
663
+ | `IconRegistry` / `IconNameFromRegistry` exports | **removed** | Icon names are plain strings now. Local art goes in `iconDir` as `local:<name>`; delete your `IconRegistry` augmentation. |
391
664
 
392
665
  ### 5.2 The polarity wave: booleans now read as opt-in
393
666
 
@@ -409,7 +682,7 @@ to the new name (`hide-close`).
409
682
 
410
683
  | Component(s) | v2 prop (default) | v3 prop (default) |
411
684
  | -------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
412
- | `BbAlert`, `BbDialog`, `BbOffCanvas` | `showClose` (`true`) | `hideClose` (`false`) — also new on `toast()` / `confirm()` options |
685
+ | `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. |
413
686
  | `BbTable` | `allowSelectAll` (`true`) | `disableSelectAll` (`false`) |
414
687
  | `BbTabs` | `animateX` / `animateY` (`true`) | `disableAnimateX` / `disableAnimateY` (`false`) |
415
688
  | `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. |
@@ -424,16 +697,18 @@ to the new name (`hide-close`).
424
697
 
425
698
  ### 5.3 Straight renames
426
699
 
427
- | Component | v2 | v3 |
428
- | -------------------------------------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
429
- | `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. |
430
- | `BbTooltip` | `timeout` | `delay` |
431
- | `BbIcon` | `type` (required) | `icon` |
432
- | `BbColorPalette`, `BbColorInput` | `picker` | `eyeDropper` |
433
- | `BbCheckboxGroup`, `BbRadioGroup`, `BbSwitchGroup` | `labelPosition` | `legendPosition` |
434
- | `BbSelect` | `prefill: 'focus'` | `prefill: 'interaction'` (value rename; same default) |
435
- | `BbToast` (host) | `placement` (e.g. `bottom-start`) | `position` (corner set: `top-left` … `bottom-right`) |
436
- | `BbAlert`, `BbTooltip`, `toast()` | `theme` | `variant` (typed — see section 4) |
700
+ | Component | v2 | v3 |
701
+ | -------------------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
702
+ | `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. |
703
+ | `BbTooltip` | `timeout` | `delay` |
704
+ | `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`. |
705
+ | `useToast()` | `dismissAll()` | `dismiss()` with **no argument**. The v3 return shape is `{ toast, dismiss, update }`. |
706
+ | `BbIcon` | `type` (required) | `icon` |
707
+ | `BbColorPalette`, `BbColorInput` | `picker` | `eyeDropper` |
708
+ | `BbCheckboxGroup`, `BbRadioGroup`, `BbSwitchGroup` | `labelPosition` | `legendPosition` |
709
+ | `BbSelect` | `prefill: 'focus'` | `prefill: 'interaction'` (value rename; same default) |
710
+ | `BbToast` (host) | `placement` (e.g. `bottom-start`) | `position` (corner set: `top-left` … `bottom-right`) |
711
+ | `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`). |
437
712
 
438
713
  ### 5.4 Removed props (no replacement needed — behavior became default or moved)
439
714
 
@@ -444,7 +719,18 @@ to the new name (`hide-close`).
444
719
  - **`color` everywhere**: gone from `BbAvatar`, `BbIcon`, `BbSpinner`,
445
720
  `BbCheckbox`, `BbRadio`, `BbSwitch`, `BbSlider`, `BbRating` and all three
446
721
  groups. Components inherit `currentColor` / theme tokens; style via CSS or
447
- the token knobs.
722
+ the token knobs. `BbAvatar`'s knobs are named: set **both** `--bg-color` and
723
+ `--text-color` on `.bb-avatar` (plus `--default-icon-color` for the built-in
724
+ icon fallback) — setting only the background leaves the initials painted
725
+ `--bb-primary-fg` on top of it.
726
+
727
+ ```css
728
+ .team-avatar .bb-avatar {
729
+ --bg-color: #7c3aed;
730
+ --text-color: #fff;
731
+ }
732
+ ```
733
+
448
734
  - **`BbSelect`**: `showChevron` and `updateOnAnimationFrame` removed; the
449
735
  `#chevron` slot and the four `#options:prepend/append(:outer)` slots are
450
736
  gone (use the new `#header` / `#footer` panel slots). Same options-slot
@@ -468,7 +754,15 @@ to the new name (`hide-close`).
468
754
  - **`BbPopover`**: `showClose`, `closeLabel`, `restoreFocus`, `block` removed —
469
755
  render your own close affordance in the content (`#default="{ close }"`).
470
756
  - **`BbDialog`**: `description`, `hideHeader`, `compact`, `overlayClasses`,
471
- `panelClasses` removed (use the `header` slot and normal classes).
757
+ `panelClasses` removed (use the `header` slot and normal classes). The
758
+ `#description` and `#close` **slots** are gone too — v3's slot set is
759
+ `header | title | default | footer`, and a custom close control goes inside
760
+ `#header`, which replaces the whole default header including the ✕ (so
761
+ `hide-close` is not needed alongside it).
762
+ - **`BbOffCanvas`**: the same `description` prop and the same `#description` /
763
+ `#close` slots were removed, with the same replacements. The
764
+ `aria-describedby` wiring `description` used to provide is gone — put the
765
+ copy in the default slot.
472
766
  - **`BbBreadcrumbs`**: string `divider` removed (slot-driven now);
473
767
  `dividerWidth` default changed `5` → `16`.
474
768
  - **`BbPagination`**: `loading` removed.
@@ -505,21 +799,161 @@ to the new name (`hide-close`).
505
799
  - **`direction`** on inputs: the exported `InputDirection` union — keyword
506
800
  typos now fail to compile; two-word ratio strings (`'1 2'`) still work.
507
801
 
508
- ### 5.6 Renamed CSS blocks
802
+ ### 5.5b Fields added to public object types
509
803
 
510
- If app CSS or test selectors target library internals, these blocks moved:
804
+ An **added** optional field breaks a consumer as hard as a removed one, and the
805
+ error text does not say so. If your app intersects a library object type to
806
+ carry its own metadata — `BbTableColumn & { sortable?: string }` for a backend
807
+ sort key, say — the intersection does not widen the property, it **collapses**
808
+ it: `string & undefined` is `undefined`, and every column literal then fails
809
+ with
810
+
811
+ ```
812
+ error TS2322: Type 'string' is not assignable to type 'undefined'.
813
+ ```
814
+
815
+ which names neither the field nor the library. That one line is worth
816
+ searching your build log for. The fix is to rename your field (`sortKey`) or to
817
+ subtract the library's first:
818
+ `Omit<BbTableColumn, 'sortable'> & { sortable?: string }`.
819
+
820
+ The collision-prone additions in v3:
821
+
822
+ | type | added in v3 | hazard |
823
+ | ----------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
824
+ | `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. |
825
+ | `BbTableColumn` | `rowClass?: ColumnClasses<Item>` | same collapse. Note this is the **column-level** field — the table-level `rowClass` prop is separate and both accumulate. |
826
+ | `BbDropdown` item | `description?: string` | collapse, plus it now renders as a second line on the item. |
827
+ | `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. |
828
+
829
+ Everything else added in v3 lands on `*Props` / `*Slots` / internal projections
830
+ — shapes you pass or read rather than intersect — so this list is deliberately
831
+ short.
832
+
833
+ ### 5.6 Renamed CSS blocks
511
834
 
512
- | v2 | v3 |
513
- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
514
- | `.bb-common-input-inner-container` (+ `--clearable`, `__prefix`, `__prepend-icon`, …) | `.common-input-wrapper` — modifiers `--active --compact --disabled --errors --loading --readonly --clearable` (note: no `bb-` prefix) |
515
- | `.base-btn`, `.base-btn--block`, … | `.bb-base-button*` |
516
- | `.bb-checkbox-icon` / `.bb-radio-icon` / `.bb-switch-icon` | `.bb-base-checkbox-icon` / `.bb-base-radio-icon` / `.bb-base-switch-icon` |
517
- | `.bb-chip*` | `.bb-badge*` (component replaced) |
518
- | `.bb-badge*` used as a floating count/dot | `.bb-indicator*` (component renamed) |
835
+ If app CSS or test selectors target library internals, these blocks moved. This
836
+ is the complete set — a class-by-class diff of the v2 stylesheet against v3
837
+ leaves nothing else orphaned. All of it is **silent**: a selector on a class
838
+ nobody emits matches nothing and throws nothing.
839
+
840
+ **Input chrome**
841
+
842
+ | v2 | v3 |
843
+ | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
844
+ | `.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) |
845
+ | `.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` |
846
+ | `.bb-base-input-outer-container` (+ `--compact`) | merged into `.bb-base-input-container` (+ `--compact`) |
847
+ | `.bb-base-input-container__input` (+ `--left --center --right`) | `.bb-base-input-container__control` (+ the same modifiers) |
848
+ | `.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` …) |
849
+ | `.bb-base-input-container--label-mode-floating` | `.bb-base-input-container--floating-label` |
850
+ | `.bb-base-input-container__errors-outer` | `.bb-base-input-container__errors-wrap` |
851
+ | `.bb-base-input-container__hint-container` | `.bb-base-input-container__hint-wrap` |
852
+ | `.base-btn`, `.base-btn--block`, … | `.bb-base-button*` |
853
+ | `.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) |
854
+ | `.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`) |
855
+ | `.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`. |
856
+
857
+ **Dialogs, off-canvas, popovers**
858
+
859
+ | v2 | v3 |
860
+ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
861
+ | `.bb-base-dialog` (+ `--deny-close --fullscreen --open`, `__header __title __body __body-content __footer`) | `.bb-dialog` / `.bb-dialog__*` — 1:1 on every one of those |
862
+ | `.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 |
863
+ | `.bb-common-popover--scrollable` | `.bb-popover__panel--scrollable` (the rest of `.bb-common-popover*` is unchanged) |
864
+ | `.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. |
865
+
866
+ **Tabs** — the whole tree was renamed, and v2's block name was `bb-tab`, not
867
+ `bb-tabs`, so nothing of a v2 tab theme survives untouched:
868
+
869
+ | v2 | v3 |
870
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
871
+ | `.bb-tab` | `.bb-tabs` |
872
+ | `.bb-tab--vertical` / `--horizontal` | `.bb-tabs--vertical` / `--horizontal` (+ `.bb-tabs-list--vertical` / `--horizontal` on the strip) |
873
+ | `.bb-tab__label-boundary` | `.bb-tabs-list` |
874
+ | `.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` |
875
+ | `.bb-tab__btn` (+ `--active`) | `.bb-tabs__trigger` (+ `--active`), with new inner `.bb-tabs__trigger-content` / `.bb-tabs__trigger-label` |
876
+ | `.bb-tab__pane` (+ `--shown`) | `.bb-tabs__pane` (+ `--shown`) |
877
+ | `.bb-tab__panes-container` | `.bb-tabs-panes` |
878
+
879
+ v2 had no full-width tab strip, so apps hand-rolled one (commonly a
880
+ `bb-tabs--full-width` class). v3 ships it: `<BbTabs block>` sets
881
+ `.bb-tabs--block` / `.bb-tabs-list--block` and gives the triggers equal widths.
882
+ Delete the hand-rolled modifier rather than porting it.
883
+
884
+ **Everything else**
885
+
886
+ | v2 | v3 |
887
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
888
+ | `.bb-chip*` | `.bb-badge*` (component replaced) |
889
+ | `.bb-badge*` used as a floating count/dot (`--dot --floating --positioned --left --bottom --empty`, `__wrapper`, `__content-container`) | `.bb-indicator*` (component renamed) |
890
+ | `.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 |
891
+ | `.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) |
892
+ | `.bb-toast-message__icon-container` | **removed** — style `.bb-toast-message__icon` directly |
893
+ | `.bb-tree--open` | `.bb-tree__node--expanded` |
894
+ | `.bb-tree-row` | `.bb-tree__row` |
895
+ | `.bb-tree-main-content` | `.bb-tree__content` |
896
+ | `.bb-dropdown-button__dropdown-chevron` | `.bb-dropdown-button__chevron` |
897
+ | `.bb-dropdown-button--loading` / `__content` / `__icon` / `__prepend-icon` / `__append-icon` | **removed** — the split button delegates to `BbButton`'s internals; target `.bb-button__*` inside it |
898
+ | `.bb-dropdown__wrapper` | **removed** — the extra wrapper element is gone |
899
+ | `.bb-select-popover__search-spinner` | `.bb-select-popover__search-icon` (one element, spinner state included) |
900
+ | `.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. |
901
+ | `.bb-base-date-picker__control` / `__year-control` | `.bb-base-date-picker__nav-btn` |
902
+ | `.bb-base-date-picker__month-button` / `__year-button` | `.bb-base-date-picker__heading-btn` (+ `--active`) |
903
+ | `.bb-base-date-picker__weekday` | `.bb-base-date-picker__header-cell` |
904
+ | `.bb-base-date-picker__monthday` | `.bb-base-date-picker__date` (+ the inner `__date-button`) |
905
+ | `.bb-base-date-picker__year-container` | `.bb-base-date-picker__year-selector` |
906
+ | `.bb-base-date-picker__header--hidden`, `__selected-day-label` | **removed** |
907
+ | `.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) |
908
+
909
+ The date **input**'s own field tree (`.bb-base-date-picker-input__field`,
910
+ `__fields`, `__placeholder`, `__separator`, `__calendar-btn`) moved to a shared
911
+ `.bb-segmented-field__*` block — full table in
912
+ [bb-date-picker-input.md](./components/bb-date-picker-input.md).
913
+
914
+ **Removed with no counterpart:** `.bb-base-dialog--compact`,
915
+ `.bb-offcanvas--compact`, `.bb-base-textarea--compact` (compact now lives on
916
+ `.common-input-wrapper--compact`), `.bb-base-select--active` / `--compact` /
917
+ `__inner-wrapper` / `__input-container` (the select activator was rebuilt —
918
+ nearest hooks are `.bb-base-select__activator`, `__value`, `__display-value`;
919
+ `--shown` survives), `.bb-base-rating__option--highlighted` (v3 uses
920
+ `--filled` / `--empty`), `.bb-progress--horizontal` (`.bb-progress` /
921
+ `.bb-progress-bar` are the blocks), `.bb-ratio` (component removed).
922
+
923
+ ### 5.7 Renamed custom properties the library WRITES
924
+
925
+ Section 3 covers the tokens you set. These are variables the library **writes
926
+ onto the DOM** at runtime for you to read — the usual reason to touch them is
927
+ nested-table column alignment. They all dropped the `bb-` prefix, and every one
928
+ of them is consumed through `var(name, fallback)`, so a v2 name silently
929
+ resolves to the fallback instead of failing.
930
+
931
+ | v2 | v3 |
932
+ | ----------------------------------------------------- | -------------------------------------------------------------------------- |
933
+ | `--bb-table-{id}-track-{key}` | `--table-{id}-track-{key}` |
934
+ | `--bb-table-offset-start` / `-end` | `--offset-start` / `--offset-end` |
935
+ | `--bb-table-offset-internal-start` / `-end` | `--offset-internal-start` / `--offset-internal-end` |
936
+ | `--bb-table-fill` | `--fill` |
937
+ | `--bb-table-natural-width` | `--natural-width` |
938
+ | `--bb-select-popover-width` (on `.bb-select-popover`) | `--w` — and the supported path is now the `width` prop, not a CSS override |
939
+ | `--bb-icon-dimensions-w` (on `.bb-icon`) | `--w` |
940
+ | `--bb-breadcrumbs-flex-basis` / `-flex-grow` | `--flex-basis` / `--flex-grow` |
941
+ | `--bb-picker-preview-color` | `--picker-preview-color` |
942
+
943
+ A regex matching the old shape (`/--bb-table-[\w-]+-track/`) matches nothing in
944
+ v3. Search your app for `--bb-table-`, `--bb-select-popover-`,
945
+ `--bb-icon-dimensions-`, `--bb-breadcrumbs-` and `--bb-picker-` — none of those
946
+ names exist in v3 and all of them fail quietly. `--available-height`,
947
+ `--inner-width` and `--overlay-min-height` were dropped outright.
948
+
949
+ If you re-published track variables by hand to align a deeply nested table,
950
+ delete that code: `inherit-column-widths` takes a **table id**
951
+ (`inherit-column-widths="<ancestor-id>"`), not just `true`, and walks the whole
952
+ ancestor chain — it is not limited to the immediate parent.
519
953
 
520
954
  ## 6. Behavior changes you will feel at runtime
521
955
 
522
- No rename to grep for — these change what existing, still-compiling code does.
956
+ Nothing here fails to compile — these change what existing code does.
523
957
 
524
958
  1. **`BbButton` tracks async click handlers by default.** An `async @click`
525
959
  now disables the button and shows a spinner until it settles. Opt out per
@@ -545,7 +979,14 @@ No rename to grep for — these change what existing, still-compiling code does.
545
979
  progress bar) instead of flashing to skeleton — and the rows and header go
546
980
  `inert` during the refetch. Escape hatch: `interactive-while-loading`. The
547
981
  `#loading` slot fires on the **first load only** now.
548
- 6. **Dynamic slot names are normalized** via
982
+ 6. **A truthy column `sortable` switches on v3's own sort UI.** `BbTable` reads
983
+ the field as `!!column.sortable`, so a column carrying a backend sort key
984
+ (`sortable: 'created_at'`) renders the built-in header sort button and sets
985
+ `aria-sort` — an app with its own sort control then shows two. Sorting stays
986
+ a controlled model (`v-model:sort`), so nothing reorders on its own; the
987
+ button appears and emits. Strip or rename the field at the `BbTable`
988
+ boundary. The compile-time half of the same collision is in §5.5b.
989
+ 7. **Dynamic slot names are normalized** via
549
990
  `slotKey(key) = key.split(/\W+/g).join('_').toLowerCase()` in
550
991
  `BbBreadcrumbs`, `BbTree`, `BbTabs`, and the date-picker day slots.
551
992
  `#Order History` → `#order_history`. **⚠ silent**: a stale raw-key slot
@@ -554,15 +995,20 @@ No rename to grep for — these change what existing, still-compiling code does.
554
995
  normalized slug (`?tab=In%20Review` → `?tab=in_review` — update deep links
555
996
  and server code), and tab/panel DOM ids are normalized. Only `v-model` /
556
997
  item `key` stay raw.
557
- 7. **Dialogs, popovers, dropdowns and selects open as bottom sheets on mobile**
998
+ 8. **Dialogs, popovers, dropdowns and selects open as bottom sheets on mobile**
558
999
  by default (`adaptive`, default `true`). Set `adaptive: false` in the
559
1000
  plugin, or per component, for v2-like floating behavior.
560
- 8. **`confirm()` buttons**: default labels are localized (no more hard-coded
1001
+ 9. **`confirm()` buttons**: default labels are localized (no more hard-coded
561
1002
  "OK"/"Annulla") and footer buttons default to `md`.
562
- 9. **Invalid enum-ish prop values** (`size`, `direction`, `BbSlider.labelMode`)
563
- now warn in dev and fall back to the documented default instead of
564
- rendering broken or throwing.
565
- 10. **`BbTree` is a disclosure list, not an ARIA tree**: the unfulfilled
1003
+ 10. **`confirm()` closes itself now.** `autoClose` was opt-in in v2 — after
1004
+ `resolve`/`reject` the dialog stayed up until you called `close()`. In v3 it
1005
+ defaults to `true`. ⚠ silent: the promise settles exactly as before, the
1006
+ dialog just disappears. A multi-step flow that kept it open between steps
1007
+ has to pass `autoClose: false`.
1008
+ 11. **Invalid enum-ish prop values** (`size`, `direction`, `BbSlider.labelMode`)
1009
+ now warn in dev and fall back to the documented default instead of
1010
+ rendering broken or throwing.
1011
+ 12. **`BbTree` is a disclosure list, not an ARIA tree**: the unfulfilled
566
1012
  `role="tree"` / `treeitem` / `aria-level` markup is gone, and a collapsed
567
1013
  branch's children are now `inert` (out of the Tab order). Tests or CSS
568
1014
  keyed on the removed roles match nothing — see
@@ -586,6 +1032,23 @@ These keep _rendering_ in v3 (the class hooks still exist), but they are now
586
1032
  the wrong layer: they bypass the typed registry, silently drift when the
587
1033
  library renames internals, and duplicate what a prop expresses. Convert them.
588
1034
 
1035
+ **But the conversion is additive, not neutral.** v2 shipped no variant CSS
1036
+ (§4): those class names carried your rules and nothing else. In v3 each one is
1037
+ a real skin, so every rewrite below slides the library's rules _underneath_
1038
+ yours — background, border, foreground, hover, pressed, focus ring. Wherever
1039
+ your CSS does not override all of them, the appearance drifts. Diff a
1040
+ screenshot; do not assume. Two consequences before you start:
1041
+
1042
+ - **Leaving the class alone is not a safe holding position.** `variant`
1043
+ defaults to `primary`, so in v3 `<BbButton class="bb-button--outline">`
1044
+ renders `bb-button--primary bb-button--outline` — the built-in skin and your
1045
+ class fighting in the cascade. For a pure class override, pass
1046
+ `variant="none"`; it is the one value that emits no variant class.
1047
+ - **A name that collides with a v3 built-in is the dangerous case, not the easy
1048
+ one.** If your app owned the look of `warning` / `destructive` / `secondary`,
1049
+ converting to the same-named `variant` hands that look to the library.
1050
+ Register the name as a custom variant (§4) to keep your CSS the only source.
1051
+
589
1052
  ### Finding them
590
1053
 
591
1054
  Run in the consumer project:
@@ -596,34 +1059,54 @@ rg -n 'class="[^"]*bb-[a-z-]+--' --type vue
596
1059
  rg -n ":class=\"[^\"]*'bb-[a-z-]+--" --type vue
597
1060
  # free-form theme props (removed in v3)
598
1061
  rg -n '(:?theme)=' --type vue
1062
+ # the same prop as an option-object key — toast()/confirm() live in .ts
1063
+ rg -n 'theme' --type ts --type js
599
1064
  # raw component markup rebuilt from classes (no <BbX> tag on the same line)
600
1065
  rg -n 'class="bb-(button|badge|alert|toast-message|tooltip|indicator)\b' --type vue
601
1066
  ```
602
1067
 
603
1068
  ### Correcting them
604
1069
 
605
- | Found | Rewrite as |
606
- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
607
- | `<BbButton class="bb-button--outline">` | `<BbButton variant="outline">` (same for `secondary`, `ghost`, `destructive`, `link`) |
608
- | `<BbAlert class="bb-alert--warning">` / v2 `theme="warning"` | `<BbAlert variant="warning">` |
609
- | `class="bb-badge bb-badge--secondary"` on a `<span>` | `<BbBadge variant="secondary">` — the real component, not markup cosplay |
610
- | `bb-toast-message--success` styling / v2 `theme` on toasts | `toast({ variant: 'success' })` |
611
- | `bb-tooltip--destructive` | `<BbTooltip variant="destructive">` / `v-bb-tooltip="{ text, variant: 'destructive' }"` |
612
- | 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"`. |
613
- | A class that overrides _layout_, not look (widths, margins) | Leave it — classes remain the right tool for layout; variants own _appearance semantics_. |
1070
+ | Found | Rewrite as |
1071
+ | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1072
+ | `<BbButton class="bb-button--outline">` | `<BbButton variant="outline">` (same for `secondary`, `ghost`, `destructive`, `link`) |
1073
+ | `<BbAlert class="bb-alert--warning">` / v2 `theme="warning"` | `<BbAlert variant="warning">` |
1074
+ | `class="bb-badge bb-badge--secondary"` on a `<span>` | `<BbBadge variant="secondary">` — the real component, not markup cosplay |
1075
+ | `bb-toast-message--success` styling / v2 `theme` on toasts | `toast({ variant: 'success' })` |
1076
+ | `bb-tooltip--destructive` | `<BbTooltip variant="destructive">` / `v-bb-tooltip="{ text, variant: 'destructive' }"` |
1077
+ | 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"`. |
1078
+ | 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. |
1079
+ | 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. |
1080
+ | A class that overrides _layout_, not look (widths, margins) | Leave it — classes remain the right tool for layout; variants own _appearance semantics_. |
614
1081
 
615
1082
  Rule of thumb: if the class name encodes a **meaning** (state, emphasis,
616
1083
  severity), it should be a variant; if it encodes **geometry**, it stays a
617
1084
  class.
618
1085
 
1086
+ Whatever stays a class should not stay in the `bb-*` namespace. An app-minted
1087
+ `bb-table--borderless` or `bb-avatar--square` is squatting on the library's
1088
+ prefix — neither name has ever shipped, and the day one does, your rule and the
1089
+ library's collide silently. Rename them into your own prefix.
1090
+
619
1091
  ## 8. The silent-failure audit
620
1092
 
621
1093
  Everything the compiler cannot catch, in one grep pass. Run each in the
622
1094
  consumer project; every hit needs a decision, not necessarily a change.
623
1095
 
1096
+ Two surfaces have no other net. **Composable option objects live in `.ts`** —
1097
+ `toast({ … })` and `confirm({ … })` inside stores, interceptors and helpers —
1098
+ and neither gate reaches them: `npx bitboss-ui check` opens only `.vue` and
1099
+ `.md`, and the eslint plugin walks template ASTs. **Nothing reads CSS at all**,
1100
+ not the CLI, not eslint, not `vue-tsc`, not the browser console. The
1101
+ `--type ts --type js` lines and the token/class lines below are the whole
1102
+ safety net for those two. (ripgrep's `js` type already covers `*.vue`, so
1103
+ adding `--type vue` to them is redundant.)
1104
+
1105
+ **Props, slots and markup**
1106
+
624
1107
  ```bash
625
1108
  rg -in 'querykey' # → queryKey (URL params silently move)
626
- 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
1109
+ 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
627
1110
  rg -n 'allow-select-all|allowSelectAll' # → disable-select-all, inverted
628
1111
  rg -n 'auto-loading|autoLoading' # BbButton behavior flip / rename
629
1112
  rg -n 'disabled-while-loading|disabledWhileLoading' # removed
@@ -633,11 +1116,11 @@ rg -n ':flip=|flip="' # → disable-flip on palette/select-p
633
1116
  rg -n 'hide-arrow|show-arrow|arrow-padding|hideArrow|showArrow|arrowPadding' # removed off tooltips
634
1117
  rg -n '<BbChip' # component removed → BbBadge clearable
635
1118
  rg -n '<BbBadge' # v2 badges are v3 indicators — review each
636
- rg -n '<BbTab[ >]' # → BbTabs
1119
+ rg -n '<BbTab\b' # → BbTabs (\b so a tag name alone on its line still matches, and <BbTabs does not)
637
1120
  rg -n '#label-' # BbTabs label slot → #label:<slotKey>
638
- rg -n 'reverse' --type vue # BbRadio/BbSwitch default flipped
639
- rg -n ':timeout=' --type vue # BbTooltip → delay (BbAvatar keeps timeout)
640
- rg -n '<BbIcon[^>]*type=' # → icon=, and prefix your own icons `local:` (bb-icon.md)
1121
+ rg -n 'reverse' --type ts --type js # BbRadio/BbSwitch default flipped
1122
+ rg -n 'timeout' --type vue # BbTooltip → delay (BbAvatar keeps timeout); no ':' so static attrs match too
1123
+ rg -nU '<BbIcon[^>]*type=' # → icon=, and prefix your own icons `local:` (bb-icon.md). -U so a multi-line tag matches
641
1124
  rg -n '(icon|type)="[a-z][a-z0-9-]*"' # bare icon names in templates → `local:<name>` so the Iconify extension previews them
642
1125
  rg -n 'allow-writing|allowWriting' # → disable-writing, inverted + new tokens
643
1126
  rg -n 'show-chevron|showChevron|updateOnAnimationFrame|update-on-animation-frame' # removed
@@ -645,17 +1128,48 @@ rg -n 'label-position|labelPosition' # groups → legend-position
645
1128
  rg -n 'prefill="focus"|prefill: .focus.' # → 'interaction'
646
1129
  rg -n ':prefill="false"|prefill: false' # now search-first, not load-on-open
647
1130
  rg -n 'role="tree|treeitem' # BbTree ARIA roles removed
648
- rg -n ':filter-by="[^\x27\[]' # function/string filterBy → string[]
1131
+ rg -n 'filter-by|filterBy' # function/string filterBy → string[] (camelCase spelling lives in .ts option objects)
649
1132
  rg -n 'option:prepend|option:append|options:prepend|options:append|#chevron' # removed slots
650
1133
  rg -n '<Bb(Checkbox|Radio|Switch|Slider|Rating|Avatar|Icon|Spinner)[^>]*color=' # removed color props
651
1134
  rg -n 'disabled:' --type ts --type vue # item.disabled near option components → selectable
652
- rg -n 'picker' --type vue # color input/palette → eye-dropper
653
- rg -n 'bb-common-input-inner-container' # CSS block → .common-input-wrapper
654
- rg -n 'IconRegistry|BbToastMessage|useWizard|BaseButtonProps|BaseDialog' # removed exports
1135
+ rg -n 'picker' --type ts --type js # color input/palette → eye-dropper
1136
+ ```
1137
+
1138
+ **Composable option objects and imports (`.ts` / `.js`)**
1139
+
1140
+ ```bash
1141
+ rg -n 'timeout' --type ts --type js # toast()/confirm() options → duration
1142
+ rg -n 'theme' --type ts --type js # toast()/confirm() options → variant
1143
+ rg -n 'dismissAll' # useToast().dismissAll() → dismiss() with no argument. useConfirm() has a NEW dismissAll — check which one each hit is
1144
+ rg -n 'autoClose|auto-close' # confirm(): opt-in in v2, default true in v3 (section 6)
1145
+ rg -n 'yesText|noText|onYes|onNo|hideHeader|setLoading' --type ts --type js # confirm() options collapsed into the yes/no configs
1146
+ rg -n 'BbTableColumn\s*&|DropdownItem\s*&' --type ts # intersections that collapse to `undefined` (§5.5b)
1147
+ rg -n "provide\('icons'|inject\('icons'" # a hand-rolled icon registry is shadowed, not merged, by the plugin's (section 1)
1148
+ rg -n 'IconRegistry|BbToastMessage|useWizard|useQueue|useQuery|useBroadcastChannelInstance|BaseButtonProps|BaseDialog' # removed exports
655
1149
  rg -n 'setConfig' # only locale survives at runtime
656
- rg -n -- '--bb-(contrasting|primary-base|hint|icon-color|input-|overlay-|button-|dialog-p)' # retired tokens
657
1150
  ```
658
1151
 
1152
+ **CSS: tokens and class names**
1153
+
1154
+ ```bash
1155
+ 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)
1156
+ rg -n -- '--bb-ring-opacity' # survives by name; every value must now carry a % (section 3)
1157
+ 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)
1158
+ rg -n 'bb-color-palette__' # → .bb-base-color-palette__*; only __popover survives under the old name
1159
+ 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>
1160
+ ```
1161
+
1162
+ ⚠ That first pattern is exact on purpose, and the near-misses are the point: a
1163
+ looser one (`input-`, `overlay-`, `dialog-p`) sweeps up tokens that are still
1164
+ live globals, and deleting those overrides shifts the layout silently. All of
1165
+ these survive in v3 under the same name — do not remove them because a pattern
1166
+ matched: `--bb-input-h`, `-px`, `-py`, `-icon`, `-spacing`, `-prefix-w`,
1167
+ `--bb-leading`, `--bb-label-spacing-x` / `-spacing-y`, `--bb-label-weight`,
1168
+ `--bb-ring-size`, `--bb-ring-opacity`, `--bb-arrow`, `--bb-radius`,
1169
+ `--bb-primary`, `--bb-panel`, `--bb-text`, `--bb-border`, `--bb-danger`,
1170
+ `--bb-ring`, `--bb-surface-hover` (and `--bb-overlay-blur`, new in v3). Six of
1171
+ them changed **default value** — the drift table in section 3 says which.
1172
+
659
1173
  Also review, with no greppable signature: dialog/off-canvas **widths**
660
1174
  (section 2), the **primary color** (section 3), `adaptive` bottom sheets on
661
1175
  mobile (section 6), and any UX that relied on second-click-to-clear selects.
@@ -665,7 +1179,10 @@ mobile (section 6), and any UX that relied on second-click-to-clear selects.
665
1179
  Detailed old→new tables and edit outlines live in
666
1180
  [`migration/components/`](./components/). Components not listed have **no
667
1181
  functional changes** — nothing to migrate beyond the family-wide notes above.
668
- That no-migration set is: `BbNumberInput`, `BbTextarea`, `BbProgress`.
1182
+ That no-migration set is: `BbNumberInput`, `BbTextarea`, `BbProgress` — with
1183
+ one caveat: the family-wide CSS renames in §5.6 still reach them
1184
+ (`.bb-base-textarea--compact`, `.bb-progress--horizontal` and the input-chrome
1185
+ blocks all moved), so check your stylesheet even for these three.
669
1186
 
670
1187
  `BbSmoothHeight` is **not** in it: its `tag` narrowed from any non-void element
671
1188
  to `'div' | 'span'`, so anything else is now a compile error — see
@@ -684,3 +1201,4 @@ to `'div' | 'span'`, so anything else is now a compile error — see
684
1201
  | Selects | [BbSelect](./components/bb-select.md) · [BbSelectPopover](./components/bb-select-popover.md) |
685
1202
  | Choice controls | [BbCheckbox / BbRadio / BbSwitch](./components/bb-checkbox.md) · [the three groups](./components/bb-checkbox-group.md) |
686
1203
  | Other inputs | [BbSlider](./components/bb-slider.md) · [BbRating](./components/bb-rating.md) · [BbTag](./components/bb-tag.md) · [BbDropzone](./components/bb-dropzone.md) |
1204
+ | Removed | [BbIntersection](./components/bb-intersection.md) · [useBroadcastChannelInstance](./components/use-broadcast-channel-instance.md) |