bitboss-ui 3.0.0-beta.4 → 3.0.0-beta.8

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 (42) hide show
  1. package/README.md +36 -29
  2. package/dist/ai/BbBadge.md +10 -5
  3. package/dist/ai/BbSelect.md +8 -0
  4. package/dist/ai/BbTable.md +257 -51
  5. package/dist/ai/changelog.json +86 -2
  6. package/dist/ai/components.json +25 -15
  7. package/dist/ai/guides/design-language.md +14 -14
  8. package/dist/ai/guides/design-tokens.md +25 -18
  9. package/dist/ai/guides/installation-and-plugin-setup.md +1 -1
  10. package/dist/ai/guides/migration/components/bb-dialog.md +35 -29
  11. package/dist/ai/guides/migration/components/bb-table.md +406 -28
  12. package/dist/ai/guides/migration/v2-to-v3.md +175 -75
  13. package/dist/ai/index.md +1 -1
  14. package/dist/ai/recipes/inertia/layout-scaffold.md +5 -1
  15. package/dist/ai/recipes/inertia/ownership-atlas.md +41 -20
  16. package/dist/ai/recipes/inertia/records-workspace.md +26 -7
  17. package/dist/ai/recipes/nuxt/layout-scaffold.md +5 -1
  18. package/dist/ai/recipes/nuxt/records-workspace.md +26 -7
  19. package/dist/ai/recipes/vue/layout-scaffold.md +5 -1
  20. package/dist/ai/recipes/vue/records-workspace.md +26 -7
  21. package/dist/ai/source/BbBaseInputContainer.md +26 -0
  22. package/dist/ai/source/BbBreadcrumbs.md +31 -0
  23. package/dist/ai/source/BbDialog.md +15 -9
  24. package/dist/ai/source/BbOffCanvas.md +17 -11
  25. package/dist/ai/source/BbProgress.md +2 -2
  26. package/dist/ai/source/BbTable.md +837 -787
  27. package/dist/ai/source/CommonInputWrapper.md +4 -0
  28. package/dist/components/BbPagination/BbPagination.vue.d.ts +1 -1
  29. package/dist/components/BbTable/BbTable.vue.d.ts +5 -2
  30. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +439 -484
  31. package/dist/components/BbTable/types.d.ts +83 -21
  32. package/dist/components/BbTable/utils.d.ts +18 -3
  33. package/dist/components/BbTable/utils.js +36 -28
  34. package/dist/index.d.ts +5 -1
  35. package/dist/index.js +68 -67
  36. package/dist/llms-full.txt +1065 -265
  37. package/dist/llms-medium.txt +15 -15
  38. package/dist/llms.txt +1 -1
  39. package/dist/styles.css +1 -1
  40. package/llms.txt +1 -1
  41. package/package.json +1 -1
  42. package/dist/assets/svgs/sort.svg_raw.js +0 -4
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "bitboss-ui",
4
- "version": "3.0.0-beta.4",
4
+ "version": "3.0.0-beta.8",
5
5
  "upgrade": "v2-to-v3",
6
6
  "guide": "ai/guides/migration/v2-to-v3.md",
7
7
  "summary": {
8
8
  "renames": 9,
9
- "behaviourBreaks": 29
9
+ "behaviourBreaks": 43
10
10
  },
11
11
  "breaking": [
12
12
  {
@@ -279,6 +279,90 @@
279
279
  "description": "BbTable #no-data fills the table's own full-width <td> (was: the slot replaced the empty-state <tr>)",
280
280
  "ruling": "F#/no-data-shell",
281
281
  "guide": "components/bb-table.md"
282
+ },
283
+ {
284
+ "kind": "behaviour",
285
+ "description": "BbTable row events click:row / contextmenu:row / dblclick:row emit (event, item) with item the RAW record (was the internal row wrapper with the record at row.item) and no trailing selected boolean",
286
+ "ruling": "owner 2026-08-25, plans/BBTABLE-GRID.md",
287
+ "guide": "components/bb-table.md § Row events"
288
+ },
289
+ {
290
+ "kind": "behaviour",
291
+ "description": "BbTable: hiding a column with thClass/tdClass + display: none (the v2/early-v3 narrow-screen recipe) no longer works — a hidden grid item keeps its track and shifts the following cells; remove the column from columns instead",
292
+ "ruling": "grid substrate, plans/BBTABLE-GRID.md",
293
+ "guide": "components/bb-table.md § Do not override; guide § Narrow screens"
294
+ },
295
+ {
296
+ "kind": "behaviour",
297
+ "description": "BbTable renders a CSS grid of divs with ARIA roles, not a <table>: every element selector under .bb-table (table/thead/tbody/tfoot/caption/tr/th/td/colgroup/col) matches nothing. Every .bb-table* class survives; .bb-table__table/__head/__body/__foot + [role=row]/[role=columnheader]/[role=cell] are the blessed translation (no data-* hooks, no shim classes)",
298
+ "ruling": "plans/BBTABLE-GRID.md § Migration & ecosystem",
299
+ "guide": "components/bb-table.md § Selector translation"
300
+ },
301
+ {
302
+ "kind": "behaviour",
303
+ "description": "BbTable #thead / #tbody / #tfoot inner markup: role-complete <div role=\"row\"> holding <div role=\"columnheader\\",
304
+ "ruling": "cell\"> — never <tr>/<th>/<td>; #tfoot's columnCount now feeds grid-column: span N / 1 / -1 (+ aria-colspan), not colspan. A custom #thead over an empty table publishes no tracks",
305
+ "guide": "plans/BBTABLE-GRID.md § Non-negotiables (exception 1)"
306
+ },
307
+ {
308
+ "kind": "behaviour",
309
+ "description": "BbTable --fill custom property removed (not republished) — it fed the fixed-layout self-correction loop the grid does not have",
310
+ "ruling": "plans/BBTABLE-GRID.md § Tracks (RULED 2026-08-26)",
311
+ "guide": "components/bb-table.md § --fill and --natural-width are removed"
312
+ },
313
+ {
314
+ "kind": "behaviour",
315
+ "description": "BbTable --natural-width custom property removed (not republished) — republishing the resolved-track sum would publish the container width under a \"natural\" name",
316
+ "ruling": "plans/BBTABLE-GRID.md § Tracks (RULED 2026-08-26)",
317
+ "guide": "components/bb-table.md § --fill and --natural-width are removed"
318
+ },
319
+ {
320
+ "kind": "behaviour",
321
+ "description": "BbTable track-distribution drift: surplus split equally among auto columns (748/52 → 544/256), constrained wrapping grows tracks by equal increments (~22% narrower long column, taller rows), frozen+auto ≤ ~19px/column, all-frozen tables no longer inflate the select/actions columns, scroll-container cells no longer floor the column (max-width: 0 ellipsis trick breaks), % widths in an overflowing table resolve against min-content (30%: 181 → 303px), expand-row content never widens parent columns, orphan snap → auto. Overflow threshold, wrap floor, frozen px and fixed equal split are exact",
322
+ "ruling": "plans/BBTABLE-GRID.md § Tracks + § v2.1",
323
+ "guide": "components/bb-table.md § Column widths: what holds and what drifts"
324
+ },
325
+ {
326
+ "kind": "behaviour",
327
+ "description": "BbTable copy-to-spreadsheet degrades: Chrome's selection serializer keys off computed display, so a selected grid pastes one value per line (was one cell per column). Accepted; sanctioned path = app-level \"copy as TSV from the items model\" recipe (queued, not shipped)",
328
+ "ruling": "plans/BBTABLE-GRID.md § Migration & ecosystem (7)",
329
+ "guide": "components/bb-table.md § Copy-to-spreadsheet degrades"
330
+ },
331
+ {
332
+ "kind": "behaviour",
333
+ "description": "BbTable print header repetition lost — <thead> page-repetition has no grid equivalent; data rows get break-inside: avoid",
334
+ "ruling": "plans/BBTABLE-GRID.md § Migration & ecosystem (8)",
335
+ "guide": "components/bb-table.md § Print: header repetition is gone"
336
+ },
337
+ {
338
+ "kind": "behaviour",
339
+ "description": "BbTable: global element CSS (table/td resets, .prose table, stray td { padding }) silently stops reaching the table — immunity benefit AND a silent look change on pages that leaned on it",
340
+ "ruling": "plans/BBTABLE-GRID.md § Migration & ecosystem (9)",
341
+ "guide": "components/bb-table.md § Global element CSS no longer reaches the table"
342
+ },
343
+ {
344
+ "kind": "behaviour",
345
+ "description": "BbTable browser floor raised to subgrid engines (Chrome/Edge 117, Firefox 71, Safari 16), block align-content centring from Chrome 123 / Safari 17.4 / Firefox 125; no @supports fallback",
346
+ "ruling": "plans/BBTABLE-GRID.md § Non-negotiables + § CSS port rules",
347
+ "guide": "components/bb-table.md § Browser floor"
348
+ },
349
+ {
350
+ "kind": "behaviour",
351
+ "description": "BbTable contain: inline-size (+ min-width: 0, grid-column: 1 / -1) on the expand / no-data / loading cells is load-bearing — overriding it lets full-span content resize the tracks (runaway loop with a nested inheriting table); joins gap: 0 on the root and nowrap on the actions cell in the do-not-override list",
352
+ "ruling": "plans/BBTABLE-GRID.md § Measurement & track publishing",
353
+ "guide": "components/bb-table.md § Do not override"
354
+ },
355
+ {
356
+ "kind": "behaviour",
357
+ "description": "BbTable caption reading-order micro-drift: .bb-table-caption is a div before the role=table root (named via aria-labelledby), no longer the table's first child",
358
+ "ruling": "plans/BBTABLE-GRID.md § Accessibility",
359
+ "guide": "components/bb-table.md § Small semantic and visual changes"
360
+ },
361
+ {
362
+ "kind": "behaviour",
363
+ "description": "BbTable busy bar now rides with the stuck header under fixed-headers (sticky moved to the .bb-table__head rowgroup; multi-row #thead heads stack instead of overlapping; aria-rowcount = totalItems + 1)",
364
+ "ruling": "plans/BBTABLE-GRID.md § Sticky + § v2.1",
365
+ "guide": "components/bb-table.md § Small semantic and visual changes"
282
366
  }
283
367
  ]
284
368
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "library": "bitboss-ui",
4
- "version": "3.0.0-beta.4",
5
- "generatedAt": "2026-08-25T12:52:57.898Z",
4
+ "version": "3.0.0-beta.8",
5
+ "generatedAt": "2026-08-26T17:02:06.381Z",
6
6
  "composables": [
7
7
  {
8
8
  "name": "useBbConfig",
@@ -12674,7 +12674,7 @@
12674
12674
  "name": "fixed",
12675
12675
  "type": "boolean | undefined",
12676
12676
  "required": false,
12677
- "description": "Defines the table layout as fixed\nmaking each column take up the same amount of space."
12677
+ "description": "Splits the width equally among the columns that declare no `width`\n(each undeclared column becomes a `minmax(0, 1fr)` track)\nmaking each column take up the same amount of space."
12678
12678
  },
12679
12679
  {
12680
12680
  "name": "fixedColumns",
@@ -12693,7 +12693,7 @@
12693
12693
  "name": "headerRowClass",
12694
12694
  "type": "Classes | undefined",
12695
12695
  "required": false,
12696
- "description": "Defines the classes to be passed to the header `<tr>`."
12696
+ "description": "Defines the classes to be passed to the header row."
12697
12697
  },
12698
12698
  {
12699
12699
  "name": "highlighted",
@@ -12712,7 +12712,7 @@
12712
12712
  "name": "inheritColumnWidths",
12713
12713
  "type": "string | boolean | undefined",
12714
12714
  "required": false,
12715
- "description": "Opt a nested table into inheriting its column widths from an ancestor\ntable's matching tracks (accounting for `select`/`actions` columns). This\nalso forces `table-layout: fixed`, so it is **off by default** — a nested\ntable renders independently unless you ask for alignment.\n- `false` (the default): do not inherit; size independently.\n- `true`: inherit from the nearest ancestor table.\n- a table id (string): inherit from the specific ancestor in the chain with\n that id rather than the immediate parent. Use this when an intermediate\n table has a different column count, so the extra columns would otherwise\n misalign against it."
12715
+ "description": "Opt a nested table into inheriting its column widths from an ancestor\ntable's matching tracks (accounting for `select`/`actions` columns). This\nalso makes the parent's tracks authoritative, so it is **off by default** — a nested\ntable renders independently unless you ask for alignment.\n- `false` (the default): do not inherit; size independently.\n- `true`: inherit from the nearest ancestor table.\n- a table id (string): inherit from the specific ancestor in the chain with\n that id rather than the immediate parent. Use this when an intermediate\n table has a different column count, so the extra columns would otherwise\n misalign against it."
12716
12716
  },
12717
12717
  {
12718
12718
  "name": "interactiveWhileLoading",
@@ -12811,7 +12811,7 @@
12811
12811
  "name": "rowClass",
12812
12812
  "type": "RowClasses<Item> | undefined",
12813
12813
  "required": false,
12814
- "description": "Defines the classes to be passed to each data `<tr>`. Can also be a\nfunction of the row's item for dynamic values."
12814
+ "description": "Defines the classes to be passed to each data row. Can also be a\nfunction of the row's item for dynamic values."
12815
12815
  },
12816
12816
  {
12817
12817
  "name": "selectable",
@@ -12849,13 +12849,13 @@
12849
12849
  "name": "tdClass",
12850
12850
  "type": "ColumnClasses<Item> | undefined",
12851
12851
  "required": false,
12852
- "description": "Defines the classes to be passed to the `<td>` elements. Can also be a function for dynamic values."
12852
+ "description": "Defines the classes to be passed to every data cell. Can also be a function for dynamic values."
12853
12853
  },
12854
12854
  {
12855
12855
  "name": "thClass",
12856
12856
  "type": "Classes | undefined",
12857
12857
  "required": false,
12858
- "description": "Defines the classes to be passed to the `<th>`."
12858
+ "description": "Defines the classes to be passed to every header cell."
12859
12859
  },
12860
12860
  {
12861
12861
  "name": "totalItems",
@@ -12880,15 +12880,15 @@
12880
12880
  "events": [
12881
12881
  {
12882
12882
  "name": "click:row",
12883
- "signature": "(e: \"click:row\", event: MouseEvent, item: any, selected: boolean): void"
12883
+ "signature": "(e: \"click:row\", event: MouseEvent, item: any): void"
12884
12884
  },
12885
12885
  {
12886
12886
  "name": "contextmenu:row",
12887
- "signature": "(e: \"contextmenu:row\", event: MouseEvent, item: any, selected: boolean): void"
12887
+ "signature": "(e: \"contextmenu:row\", event: MouseEvent, item: any): void"
12888
12888
  },
12889
12889
  {
12890
12890
  "name": "dblclick:row",
12891
- "signature": "(e: \"dblclick:row\", event: MouseEvent, item: any, selected: boolean): void"
12891
+ "signature": "(e: \"dblclick:row\", event: MouseEvent, item: any): void"
12892
12892
  },
12893
12893
  {
12894
12894
  "name": "item:selected",
@@ -12939,6 +12939,16 @@
12939
12939
  "type": "{ text: string; }",
12940
12940
  "description": "Replaces the default actions header cell content."
12941
12941
  },
12942
+ {
12943
+ "name": "header:append",
12944
+ "type": "BbTableHeaderAffixSlotProps<Item>",
12945
+ "description": "Content rendered after every data column's header cell content — the\nlabel + sort control, or the column's own `header:<key>` replacement.\nA column's `header:<key>:append` slot wins over this one for that\ncolumn; they never stack. Branch on `columnKey` for per-column output."
12946
+ },
12947
+ {
12948
+ "name": "header:prepend",
12949
+ "type": "BbTableHeaderAffixSlotProps<Item>",
12950
+ "description": "Content rendered before every data column's header cell content — the\nlabel + sort control, or the column's own `header:<key>` replacement.\nA column's `header:<key>:prepend` slot wins over this one for that\ncolumn; they never stack. Branch on `columnKey` for per-column output."
12951
+ },
12942
12952
  {
12943
12953
  "name": "header:select",
12944
12954
  "type": "{ multiple: boolean; selectAll: boolean; text: string; }",
@@ -12952,7 +12962,7 @@
12952
12962
  {
12953
12963
  "name": "no-data",
12954
12964
  "type": "object",
12955
- "description": "Content shown when the items list is empty and not loading. It fills the\ntable's own full-width empty-state cell (the colspan already accounts for\nthe selection/actions columns), so provide the *content* — a message, an\nillustration, a \"clear filters\" button — never a `<tr>`/`<td>` of your own."
12965
+ "description": "Content shown when the items list is empty and not loading. It fills the\ntable's own full-width empty-state cell (it already spans the\nselection/actions columns), so provide the *content* — a message, an\nillustration, a \"clear filters\" button — never a row/cell of your own."
12956
12966
  },
12957
12967
  {
12958
12968
  "name": "select",
@@ -12962,17 +12972,17 @@
12962
12972
  {
12963
12973
  "name": "tbody",
12964
12974
  "type": "object",
12965
- "description": "Replaces the entire `<tbody>` element. Use when you need full control over the body markup."
12975
+ "description": "Replaces the content of the body row group (`.bb-table__body`). Use when\nyou need full control over the body markup. Provide role-complete rows —\n`<div role=\"row\">` holding one `<div role=\"cell\">` per column — never a\n`<tr>`/`<td>`. A full-width cell spans with `grid-column: 1 / -1`."
12966
12976
  },
12967
12977
  {
12968
12978
  "name": "tfoot",
12969
12979
  "type": "{ columnCount: number; }",
12970
- "description": "Replaces the entire `<tfoot>` element. Use when you need full control over the footer markup."
12980
+ "description": "Fills the footer row group (`.bb-table__foot`). Use when you need a\nfooter. Provide role-complete rows — `<div role=\"row\">` holding\n`<div role=\"cell\">` elements — never a `<tr>`/`<td>`."
12971
12981
  },
12972
12982
  {
12973
12983
  "name": "thead",
12974
12984
  "type": "object",
12975
- "description": "Replaces the entire `<thead>` element. Use when you need full control over the header markup."
12985
+ "description": "Replaces the content of the header row group (`.bb-table__head`). Use when\nyou need full control over the header markup. The table is a CSS grid,\nnot a `<table>`: provide role-complete rows — `<div role=\"row\">` holding\none `<div role=\"columnheader\">` per column — and never a `<tr>`/`<th>`.\nRows are subgrid rows, so each cell lands on the matching column track."
12976
12986
  }
12977
12987
  ],
12978
12988
  "exposed": [],
@@ -53,20 +53,20 @@ naming rules, the emphasis ladder) lives in
53
53
 
54
54
  ### Surfaces & structure
55
55
 
56
- | Token | Meaning |
57
- | --------------------- | ----------------------------------------------------------------- |
58
- | `--bb-panel` | Surface/background color of panels, popovers, dialogs |
59
- | `--bb-muted` | Subtle/disabled surface (derived, neutral) |
60
- | `--bb-hover` | Hover fill for **affordances** (buttons, links, controls) |
61
- | `--bb-surface-hover` | Hover fill for **passive surfaces** (rows, cells, days) — fainter |
62
- | `--bb-pressed` | Neutral pressed/active fill |
63
- | `--bb-border` | Border color; pair with `--bb-border-w` (1px) |
64
- | `--bb-radius` | Corner radius of controls (8px) |
65
- | `--bb-radius-surface` | Corner radius of large surfaces (derived, ×1.8) |
66
- | `--bb-radius-sm` | Corner radius of small nested elements (derived) |
67
- | `--bb-shadow` | Global elevation shadow |
68
- | `--bb-panel-p` | Content padding of large surfaces (dialog, off-canvas) |
69
- | `--bb-z-overlay` | Z-index for overlays (10) |
56
+ | Token | Meaning |
57
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `--bb-panel` | Surface/background color of panels, popovers, dialogs |
59
+ | `--bb-muted` | Subtle/disabled surface (derived, neutral) |
60
+ | `--bb-hover` | Hover fill for **affordances** (buttons, links, controls) |
61
+ | `--bb-surface-hover` | Hover fill for **passive surfaces** (rows, cells, days) — fainter |
62
+ | `--bb-pressed` | Neutral pressed/active fill |
63
+ | `--bb-border` | Border color; pair with `--bb-border-w` (1px) |
64
+ | `--bb-radius` | Corner radius of controls (8px) |
65
+ | `--bb-radius-surface` | Corner radius of large surfaces (derived, ×1.8) |
66
+ | `--bb-radius-sm` | Corner radius of small nested elements (derived) |
67
+ | `--bb-shadow` | Global elevation shadow |
68
+ | `--bb-panel-p` | Content padding of large surfaces (dialog, off-canvas); axis split via the locals `--px`/`--py` on `.bb-dialog` / `.bb-offcanvas` |
69
+ | `--bb-z-overlay` | Z-index for overlays (10) |
70
70
 
71
71
  ### Color semantics
72
72
 
@@ -190,11 +190,11 @@ multipliers — never literals.
190
190
 
191
191
  ### Scale
192
192
 
193
- | Token | Default | Notes |
194
- | ---------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
195
- | `--bb-control-h` | `32px` | Master control height. Inputs are exactly this; buttons/tabs derive their size scale from it via `calc()` offsets |
196
- | `--bb-fs` | `14px` | Master font size. Component font sizes derive via `calc()` offsets |
197
- | `--bb-panel-p` | `16px` | Content padding of large surfaces (dialog, off-canvas; header/footer paddings derive from it) |
193
+ | Token | Default | Notes |
194
+ | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
195
+ | `--bb-control-h` | `32px` | Master control height. Inputs are exactly this; buttons/tabs derive their size scale from it via `calc()` offsets |
196
+ | `--bb-fs` | `14px` | Master font size. Component font sizes derive via `calc()` offsets |
197
+ | `--bb-panel-p` | `16px` | Content padding of large surfaces (dialog, off-canvas; header/footer paddings derive from it). Axis split via the component locals `--px`/`--py` on `.bb-dialog` / `.bb-offcanvas`, each branching off `var(--bb-panel-p)`; set the pair (on the component's class) only when the axes differ |
198
198
 
199
199
  ### Motion
200
200
 
@@ -272,10 +272,13 @@ Rules:
272
272
  component seals itself.
273
273
  3. **Short names are for self-consumption only.** `--h`, `--px`, `--gap`,
274
274
  `--fs`, `--r` are fine while the rules using them target the component's
275
- own boxes. If a value must be read _inside a nested foreign component_
276
- (e.g. dialog padding positioning the close button, which is itself a
277
- button defining `--px`), the token gets a distinctive name (`--dialog-px`)
278
- or — better — the formula moves to an element the component owns.
275
+ own boxes. If a value must be read _inside a nested foreign component_,
276
+ prefer moving the formula to an element the component owns, or a
277
+ distinctive name. One ruled exception (2026-08-25): the dialog/off-canvas
278
+ padding pair `--px`/`--py` is read by the nested close button's inset
279
+ formula — safe because `BbCloseButton` declares neither, a constraint that
280
+ component must keep (minting `--px`/`--py` there would silently mask the
281
+ surface's value and break the inset).
279
282
  4. **Mint only what varies.** A size variant that overrides four locals is
280
283
  four tokens, not four × six sizes. Per-size lookup tables
281
284
  (`--h-xs … --h-2xl`) are allowed only when a responsive matrix genuinely
@@ -443,19 +446,22 @@ every derivation that mixes it. It is also the trap. Write
443
446
  } /* wrong: not a <percentage> */
444
447
  ```
445
448
 
446
- and nothing errors — not the build, not the console. The computed value is
447
- just `25%`, and every ring paints 1.67× stronger than the `15%` you asked for
448
- (opacity is the ring's strength; its thickness is `--bb-ring-size`).
449
+ and nothing errors — not the build, not the console. The declaration is
450
+ discarded and the token lands on the registered `initial-value: 15%` — which
451
+ happens to be the very value this override asked for, so a light page looks
452
+ right by accident. Write `0.4` on a dark page instead and the ring quietly
453
+ drops from the shipped `40%` to `15%` (opacity is the ring's strength; its
454
+ thickness is `--bb-ring-size`).
449
455
  Percentages need the `%`; colors must be a `<color>` (`transparent` and
450
456
  `currentColor` qualify, `none` and a mistyped hex do not). Nothing in the
451
457
  toolchain catches it either: `bitboss-ui check` reads `.vue`/`.md`, never
452
458
  stylesheets.
453
459
 
454
460
  Fail-soft is only soft when the initial value is close to the real one. Every
455
- color knob is registered at its light default, so a rejected color override
456
- renders the library's own light theme. `--bb-ring-opacity` is registered at
457
- `25%`, which matches neither shipped scope (light `15%`, dark `40%`), so a
458
- rejected value there is the one that shows.
461
+ registered knob — `--bb-ring-opacity` included — is registered at its light
462
+ default, so a rejected override renders the library's own light theme. On a
463
+ dark page that is still visible: a rejected `--bb-ring-opacity` lands on the
464
+ light `15%` against the shipped dark `40%`.
459
465
 
460
466
  Typed knobs are also **animatable**. Add `.theme-animated`
461
467
  (main.css) to the element that carries the scheme class or theme overrides:
@@ -609,9 +615,10 @@ escape the host dialog's `overflow: hidden` and centering transform.
609
615
  | `--bb-overlay-color` + `--bb-overlay-opacity` | `--bb-overlay` (one color with alpha) |
610
616
  | `--bb-overlay-z-index` | `--bb-z-overlay` |
611
617
  | inline ring `color-mix()` formulas | `--bb-ring` / `--bb-ring-danger` (strength knob: `--bb-ring-opacity`) |
612
- | `--bb-dialog-px/pt/pb/gap/…` | `--bb-panel-p` (+ local derivations) |
618
+ | `--bb-dialog-px/pt/pb/gap/…` | `--bb-panel-p` + the local axis pair `--px`/`--py` on the surface roots |
613
619
  | `--bb-button-*`, `--bb-tabs-*`, `--bb-checkbox-*`, `--bb-radio-*`, `--bb-switch-*`, `--bb-rating-*`, `--bb-slider-*`, `--bb-table-cell-h`, `--bb-progress-track` | unprefixed locals in each component's CSS |
614
- | `--bb-table-offset-*`, `--bb-table-fill`, `--bb-table-natural-width` (JS plumbing) | unprefixed locals on `.bb-table` (`--offset-*`, `--fill`, `--natural-width`) |
620
+ | `--bb-table-offset-*` (JS plumbing) | unprefixed locals on `.bb-table` (`--offset-*`) |
621
+ | `--bb-table-fill`, `--bb-table-natural-width` | **removed** with the grid substrate — nothing publishes them any more (v3.0) |
615
622
  | `--bb-table-{id}-track-{key}` (parent→nested width bridge) | `--table-{id}-track-{key}` (distinctive cross-component name, rule 3) |
616
623
  | `--bb-select-option-*`, `--bb-primary-base`, all `--x-light`/`--x-dark` shadows | deleted (dead) |
617
624
 
@@ -506,7 +506,7 @@ template emits are already styled by the public `bitboss-ui/styles.css`.
506
506
 
507
507
  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).
508
508
 
509
- 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.
509
+ 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. One deliberate boundary: the rule's "did you mean `<prop>`?" hints for a mistyped/unknown attribute are advisory message text, not fixes — `eslint --fix` (including an unattended run in a pre-commit hook) never applies them; only the registered v2→v3 migrations above are auto-fixed. A guess applied blind across a codebase would be a silent behavior change, so acting on a hint is always a per-site decision. **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.
510
510
 
511
511
  **The manifest ships with the package** — `dist/ai/components.json` is inside
512
512
  the tarball, and the rules resolve it relative to their own file, so a normal
@@ -50,16 +50,18 @@ v2's `BbDialog` was a re-export of `BaseDialog`; v3 makes it first-class
50
50
  ## CSS and tokens
51
51
 
52
52
  The `--bb-dialog-*` family is gone. Padding comes from the shared
53
- `--bb-panel-p`; the rest are unprefixed locals on `.bb-dialog` or plain CSS
54
- rules. `BbOffCanvas` is identical throughout with `.bb-offcanvas__*` parts.
53
+ `--bb-panel-p` and the local axis pair `--px` / `--py` on `.bb-dialog`; the
54
+ rest are unprefixed locals on `.bb-dialog` or plain CSS rules. `BbOffCanvas`
55
+ is identical throughout with `.bb-offcanvas__*` parts and the same `--px` /
56
+ `--py` names.
55
57
 
56
58
  | v2 token | v3 |
57
59
  | -------------------------------------------- | -------------------------------------------------------------------------------------- |
58
- | `--bb-dialog-px` / `--bb-dialog-py` (`24px`) | `--bb-panel-p` (`16px`) — **one** value on both axes; asymmetry needs a CSS rule |
60
+ | `--bb-dialog-px` / `--bb-dialog-py` (`24px`) | local `--px` / `--py` on `.bb-dialog`, each branching off `--bb-panel-p` (`16px`) |
59
61
  | `--bb-dialog-close` (`12px`) | **delete it** — v3's default already reproduces v2's control size |
60
62
  | `--bb-dialog-title-size` (`18px`) | local `--dialog-title-fs` on `.bb-dialog`, default `calc(var(--bb-fs) + 2px)` = `16px` |
61
63
  | `--bb-dialog-title-weight` (`600`) | no knob — `500` is hard-coded; override with a CSS rule |
62
- | — | local `--dialog-gap`, default `--bb-panel-p` — the body↔footer vertical rhythm |
64
+ | — | local `--dialog-gap`, default `--py` — the body↔footer vertical rhythm |
63
65
 
64
66
  ### `--bb-dialog-close` → delete it
65
67
 
@@ -102,43 +104,46 @@ specificity — a two-class `.my-dialog .bb-dialog__title` loses.
102
104
 
103
105
  ### Asymmetric padding
104
106
 
105
- `--bb-panel-p` is a single value used on both axes; there is no
106
- `--bb-panel-px`/`--bb-panel-py` pair. If your v2 theme set `--bb-dialog-px` ≠
107
- `--bb-dialog-py`, set `--bb-panel-p` to the **horizontal** value (it also
108
- positions the close button) and override the vertical with a `padding-block`
109
- rule on the part classes.
107
+ The local pair `--px` / `--py` splits the axes; each branches off
108
+ `var(--bb-panel-p)`, so setting `--bb-panel-p` alone still moves both. Like
109
+ every unprefixed component token, set the pair on the component's class, never
110
+ `:root` (other components also use `--px` as a sealed local of their own, so a
111
+ dialog-scoped value never leaks into the buttons or alerts inside). v2's `--bb-dialog-px` / `--bb-dialog-py` map onto it directly:
110
112
 
111
- ```css
112
- /* v2: --bb-dialog-px: 24px; header --bb-dialog-py: 10px; body --bb-dialog-py: 20px */
113
+ ````css
114
+ /* v2: --bb-dialog-px: 24px; --bb-dialog-py: 10px */
113
115
  .my-dialog {
114
- --bb-panel-p: 24px; /* horizontal + close-button inset */
116
+ --px: 24px; /* horizontal padding + close-button inline inset */
117
+ --py: 10px; /* vertical padding + close-button block inset */
115
118
  }
119
+ ``` Per-**part** vertical differences (header `py: 10px`,
120
+ body `py: 20px`) work by scoping the token to the part class — the padding
121
+ rules resolve it on the part elements and the token inherits:
122
+
123
+ ```css
116
124
  .my-dialog .bb-dialog__header {
117
- padding-block: 10px 5px; /* the library halves the header's bottom padding */
125
+ --py: 10px; /* top 10px, bottom py/2 = 5px; the × inset follows */
118
126
  }
119
- .my-dialog .bb-dialog__body .bb-dialog__body-content {
120
- padding-block: 0 20px;
127
+ .my-dialog .bb-dialog__body-content {
128
+ --py: 20px; /* bottom padding, and top in the headerless case */
121
129
  }
122
- .my-dialog .bb-dialog__body:first-child .bb-dialog__body-content {
123
- padding-top: 20px;
124
- }
125
- .my-dialog .bb-dialog__footer {
126
- padding-block: 20px;
127
- }
128
- ```
130
+ ````
129
131
 
130
- Use `padding-block`, not the `padding` shorthand, so the horizontal half keeps
131
- tracking `--bb-panel-p`. Library selectors are `.bb-dialog .bb-dialog__header`
132
+ Derived spacings scale with it (header bottom is `py / 2`; the close button's
133
+ block inset tracks the header's `py`). For exact literals instead of those
134
+ ratios, use a `padding-block` rule on the part class — `padding-block`, not
135
+ the `padding` shorthand, so the horizontal half keeps tracking
136
+ `--px`. Library selectors are `.bb-dialog .bb-dialog__header`
132
137
  (0,2,0), `.bb-dialog__body-content` (0,1,0),
133
138
  `.bb-dialog__body:first-child .bb-dialog__body-content` (0,2,1) and
134
- `.bb-dialog .bb-dialog__footer` (0,2,0) — the three-class app rules above beat
135
- all four.
139
+ `.bb-dialog .bb-dialog__footer` (0,2,0) — a three-class app rule beats all
140
+ four.
136
141
 
137
142
  ### `--dialog-gap`
138
143
 
139
144
  If only the vertical rhythm _between parts_ differs, `--dialog-gap` does it
140
- without a padding rule — it replaces `--bb-panel-p` in the body's bottom padding
141
- and the footer's block padding:
145
+ without a padding rule — it replaces `--py` in the body's bottom
146
+ padding and the footer's block padding:
142
147
 
143
148
  ```css
144
149
  .my-dialog {
@@ -147,7 +152,8 @@ and the footer's block padding:
147
152
  ```
148
153
 
149
154
  It does **not** reach the header's top padding, any horizontal padding, or the
150
- body's top padding in the headerless case; those still read `--bb-panel-p`.
155
+ body's top padding in the headerless case; those still read `--py` (and
156
+ `--px` for the horizontal slots).
151
157
 
152
158
  ### `.bb-confirm`
153
159