bitboss-ui 3.0.0-beta.32 → 3.0.0-beta.33

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 (261) hide show
  1. package/README.md +28 -27
  2. package/dist/ai/BbAsterisk.md +3 -2
  3. package/dist/ai/BbAvatar.md +1 -0
  4. package/dist/ai/BbBadge.md +2 -2
  5. package/dist/ai/BbBadgeButton.md +2 -2
  6. package/dist/ai/BbBreadcrumbs.md +1 -1
  7. package/dist/ai/BbButton.md +22 -1
  8. package/dist/ai/BbCheckbox.md +7 -4
  9. package/dist/ai/BbCheckboxGroup.md +255 -14
  10. package/dist/ai/BbColorInput.md +4 -4
  11. package/dist/ai/BbColorPalette.md +9 -1
  12. package/dist/ai/BbConfirm.md +4 -2
  13. package/dist/ai/BbDatePicker.md +1 -1
  14. package/dist/ai/BbDatePickerInput.md +5 -5
  15. package/dist/ai/BbDialog.md +15 -6
  16. package/dist/ai/BbDropdown.md +6 -5
  17. package/dist/ai/BbDropdownButton.md +2 -2
  18. package/dist/ai/BbDropzone.md +1 -1
  19. package/dist/ai/BbNumberInput.md +9 -9
  20. package/dist/ai/BbOffCanvas.md +29 -8
  21. package/dist/ai/BbPagination.md +31 -17
  22. package/dist/ai/BbPopover.md +2 -2
  23. package/dist/ai/BbProgress.md +20 -11
  24. package/dist/ai/BbRadio.md +5 -3
  25. package/dist/ai/BbRadioGroup.md +161 -50
  26. package/dist/ai/BbRating.md +1 -1
  27. package/dist/ai/BbSelect.md +13 -6
  28. package/dist/ai/BbSelectPopover.md +1 -1
  29. package/dist/ai/BbSlider.md +2 -2
  30. package/dist/ai/BbSmoothHeight.md +2 -2
  31. package/dist/ai/BbSwitch.md +18 -19
  32. package/dist/ai/BbSwitchGroup.md +69 -9
  33. package/dist/ai/BbTabs.md +1 -1
  34. package/dist/ai/BbTabsList.md +1 -1
  35. package/dist/ai/BbTabsPanes.md +1 -1
  36. package/dist/ai/BbTabsRoot.md +1 -1
  37. package/dist/ai/BbTag.md +6 -4
  38. package/dist/ai/BbTextInput.md +14 -9
  39. package/dist/ai/BbTextarea.md +5 -5
  40. package/dist/ai/BbTimePicker.md +1 -1
  41. package/dist/ai/BbTimePickerInput.md +17 -2
  42. package/dist/ai/BbTree.md +7 -0
  43. package/dist/ai/ChipsBox.md +2 -2
  44. package/dist/ai/CommonField.md +2 -2
  45. package/dist/ai/OptionsContainer.md +4 -2
  46. package/dist/ai/changelog.json +181 -4
  47. package/dist/ai/components.json +2128 -252
  48. package/dist/ai/composables/useBbDropdownContext.md +168 -0
  49. package/dist/ai/composables/useBbFormContext.md +15 -1
  50. package/dist/ai/composables/useBbTableContext.md +30 -14
  51. package/dist/ai/composables/useConfirm.md +2 -1
  52. package/dist/ai/composables/useMobile.md +17 -10
  53. package/dist/ai/composables/useToast.md +3 -0
  54. package/dist/ai/guides/agent-contract.md +4 -2
  55. package/dist/ai/guides/design-language.md +9 -6
  56. package/dist/ai/guides/design-tokens.md +12 -11
  57. package/dist/ai/guides/inline-edit-playbook.md +6 -1
  58. package/dist/ai/guides/installation-and-plugin-setup.md +4 -4
  59. package/dist/ai/guides/migration/components/bb-checkbox-group.md +43 -29
  60. package/dist/ai/guides/migration/components/bb-checkbox.md +1 -0
  61. package/dist/ai/guides/migration/components/bb-color-input.md +5 -5
  62. package/dist/ai/guides/migration/components/bb-date-picker-input.md +10 -9
  63. package/dist/ai/guides/migration/components/bb-dropdown.md +1 -1
  64. package/dist/ai/guides/migration/components/bb-dropzone.md +8 -0
  65. package/dist/ai/guides/migration/components/bb-intersection.md +1 -1
  66. package/dist/ai/guides/migration/components/bb-rating.md +8 -7
  67. package/dist/ai/guides/migration/components/bb-select-popover.md +1 -1
  68. package/dist/ai/guides/migration/components/bb-select.md +2 -1
  69. package/dist/ai/guides/migration/components/bb-slider.md +7 -6
  70. package/dist/ai/guides/migration/components/bb-tag.md +1 -0
  71. package/dist/ai/guides/migration/components/bb-text-input.md +6 -6
  72. package/dist/ai/guides/migration/components/bb-toast.md +1 -1
  73. package/dist/ai/guides/migration/components/bb-tree.md +8 -8
  74. package/dist/ai/guides/migration/components/use-confirm.md +8 -0
  75. package/dist/ai/guides/migration/v2-to-v3.md +83 -79
  76. package/dist/ai/guides/options-items-playbook.md +12 -4
  77. package/dist/ai/guides/page-shell.md +1 -1
  78. package/dist/ai/guides/passthrough.md +43 -19
  79. package/dist/ai/guides/validated-forms.md +8 -2
  80. package/dist/ai/index.md +2 -1
  81. package/dist/ai/recipes/inertia/approvals-inbox.md +21 -4
  82. package/dist/ai/recipes/inertia/inline-edit-workspace.md +20 -3
  83. package/dist/ai/recipes/inertia/onboarding.md +26 -4
  84. package/dist/ai/recipes/inertia/ownership-atlas.md +1 -0
  85. package/dist/ai/recipes/inertia/record-form.md +9 -1
  86. package/dist/ai/recipes/inertia/records-workspace.md +52 -25
  87. package/dist/ai/recipes/inertia/upload-center.md +14 -2
  88. package/dist/ai/recipes/inertia/wizard-form.md +16 -3
  89. package/dist/ai/recipes/nuxt/approvals-inbox.md +21 -4
  90. package/dist/ai/recipes/nuxt/inline-edit-workspace.md +20 -3
  91. package/dist/ai/recipes/nuxt/onboarding.md +26 -4
  92. package/dist/ai/recipes/nuxt/record-form.md +22 -4
  93. package/dist/ai/recipes/nuxt/records-workspace.md +48 -24
  94. package/dist/ai/recipes/nuxt/upload-center.md +14 -2
  95. package/dist/ai/recipes/nuxt/wizard-form.md +16 -3
  96. package/dist/ai/recipes/vue/approvals-inbox.md +21 -4
  97. package/dist/ai/recipes/vue/inline-edit-workspace.md +20 -3
  98. package/dist/ai/recipes/vue/onboarding.md +28 -4
  99. package/dist/ai/recipes/vue/record-form.md +22 -4
  100. package/dist/ai/recipes/vue/records-workspace.md +53 -25
  101. package/dist/ai/recipes/vue/upload-center.md +14 -2
  102. package/dist/ai/recipes/vue/wizard-form.md +18 -3
  103. package/dist/ai/source/BbAccordion.md +4 -1
  104. package/dist/ai/source/BbAvatar.md +4 -0
  105. package/dist/ai/source/BbBadge.md +14 -12
  106. package/dist/ai/source/BbBadgeButton.md +14 -12
  107. package/dist/ai/source/BbButton.md +29 -4
  108. package/dist/ai/source/BbCalendar.md +12 -4
  109. package/dist/ai/source/BbCheckbox.md +10 -7
  110. package/dist/ai/source/BbCheckboxGroup.md +211 -69
  111. package/dist/ai/source/BbColorInput.md +8 -5
  112. package/dist/ai/source/BbColorPalette.md +22 -8
  113. package/dist/ai/source/BbDatePickerInput.md +19 -5
  114. package/dist/ai/source/BbDialog.md +56 -3
  115. package/dist/ai/source/BbDropdown.md +20 -7
  116. package/dist/ai/source/BbDropdownButton.md +20 -6
  117. package/dist/ai/source/BbDropdownGroup.md +13 -5
  118. package/dist/ai/source/BbDropzone.md +24 -5
  119. package/dist/ai/source/BbIndicator.md +5 -0
  120. package/dist/ai/source/BbNumberInput.md +12 -6
  121. package/dist/ai/source/BbOffCanvas.md +184 -13
  122. package/dist/ai/source/BbPagination.md +46 -18
  123. package/dist/ai/source/BbProgress.md +23 -11
  124. package/dist/ai/source/BbRadio.md +10 -7
  125. package/dist/ai/source/BbRadioGroup.md +204 -67
  126. package/dist/ai/source/BbRating.md +21 -7
  127. package/dist/ai/source/BbSelect.md +32 -21
  128. package/dist/ai/source/BbSelectPopover.md +140 -15
  129. package/dist/ai/source/BbSlider.md +23 -9
  130. package/dist/ai/source/BbSwitch.md +4 -4
  131. package/dist/ai/source/BbSwitchGroup.md +178 -61
  132. package/dist/ai/source/BbTable.md +10 -1
  133. package/dist/ai/source/BbTabsList.md +12 -3
  134. package/dist/ai/source/BbTag.md +10 -7
  135. package/dist/ai/source/BbTextInput.md +24 -23
  136. package/dist/ai/source/BbTextarea.md +7 -5
  137. package/dist/ai/source/BbTimePicker.md +11 -4
  138. package/dist/ai/source/BbTimePickerInput.md +7 -4
  139. package/dist/ai/source/BbTooltip.md +32 -6
  140. package/dist/ai/source/CommonField.md +121 -38
  141. package/dist/ai/source/CommonFieldInput.md +3 -0
  142. package/dist/ai/source/CommonTimeSelector.md +3 -1
  143. package/dist/ai/source/GroupedListBox.md +80 -85
  144. package/dist/ai/source/OptionsContainer.md +148 -93
  145. package/dist/components/BbAccordion/BbAccordion.vue_vue_type_script_setup_true_lang.js +8 -8
  146. package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +5 -4
  147. package/dist/components/BbCalendar/useCalendarGrid.js +21 -21
  148. package/dist/components/BbCalendar/useDayGrid.js +14 -14
  149. package/dist/components/BbCheckbox/BbCheckbox.vue.d.ts +1 -1
  150. package/dist/components/BbCheckbox/BbCheckbox.vue_vue_type_script_setup_true_lang.js +3 -3
  151. package/dist/components/BbCheckbox/types.d.ts +6 -3
  152. package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +77 -71
  153. package/dist/components/BbCheckboxGroup/types.d.ts +30 -7
  154. package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +3 -3
  155. package/dist/components/BbColorInput/types.d.ts +6 -3
  156. package/dist/components/BbColorPalette/BbColorPalette.vue_vue_type_script_setup_true_lang.js +1 -1
  157. package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +3 -3
  158. package/dist/components/BbDatePickerInput/types.d.ts +6 -3
  159. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +138 -138
  160. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +277 -277
  161. package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +333 -305
  162. package/dist/components/BbDropdown/types.d.ts +1 -1
  163. package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +23 -22
  164. package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +143 -140
  165. package/dist/components/BbDropzone/types.d.ts +2 -1
  166. package/dist/components/BbIcon/iconCache.js +2 -2
  167. package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +4 -4
  168. package/dist/components/BbNumberInput/types.d.ts +6 -3
  169. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +217 -211
  170. package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +59 -55
  171. package/dist/components/BbPagination/types.d.ts +16 -10
  172. package/dist/components/BbPagination/types.js +2 -0
  173. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +11 -11
  174. package/dist/components/BbProgress/BbProgress.vue_vue_type_script_setup_true_lang.js +13 -12
  175. package/dist/components/BbProgress/types.d.ts +6 -3
  176. package/dist/components/BbProgress/types.js +1 -1
  177. package/dist/components/BbRadio/BbRadio.vue.d.ts +1 -1
  178. package/dist/components/BbRadio/BbRadio.vue_vue_type_script_setup_true_lang.js +3 -3
  179. package/dist/components/BbRadio/types.d.ts +6 -3
  180. package/dist/components/BbRadioGroup/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +37 -31
  181. package/dist/components/BbRadioGroup/types.d.ts +30 -7
  182. package/dist/components/BbRating/BbRating.vue_vue_type_script_setup_true_lang.js +6 -3
  183. package/dist/components/BbRating/types.d.ts +6 -3
  184. package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +170 -166
  185. package/dist/components/BbSelect/types.d.ts +6 -4
  186. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +442 -408
  187. package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +9 -6
  188. package/dist/components/BbSlider/types.d.ts +6 -4
  189. package/dist/components/BbSwitch/BbSwitch.vue.d.ts +1 -1
  190. package/dist/components/BbSwitch/BbSwitch.vue_vue_type_script_setup_true_lang.js +3 -3
  191. package/dist/components/BbSwitchGroup/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +77 -71
  192. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +103 -100
  193. package/dist/components/BbTabs/BbTabsList.vue_vue_type_script_setup_true_lang.js +21 -21
  194. package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +91 -91
  195. package/dist/components/BbTag/types.d.ts +6 -4
  196. package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +9 -16
  197. package/dist/components/BbTextInput/types.d.ts +6 -4
  198. package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +3 -3
  199. package/dist/components/BbTextarea/types.d.ts +6 -4
  200. package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +3 -3
  201. package/dist/components/BbTimePickerInput/types.d.ts +6 -3
  202. package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +91 -90
  203. package/dist/components/CommonField/CommonField.vue.d.ts +1 -1
  204. package/dist/components/CommonField/CommonField.vue_vue_type_script_setup_true_lang.js +60 -53
  205. package/dist/components/CommonField/types.d.ts +7 -6
  206. package/dist/components/CommonFieldInput/CommonFieldInput.vue_vue_type_script_setup_true_lang.js +1 -1
  207. package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +137 -124
  208. package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +49 -33
  209. package/dist/components/OptionsContainer/types.d.ts +15 -3
  210. package/dist/composables/useBaseOptions.js +47 -39
  211. package/dist/composables/useBbTableContext.js +7 -6
  212. package/dist/composables/useBbTabsContext.js +64 -64
  213. package/dist/composables/useClosingInert.d.ts +4 -4
  214. package/dist/composables/useCoherence.js +1 -1
  215. package/dist/composables/useConfirm.js +48 -42
  216. package/dist/composables/useKeyboardInset.d.ts +28 -0
  217. package/dist/composables/useKeyboardInset.js +32 -0
  218. package/dist/composables/usePassthrough.js +14 -4
  219. package/dist/deprecation/ai-deprecations.json.d.ts +221 -0
  220. package/dist/deprecation/ai-deprecations.json.js +1 -1
  221. package/dist/index.d.ts +1 -1
  222. package/dist/llms-full.txt +1796 -585
  223. package/dist/llms-medium.txt +18 -13
  224. package/dist/llms.txt +4 -3
  225. package/dist/styles.css +1 -1
  226. package/dist/types/FieldLayout.d.ts +32 -0
  227. package/dist/types/ptComponentMap.d.ts +2 -2
  228. package/dist/utilities/functions/isImeComposing.d.ts +7 -0
  229. package/dist/utilities/functions/isImeComposing.js +4 -0
  230. package/dist/utils/overlayEscapeStack.d.ts +10 -0
  231. package/dist/utils/overlayEscapeStack.js +5 -3
  232. package/dist/validated/BbCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
  233. package/dist/validated/BbCheckboxGroup.vue.d.ts +4 -2
  234. package/dist/validated/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +4 -2
  235. package/dist/validated/BbColorInput.vue_vue_type_script_setup_true_lang.js +1 -1
  236. package/dist/validated/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
  237. package/dist/validated/BbNumberInput.vue_vue_type_script_setup_true_lang.js +1 -1
  238. package/dist/validated/BbRadioGroup.vue.d.ts +4 -2
  239. package/dist/validated/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +4 -2
  240. package/dist/validated/BbRating.vue_vue_type_script_setup_true_lang.js +1 -1
  241. package/dist/validated/BbSelect.vue.d.ts +1 -1
  242. package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  243. package/dist/validated/BbSlider.vue_vue_type_script_setup_true_lang.js +1 -1
  244. package/dist/validated/BbSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
  245. package/dist/validated/BbSwitchGroup.vue.d.ts +4 -2
  246. package/dist/validated/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +4 -2
  247. package/dist/validated/BbTag.vue_vue_type_script_setup_true_lang.js +1 -1
  248. package/dist/validated/BbTextInput.vue_vue_type_script_setup_true_lang.js +1 -1
  249. package/dist/validated/BbTextarea.vue_vue_type_script_setup_true_lang.js +1 -1
  250. package/dist/validated/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
  251. package/dist/vite-plugin.d.ts +8 -0
  252. package/dist/vite.js +115 -98
  253. package/llms.txt +4 -3
  254. package/package.json +4 -4
  255. package/scripts/lib/component-attr-targets.ts +117 -0
  256. package/scripts/lib/component-tree.ts +43 -6
  257. package/scripts/lib/html-attributes.mjs +220 -0
  258. package/scripts/lib/validate-bb-markup.mjs +17 -3
  259. package/dist/types/InputDirection.d.ts +0 -15
  260. package/dist/utils/passthroughTestKit.d.ts +0 -54
  261. package/dist/utils/swipeTestKit.d.ts +0 -9
package/README.md CHANGED
@@ -17,9 +17,10 @@ Published artifacts are **`dist/`** (ESM JavaScript, `.d.ts`, the two stylesheet
17
17
  ### TypeScript: use `vue-tsc` 3
18
18
 
19
19
  Use **`vue-tsc` 3 or newer**. Under `vue-tsc` 2 the template type-checker
20
- silently checks **nothing** about the props of the nine components declared with
21
- `generic="T"` — `BbSelect`, `BbSelectPopover`, `BbTable`, `BbTabs`,
22
- `BbTabsRoot`, `BbTree`, `BbRadioGroup`, `BbCheckboxGroup`, `BbSwitchGroup` —
20
+ silently checks **nothing** about the props of the nine public components that
21
+ use `<script setup generic="…">` — `BbSelect`, `BbSelectPopover`, `BbTable`,
22
+ `BbTabs`, `BbTabsRoot`, `BbTree`, `BbRadioGroup`, `BbCheckboxGroup`,
23
+ `BbSwitchGroup` —
23
24
  because they emit as generic functions rather than `DefineComponent<…>`, a shape
24
25
  Vue Language Tools 2 cannot read props out of.
25
26
 
@@ -39,7 +40,7 @@ Full detail: `ai/guides/installation-and-plugin-setup.md`.
39
40
 
40
41
  | Composable | Role |
41
42
  | ---------------------- | --------------------------------------------------------------------- |
42
- | `useBbConfig` | Global options for the kit (locale, defaults, etc.). |
43
+ | `useBbConfig` | `setConfig()` patches the one runtime-editable kit option: `locale`. |
43
44
  | `useBbDropdownContext` | Read a `BbDropdown`'s resolved groups/selections from anywhere by id. |
44
45
  | `useBbTableContext` | Imperative handle onto a `BbTable` by id. |
45
46
  | `useBbTabsContext` | Programmatic tab selection for a `BbTabs` by id. |
@@ -116,15 +117,15 @@ For the public **Bb\*** components, the package exports matching **props**, **ev
116
117
  **For AI coding agents:**
117
118
 
118
119
  The same knowledge base the npm package ships is served publicly over a CDN, so
119
- an agent can read it before anything is installed. `@alpha` tracks the latest
120
- v3 prerelease; pin an exact version (`bitboss-ui@3.0.0-alpha.5`) for stable
120
+ an agent can read it before anything is installed. `@beta` tracks the latest
121
+ v3 prerelease; pin an exact version (`bitboss-ui@3.0.0-beta.32`) for stable
121
122
  links. Sizes grow with the catalogue — treat them as the current order of
122
123
  magnitude, not a contract.
123
124
 
124
- - [Core knowledge base](https://llms-bitboss-ui.bitboss.it/dist/llms-medium.txt) (~110 KB) — **start here if you can only fetch one file.** Hard rules, setup, component picker, design language, and the full component catalogue.
125
- - [Index](https://llms-bitboss-ui.bitboss.it/llms.txt) (~19 KB) — link index into every document.
125
+ - [Core knowledge base](https://llms-bitboss-ui.bitboss.it/dist/llms-medium.txt) (~190 KB) — **start here if you can only fetch one file.** Hard rules, setup, component picker, design language, and the full component catalogue.
126
+ - [Index](https://llms-bitboss-ui.bitboss.it/llms.txt) (~22 KB) — link index into every document.
126
127
  - [components.json](https://llms-bitboss-ui.bitboss.it/dist/ai/components.json) — machine-readable API surface, for programmatic validation.
127
- - [Complete knowledge base](https://llms-bitboss-ui.bitboss.it/dist/llms-full.txt) (~3.2 MB) — everything concatenated. Bulk ingestion only; too large to prompt with.
128
+ - [Complete knowledge base](https://llms-bitboss-ui.bitboss.it/dist/llms-full.txt) (~4.3 MB) — everything concatenated. Bulk ingestion only; too large to prompt with.
128
129
 
129
130
  Already installed? Prefer the local copy under `node_modules/bitboss-ui/dist/ai/`,
130
131
  run `npx bitboss-ui ai-init` to write agent pointers, and
@@ -142,24 +143,24 @@ This package is developed and maintained by [BitBoss](https://www.bitboss.it/),
142
143
 
143
144
  ## Installation
144
145
 
145
- **v3 is a prerelease: the `@alpha` tag is required.** A bare
146
+ **v3 is a prerelease: the `@beta` tag is required.** A bare
146
147
  `npm install bitboss-ui` resolves to the v2 line, which has no build plugin and
147
148
  no `dist/ai` knowledge base — if you are following the v3 docs, that install
148
149
  will not work.
149
150
 
150
151
  ```bash
151
- npm install bitboss-ui@alpha
152
+ npm install bitboss-ui@beta
152
153
  ```
153
154
 
154
155
  ```bash
155
- pnpm add bitboss-ui@alpha
156
+ pnpm add bitboss-ui@beta
156
157
  ```
157
158
 
158
159
  ```bash
159
- yarn add bitboss-ui@alpha
160
+ yarn add bitboss-ui@beta
160
161
  ```
161
162
 
162
- Verify with `npm ls bitboss-ui` — it must report `3.0.0-alpha.*`.
163
+ Verify with `npm ls bitboss-ui` — it must report `3.0.0-beta.*`.
163
164
 
164
165
  ---
165
166
 
@@ -501,17 +502,17 @@ A standard `llms.txt` discovery file is also published at:
501
502
 
502
503
  ### Knowledge-base layout
503
504
 
504
- | Path | Contents |
505
- | ------------------------------------- | ----------------------------------------- |
506
- | `dist/ai/guides/ai-router.md` | **Start here** — which file to open next |
507
- | `dist/ai/index.md` | Full catalogue + agent rules |
508
- | `dist/ai/components.json` | Machine-readable API (props/events/slots) |
509
- | `dist/ai/<ComponentName>.md` | Guide-first contract per component |
510
- | `dist/ai/composables/<name>.md` | Composable docs |
511
- | `dist/ai/guides/agent-contract.md` | Mandatory agent rules |
512
- | `dist/ai/guides/*-playbook.md` | Coherence / fetch / options / inline-edit |
513
- | `dist/ai/guides/migration/` | v2 → v3 upgrade corpus |
514
- | `dist/ai/recipes/{vue,nuxt,inertia}/` | Page-level assemblies (11 × 3) |
505
+ | Path | Contents |
506
+ | ------------------------------------- | ------------------------------------------------------------------------------------ |
507
+ | `dist/ai/guides/ai-router.md` | **Start here** — which file to open next |
508
+ | `dist/ai/index.md` | Full catalogue + agent rules |
509
+ | `dist/ai/components.json` | Machine-readable API (props/events/slots) |
510
+ | `dist/ai/<ComponentName>.md` | Guide-first contract per component |
511
+ | `dist/ai/composables/<name>.md` | Composable docs |
512
+ | `dist/ai/guides/agent-contract.md` | Mandatory agent rules |
513
+ | `dist/ai/guides/*-playbook.md` | Coherence / fetch / options / inline-edit |
514
+ | `dist/ai/guides/migration/` | v2 → v3 upgrade corpus |
515
+ | `dist/ai/recipes/{vue,nuxt,inertia}/` | Page-level assemblies (9 vue / 9 nuxt / 10 inertia; Ownership Atlas is inertia-only) |
515
516
 
516
517
  ### Package exports
517
518
 
@@ -571,8 +572,8 @@ To make a report actionable, include:
571
572
  - whether it reproduces in dev, in a production build, or only under SSR
572
573
  - the smallest component snippet that shows it
573
574
 
574
- v3 ships as a prerelease (`npm i bitboss-ui@alpha`). Expect breaking changes
575
- between alphas; each one is recorded in the migration guides under
575
+ v3 ships as a prerelease (`npm i bitboss-ui@beta`). Expect breaking changes
576
+ between prereleases; each one is recorded in the migration guides under
576
577
  [`ai/guides/migration/`](ai/guides/migration/).
577
578
 
578
579
  ---
@@ -4,7 +4,7 @@
4
4
 
5
5
  - Safety level: **Consumer API**
6
6
  - Group: **Other**
7
- - Description: Presentational required-field marker (red "*"). No props/slots/events; placed in custom label content.
7
+ - Description: Presentational required-field marker (red "*"). No behaviour props/slots/events (only `pt`); placed in custom label content.
8
8
  - Usage rule: Prefer this component for consumer-facing implementations.
9
9
  - Implementation source (only when the contract below is not enough): [source/BbAsterisk.md](./source/BbAsterisk.md)
10
10
 
@@ -13,7 +13,8 @@
13
13
  ### When to use
14
14
 
15
15
  `BbAsterisk` is the small red `*` that marks a field as required. It is purely
16
- presentational — it has no props, slots, or events, and it does not read
16
+ presentational — it has no behaviour props, slots, or events (only `pt` to
17
+ restyle its root), and it does not read
17
18
  `required` or drive validation. You render it yourself wherever you build custom
18
19
  label content, so the marker matches the library's styling (`--bb-danger`).
19
20
 
@@ -648,6 +648,7 @@ Set these on the element, or on a class you put on it, to retune this component
648
648
  | `--bg` | `.bb-avatar` | `var(--bb-primary)` | fallback surface (an image covers it) |
649
649
  | `--fg` | `.bb-avatar` | `var(--bb-primary-fg)` | Initials; the built-in icon is an 80/20 mix of this toward --bg |
650
650
  | `--default-icon-size` | `.bb-avatar` | `clamp(14px, calc(var(--size) * 0.7), 48px)` | Built-in fallback icon: 70% of the avatar, floored at 14px and capped at 48px. `--size` is the avatar's own box, set inline from the `size` prop. |
651
+ | `--elev` | `.bb-avatar` | `var(--bb-elev-sm)` | The theme's resting lift on small chrome (Q44.15), declared here so an ancestor's `--elev` cannot reach in; `--elev: none` flattens one. |
651
652
  | `--size` | `.bb-avatar__default-icon` | `var(--default-icon-size)` | |
652
653
 
653
654
  ## Component tree
@@ -891,11 +891,11 @@ Set these on the element, or on a class you put on it, to retune this component
891
891
  | `--border-width` | `.bb-badge` | `0px` | Pill border. The `border` shorthand is always present, so the width is what turns it on — the filled variants leave it at 0 and the bordered ones (secondary/destructive/outline) step up to `--bb-border-w-sm`. Nothing compensates for it: the… |
892
892
  | `--border-color` | `.bb-badge` | `transparent` | |
893
893
  | `--ring` | `.bb-badge` | `var(--bb-primary-ring)` | focus ring, per variant |
894
- | `--min-size-md` | `.bb-badge` | `16px` | The md value. Only the default --min-size below reads it; the size modifiers set --min-size directly. |
894
+ | `--min-size-md` | `.bb-badge` | `calc(var(--bb-control-h) - 16px)` | 16px at the default |
895
895
  | `--min-size` | `.bb-badge` | `var(--min-size-md)` | pill min height and min width |
896
896
  | `--elev` | `.bb-badge` | `var(--bb-elev-sm)` | Resting lift. Invisible while `--bb-elev` is its default transparent layer. A chip is small enough that a theme wanting a different lift here than on a button sets `--elev` on this class. |
897
897
  | `--r` | `.bb-badge` | `calc(var(--bb-radius) * 0.5)` | Pill corners; the body and trailing wrappers round their outer edges to match |
898
- | `--font-size` | `.bb-badge` | `10px` | label |
898
+ | `--font-size` | `.bb-badge` | `calc(var(--bb-fs) - 4px)` | Label. Sizes are offsets from the density masters (--bb-fs, --bb-control-h), like BbButton's, so a denser theme scales the pill. |
899
899
  | `--icon-size` | `.bb-badge` | `12px` | leading/append icons (passed to BbIcon as --size) |
900
900
  | `--gap` | `.bb-badge` | `3px` | space between label, icons and the trailing side |
901
901
  | `--padding-inline` | `.bb-badge` | `6px` | base side padding; the modifiers below scale it per side |
@@ -87,11 +87,11 @@ Set these on the element, or on a class you put on it, to retune this component
87
87
  | `--border-width` | `.bb-badge` | `0px` | Pill border. The `border` shorthand is always present, so the width is what turns it on — the filled variants leave it at 0 and the bordered ones (secondary/destructive/outline) step up to `--bb-border-w-sm`. Nothing compensates for it: the… |
88
88
  | `--border-color` | `.bb-badge` | `transparent` | |
89
89
  | `--ring` | `.bb-badge` | `var(--bb-primary-ring)` | focus ring, per variant |
90
- | `--min-size-md` | `.bb-badge` | `16px` | The md value. Only the default --min-size below reads it; the size modifiers set --min-size directly. |
90
+ | `--min-size-md` | `.bb-badge` | `calc(var(--bb-control-h) - 16px)` | 16px at the default |
91
91
  | `--min-size` | `.bb-badge` | `var(--min-size-md)` | pill min height and min width |
92
92
  | `--elev` | `.bb-badge` | `var(--bb-elev-sm)` | Resting lift. Invisible while `--bb-elev` is its default transparent layer. A chip is small enough that a theme wanting a different lift here than on a button sets `--elev` on this class. |
93
93
  | `--r` | `.bb-badge` | `calc(var(--bb-radius) * 0.5)` | Pill corners; the body and trailing wrappers round their outer edges to match |
94
- | `--font-size` | `.bb-badge` | `10px` | label |
94
+ | `--font-size` | `.bb-badge` | `calc(var(--bb-fs) - 4px)` | Label. Sizes are offsets from the density masters (--bb-fs, --bb-control-h), like BbButton's, so a denser theme scales the pill. |
95
95
  | `--icon-size` | `.bb-badge` | `12px` | leading/append icons (passed to BbIcon as --size) |
96
96
  | `--gap` | `.bb-badge` | `3px` | space between label, icons and the trailing side |
97
97
  | `--padding-inline` | `.bb-badge` | `6px` | base side padding; the modifiers below scale it per side |
@@ -726,7 +726,7 @@ States are listed in precedence order: when two are on at once and their entries
726
726
  - `BbDropdownList` _(internal — not importable, reach it through `BbDropdown`)_ — also mounts `BbBaseButton`, `BbIcon`, `CommonPopover`, `BbDropdownList`
727
727
  - `BbBadge` _(public — [contract](./BbBadge.md))_ — its own pt parts (`BbBadge`): `spinner` → `BbSpinner`, `icon` → `BbIcon`, `clear` → `button.bb-badge__clear-button`; documented CSS variables: `--bg`, `--fg`, `--border-width`, `--ring`, `--min-size-md`, `--min-size`, `--elev`, `--r`, `--font-size`, `--icon-size`, `--gap`, `--padding-inline`, `--clear-size`, `--pad-left`; also mounts `BbIcon`, `BbSpinner`, `BadgeBodyContent`
728
728
  - `AdaptiveDropdown` _(internal — not importable, reach it through `BbDropdown`)_ — hands `BbOffCanvas` the pt map `{ header: 'header', footer: 'footer', panel: 'root', sheet: 'root' }` (ours → theirs); also mounts `BbSmoothHeight`, `BbDropdownList`
729
- - `BbOffCanvas` _(public — [contract](./BbOffCanvas.md))_ — its own pt parts (`BbOffCanvas`): `header` → `div.bb-offcanvas__header`, `title` → `span.bb-offcanvas__title`, `description` → `p.bb-offcanvas__description`, `close` → `CloseButton`, `content` → `div.bb-offcanvas__body`, `footer` → `div.bb-offcanvas__footer`; documented CSS variables: `--stack-x`, `--stack-fill`, `--stack-transition-duration`, `--px`, `--handle-w`, `--handle-h`, `--handle-bg`, `--close-size`
729
+ - `BbOffCanvas` _(public — [contract](./BbOffCanvas.md))_ — its own pt parts (`BbOffCanvas`): `header` → `div.bb-offcanvas__header`, `title` → `span.bb-offcanvas__title`, `description` → `p.bb-offcanvas__description`, `close` → `CloseButton`, `content` → `div.bb-offcanvas__body`, `footer` → `div.bb-offcanvas__footer`; documented CSS variables: `--stack-x`, `--stack-fill`, `--stack-transition-duration`, `--px`, `--handle-w`, `--handle-h`, `--handle-bg`, `--safe-top`, `--close-size`
730
730
  - `CloseButton` _(internal — not importable, reach it through `BbOffCanvas`)_ — documented CSS variables: `--size`, `--p`
731
731
  - `BbButton` _(public — [contract](./BbButton.md))_ — its own pt parts (`BbButton`): `root` → `BbBaseButton`, `spinner` → `BbSpinner`, `icon` → `BbIcon`, `text` → `span.bb-button__content`; documented CSS variables: `--h-xs`, `--h`, `--icon-size`, `--px`, `--fs`, `--r`, `--gap`, `--bg`, `--bg-hover`, `--bg-pressed`, `--fg`, `--border-color`, `--ring`, `--bw`, `--fg-hover`, `--border-hover`, `--fg-pressed`, `--border-pressed`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--opacity-disabled`, `--elev-tier`, `--elev`; also mounts `BbIcon`, `BbBaseButton`, `BbSpinner`
732
732
  - Also mounts `BbIcon`
@@ -590,7 +590,9 @@ Escape hatches, when the default isn't right:
590
590
  handler opens a dialog and you don't want a spinner flash).
591
591
  - `interactive-while-loading` — keeps the button clickable while it shows the
592
592
  loading state, for controls that must stay interactive (this is how the
593
- `BbDropdownButton` caret stays usable while the primary action runs).
593
+ `BbDropdownButton` caret stays usable while the primary action runs). The
594
+ soft-disable is dropped whole: no `aria-disabled`, the button stays in the tab
595
+ order, and the pointer still reaches it.
594
596
 
595
597
  The visually-hidden loading announcement is localized via the plugin's `locale`
596
598
  option. `disabled` and `loading` are independent props, and setting both is
@@ -848,6 +850,25 @@ never instead of it. The full grammar, the merge rules and the global map are
848
850
  in the [passthrough guide](./guides/passthrough.md); a look several
849
851
  buttons share is a `variant`, not a `pt`.
850
852
 
853
+ **Padding, radius, font size and gap are the button's own.** `root` declares
854
+ `padding-left` / `padding-right`, `border-radius`, `font-size` and `gap`,
855
+ reading them from the active locals `--px`, `--r`, `--fs` and `--gap`, which
856
+ the size class (`.bb-button--md` by default) sets. So in the default,
857
+ unlayered build `pt:root="px-6 rounded-lg text-lg gap-2"` changes nothing, and
858
+ `p-4` half-applies (top and bottom only) — measured in
859
+ [Installation](./guides/installation-and-plugin-setup.md) § _What "the utility
860
+ loses" actually means_. An arbitrary-property class (`[--r:12px]`) loses the
861
+ same way: the size class out-specifies it. The object prop above, like any
862
+ such class, only lands with `cssLayers: TAILWIND_V4_LAYERS`, where every
863
+ utility wins. What works in every build:
864
+
865
+ - a different `size` step, which moves height, padding, font, radius and icon
866
+ together;
867
+ - the locals as an inline style, which nothing in the sheet outranks —
868
+ `:pt:root="{ style: { '--r': '9999px', '--px': '20px' } }"`;
869
+ - a utility with Tailwind's important suffix (`px-6!`, `[--r:12px]!`), or a
870
+ rule of your own on `.bb-button` that out-specifies the size class.
871
+
851
872
  ### Works well with
852
873
 
853
874
  - `BbDropdownButton` — escalate to it when a primary action grows close
@@ -476,12 +476,15 @@ const toggleAll = (checked: boolean) => {
476
476
  </script>
477
477
  ```
478
478
 
479
- ### Layout: direction, alignment, `reverse`
479
+ ### Layout: `layout`, alignment, `reverse`
480
480
 
481
- - `direction` defaults to `'auto'` (label and box size to their content, on one
481
+ - `layout` defaults to `'auto'` (label and box size to their content, on one
482
482
  line). `'horizontal'` splits label/box 50-50; `'vertical'` stacks them; a
483
483
  two-token pattern like `"x xxxxxxx"` splits proportionally by token length —
484
484
  the first token sizes the **visually first** element, the second the other.
485
+ `'label-fill'` hands the label the free space and keeps the box at its own
486
+ size; with `reverse` that is the settings row — label on the left, box on
487
+ the trailing edge. `'input-fill'` is the other way round.
485
488
  - The box renders **before** the label by default — the conventional checkbox
486
489
  arrangement. Setting `reverse` swaps the label in front of the box. It is an
487
490
  ordinary opt-in boolean: unset and `:reverse="false"` both give box-first,
@@ -490,7 +493,7 @@ const toggleAll = (checked: boolean) => {
490
493
  invisible from outside. Earlier revisions of this guide described that
491
494
  negation as a consumer-facing quirk — it is not.)
492
495
  - `input-position` / `label-position` (`'left' | 'center' | 'right'`) align box
493
- and label text inside their columns — useful with percentage directions.
496
+ and label text inside their columns — useful with ratio layouts.
494
497
  - `hide-label` keeps the label for screen readers but hides it visually — only
495
498
  for dense composites where visible text lives elsewhere.
496
499
  - `id` is auto-generated for the label→input wiring; pass your own only when
@@ -674,7 +677,6 @@ map are in the [passthrough guide](./guides/passthrough.md).
674
677
  | `autofocus` | `Booleanish \| undefined` | | | Sets autofocus on page load. |
675
678
  | `checked` | `boolean \| undefined` | `undefined` | | Defines the input as checked. |
676
679
  | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
677
- | `direction` | `InputDirection \| undefined` | `"auto"` | | Direction of the layout of the component. Can either be a predefined value or a pattern separated by a space like `xx xxxxx`. |
678
680
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
679
681
  | `errors` | `string \| string[] \| undefined` | | | Can be a string or an array of string containing the messages to display. They render in an `aria-live="polite"` region (announced when they appear) and, while the list is non-empty, are referenced FIRST from the control's `aria-describedby… |
680
682
  | `falseValue` | `any` | | | Value of the input when unchecked. It handles any kind of serializable object. |
@@ -687,6 +689,7 @@ map are in the [passthrough guide](./guides/passthrough.md).
687
689
  | `inputPosition` | `"left" \| "center" \| "right" \| undefined` | `"left"` | | Sets the alignment of the input. Since inputs are inline block they can be aligned just as text can. |
688
690
  | `label` | `string` | | yes | Text content of the label of the element. |
689
691
  | `labelPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
692
+ | `layout` | `FieldLayout \| undefined` | `"auto"` | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'` (the label takes the free space, the input keeps its size), `'input-fill'` (the other way round), or a two-word ratio pattern like `'xx… |
690
693
  | `modelValue` | `any` | | | Used by v-model. Can be any serializable type. |
691
694
  | `name` | `string \| undefined` | | | Defines the name of the input. |
692
695
  | `persistentHint` | `boolean \| undefined` | `false` | | Keeps the hint displayed. |
@@ -148,12 +148,12 @@ control.
148
148
  <div class="grid max-w-sm gap-3 rounded-(--bb-radius) border p-3">
149
149
  <BbCheckboxGroup
150
150
  v-model="addons"
151
- input-direction="vertical"
152
151
  :item-props="{ description: 'summary' }"
153
152
  item-text="name"
154
153
  item-value="id"
155
154
  :items="catalog"
156
155
  legend="Add-ons"
156
+ list-layout="vertical"
157
157
  name="addons"
158
158
  >
159
159
  <!-- The group's OWN description, under the legend. A slot, so it can
@@ -265,9 +265,9 @@ choice out. Pair it with a `hint` that states the limit.
265
265
  v-model="topics"
266
266
  compact
267
267
  hint="You can change these anytime."
268
- input-direction="horizontal"
269
268
  :items="allTopics"
270
269
  legend="Topics"
270
+ list-layout="horizontal"
271
271
  :max="3"
272
272
  name="topics"
273
273
  persistent-hint
@@ -354,11 +354,18 @@ disabling it.
354
354
 
355
355
  ### Layout and density
356
356
 
357
- - `input-direction` (`'horizontal' | 'vertical'`) arranges the **options**
358
- within the fieldset — a row for compact filter bars, a stack for readability.
359
- - `direction` arranges the **legend against the options block** (the outer
360
- label/control split): `'auto'`, `'horizontal'`, `'vertical'`, or a
361
- proportional pattern like `'xx xxxxxx'`.
357
+ - `list-layout` arranges the **options** within the fieldset: `'horizontal'`
358
+ (the default — a wrapping row, for compact filter bars), `'vertical'` (a
359
+ stack, each row its own width), `'vertical-stretch'` (a stack, each row as
360
+ wide as the list) or `'tiles'` (equal-size boxes that wrap as the field
361
+ narrows; `pt:list="[--tile-min-width:160px]"` sets how narrow one gets).
362
+ - `item-layout` arranges **each row** — its input against its label — with the
363
+ same keywords as `layout`; `item-reverse` swaps the two. A settings list is
364
+ `list-layout="vertical-stretch" item-layout="label-fill" item-reverse`: the
365
+ label takes the row, the input sits at the trailing edge.
366
+ - `layout` arranges the **legend against the options block** (the outer
367
+ split): `'auto'`, `'horizontal'`, `'vertical'`, `'label-fill'`,
368
+ `'input-fill'`, or a proportional pattern like `'xx xxxxxx'`.
362
369
  - `compact` tightens per-option spacing for dense groups.
363
370
  - `input-position` / `legend-position` (`'left' | 'center' | 'right'`) align the
364
371
  options and the legend text within their columns.
@@ -368,6 +375,238 @@ disabling it.
368
375
  - `hide-legend` keeps the legend for assistive tech but hides it visually;
369
376
  `hide-label` hides the individual option labels (dense composites only).
370
377
 
378
+ #### Layout recipes
379
+
380
+ Three ready compositions, each driven by `list-layout` / `item-layout` with
381
+ `pt` only for the look.
382
+
383
+ A **filter facet** — every row as wide as the panel, the label cell filling it
384
+ so the count sits on the trailing edge:
385
+
386
+ **Filter facet with counts**
387
+
388
+ ```vue
389
+ <template>
390
+ <div class="w-64 rounded-(--bb-radius) border bg-(--bb-panel) p-1.5">
391
+ <div class="flex items-center justify-between px-2 pt-1 pb-1.5">
392
+ <span class="text-sm font-medium">Status</span>
393
+ <BbButton
394
+ :disabled="!statuses.length"
395
+ size="xs"
396
+ variant="ghost"
397
+ @click="statuses = []"
398
+ >Clear</BbButton
399
+ >
400
+ </div>
401
+ <BbCheckboxGroup
402
+ v-model="statuses"
403
+ hide-legend
404
+ item-layout="label-fill"
405
+ item-text="name"
406
+ item-value="id"
407
+ :items="options"
408
+ legend="Status"
409
+ list-layout="vertical-stretch"
410
+ name="status"
411
+ pt:item="rounded-[calc(var(--bb-radius)-4px)] px-2 py-1.5 hover:bg-(--bb-muted)"
412
+ pt:label="text-sm"
413
+ pt:list="[--row-gap:0px]"
414
+ >
415
+ <template #label="{ item }">
416
+ <span class="flex items-baseline justify-between gap-3">
417
+ <span>{{ item.name }}</span>
418
+ <span class="text-xs text-(--bb-text-muted) tabular-nums">{{
419
+ item.count
420
+ }}</span>
421
+ </span>
422
+ </template>
423
+ </BbCheckboxGroup>
424
+ </div>
425
+ </template>
426
+ <script setup lang="ts">
427
+ import { ref } from 'vue';
428
+ import { BbButton, BbCheckboxGroup } from 'bitboss-ui';
429
+
430
+ type Status = { id: string; name: string; count: number };
431
+
432
+ // A filter facet. `list-layout="vertical-stretch"` makes every row as wide as
433
+ // the panel (the whole row is the hit area, so the hover wash reads as one
434
+ // target) and `item-layout="label-fill"` hands the label cell the free space,
435
+ // so a flex row inside the `#label` slot pushes the count to the trailing
436
+ // edge. The visible heading is our own, beside the Clear action; the legend
437
+ // stays for assistive tech (`hide-legend`).
438
+ const options: Status[] = [
439
+ { id: 'draft', name: 'Draft', count: 12 },
440
+ { id: 'open', name: 'Open', count: 48 },
441
+ { id: 'overdue', name: 'Overdue', count: 7 },
442
+ { id: 'paid', name: 'Paid', count: 214 },
443
+ { id: 'void', name: 'Void', count: 3 },
444
+ ];
445
+
446
+ const statuses = ref<string[]>(['open', 'overdue']);
447
+ </script>
448
+ ```
449
+
450
+ A **weekday picker** — seven equal toggles with the box hidden
451
+ (`pt:icon="hidden"`; an empty `#icon` slot would fall back to the box), the
452
+ checked day painted, the hint reading the choice back:
453
+
454
+ **Weekday picker**
455
+
456
+ ```vue
457
+ <template>
458
+ <div class="max-w-md">
459
+ <BbCheckboxGroup
460
+ v-model="days"
461
+ :hint="summary"
462
+ item-layout="vertical"
463
+ item-text="short"
464
+ item-value="id"
465
+ :items="weekdays"
466
+ legend="Send reminders on"
467
+ list-layout="tiles"
468
+ name="reminder-days"
469
+ persistent-hint
470
+ pt:icon="hidden"
471
+ pt:item="justify-items-center gap-0 rounded-(--bb-radius) border py-2 hover:bg-(--bb-muted)"
472
+ pt:item:checked="border-(--bb-primary) bg-(--bb-primary) hover:bg-(--bb-primary-hover)"
473
+ pt:item:focus-visible="ring-2 ring-(--bb-primary-ring)"
474
+ pt:label="text-sm font-medium"
475
+ pt:label:checked="text-(--bb-primary-fg)"
476
+ pt:list="[--gap:6px] [--tile-min-width:0px]"
477
+ />
478
+ </div>
479
+ </template>
480
+ <script setup lang="ts">
481
+ import { computed, ref } from 'vue';
482
+ import { BbCheckboxGroup } from 'bitboss-ui';
483
+
484
+ type Weekday = { id: number; short: string; long: string };
485
+
486
+ // Seven equal toggles: `list-layout="tiles"` with `--tile-min-width: 0` keeps
487
+ // them on one line, `item-layout="vertical"` stacks the (hidden) glyph cell
488
+ // over the label so the label centres. `pt:icon="hidden"` removes the box —
489
+ // an empty `#icon` slot would not: Vue renders a slot's fallback when the
490
+ // slot is empty. The native input stays, so Tab / Space / screen readers work
491
+ // as on any checkbox. The label cell paints its own colour, so the checked
492
+ // text colour goes on `pt:label:checked`, not on `pt:item`.
493
+ const weekdays: Weekday[] = [
494
+ { id: 1, short: 'Mon', long: 'Monday' },
495
+ { id: 2, short: 'Tue', long: 'Tuesday' },
496
+ { id: 3, short: 'Wed', long: 'Wednesday' },
497
+ { id: 4, short: 'Thu', long: 'Thursday' },
498
+ { id: 5, short: 'Fri', long: 'Friday' },
499
+ { id: 6, short: 'Sat', long: 'Saturday' },
500
+ { id: 7, short: 'Sun', long: 'Sunday' },
501
+ ];
502
+
503
+ const days = ref<number[]>([1, 3, 5]);
504
+
505
+ // The hint reads the choice back in words.
506
+ const summary = computed(() => {
507
+ const picked = [...days.value].sort((a, b) => a - b);
508
+ if (!picked.length) return 'No reminders will be sent.';
509
+ if (picked.length === 7) return 'Every day.';
510
+ if (picked.join() === '1,2,3,4,5') return 'Every weekday.';
511
+ if (picked.join() === '6,7') return 'Weekends only.';
512
+ const names = picked.map((id) => weekdays[id - 1]!.long);
513
+ return `Every ${names.join(', ').replace(/, ([^,]*)$/, ' and $1')}.`;
514
+ });
515
+ </script>
516
+ ```
517
+
518
+ **Priced cards** — equal tiles that wrap, the box in the corner
519
+ (`item-layout="label-fill" item-reverse`), the price pinned to the bottom edge
520
+ by a stretched `pt:label`, a running total under the grid:
521
+
522
+ **Add-on cards with a total**
523
+
524
+ ```vue
525
+ <template>
526
+ <div class="grid max-w-2xl gap-2">
527
+ <BbCheckboxGroup
528
+ v-model="selected"
529
+ description="Billed monthly with your plan. Cancel any add-on at renewal."
530
+ item-layout="label-fill"
531
+ item-reverse
532
+ item-text="name"
533
+ item-value="id"
534
+ :items="addons"
535
+ legend="Add-ons"
536
+ list-layout="tiles"
537
+ name="addons"
538
+ pt:item="items-start rounded-(--bb-radius) border p-3.5 hover:border-(--bb-text-faint)"
539
+ pt:item:checked="border-(--bb-primary) ring-1 ring-(--bb-primary) hover:border-(--bb-primary)"
540
+ pt:label="flex flex-col gap-0.5 self-stretch"
541
+ pt:list="[--gap:10px] [--row-gap:10px] [--tile-min-width:240px]"
542
+ >
543
+ <template #label="{ item }">
544
+ <span class="text-sm font-medium">{{ item.name }}</span>
545
+ <span class="text-xs leading-snug text-(--bb-text-muted)">{{
546
+ item.detail
547
+ }}</span>
548
+ <span class="mt-auto pt-3 text-sm tabular-nums"
549
+ >€{{ item.price
550
+ }}<span class="text-xs text-(--bb-text-muted)"> / month</span></span
551
+ >
552
+ </template>
553
+ </BbCheckboxGroup>
554
+ <div class="flex items-baseline justify-between border-t pt-2 text-sm">
555
+ <span class="text-(--bb-text-muted)"
556
+ >{{ selected.length }} of {{ addons.length }} selected</span
557
+ >
558
+ <span class="font-medium tabular-nums">€{{ total }} / month</span>
559
+ </div>
560
+ </div>
561
+ </template>
562
+ <script setup lang="ts">
563
+ import { computed, ref } from 'vue';
564
+ import { BbCheckboxGroup } from 'bitboss-ui';
565
+
566
+ type Addon = { id: string; name: string; detail: string; price: number };
567
+
568
+ // Priced cards. `list-layout="tiles"` makes equal boxes (every row as tall as
569
+ // the tallest) that wrap when the field narrows — `--tile-min-width: 240px`
570
+ // gives two columns here and one on a phone. `item-layout="label-fill"` +
571
+ // `item-reverse` puts the text first and the box in the corner. To pin the
572
+ // price to the card's bottom edge, `pt:label` stretches the label cell to the
573
+ // row's height and makes it a column; the price takes `mt-auto`.
574
+ const addons: Addon[] = [
575
+ {
576
+ id: 'sso',
577
+ name: 'Single sign-on',
578
+ detail: 'SAML and OIDC for every workspace.',
579
+ price: 9,
580
+ },
581
+ {
582
+ id: 'audit',
583
+ name: 'Audit log',
584
+ detail: 'Every change, kept for two years.',
585
+ price: 6,
586
+ },
587
+ {
588
+ id: 'guests',
589
+ name: 'Guest seats',
590
+ detail: 'Ten read-only collaborators.',
591
+ price: 4,
592
+ },
593
+ {
594
+ id: 'support',
595
+ name: 'Priority support',
596
+ detail: 'Answers within four working hours.',
597
+ price: 12,
598
+ },
599
+ ];
600
+
601
+ const selected = ref<string[]>(['sso', 'audit']);
602
+ const total = computed(() =>
603
+ addons
604
+ .filter((addon) => selected.value.includes(addon.id))
605
+ .reduce((sum, addon) => sum + addon.price, 0)
606
+ );
607
+ </script>
608
+ ```
609
+
371
610
  ### Legend, description and validation
372
611
 
373
612
  The `legend` is the group's accessible name — always set it. `description` is
@@ -471,11 +710,11 @@ Read-only, with no cleanup code.
471
710
  <BbRadioGroup
472
711
  v-model="role"
473
712
  compact
474
- input-direction="horizontal"
475
713
  item-text="label"
476
714
  item-value="id"
477
715
  :items="roles"
478
716
  legend="Token role"
717
+ list-layout="horizontal"
479
718
  name="token-role"
480
719
  />
481
720
  <BbCheckboxGroup
@@ -599,8 +838,8 @@ row's own `disabled` — the states a standalone control has. A name outside
599
838
  that list is a type error and an ESLint error.
600
839
 
601
840
  Digest cadence as option tiles: `pt:item` is a broadcast over every row,
602
- `pt:item:checked` resolves per row. `flex-1` is what shares the row — size
603
- the group itself (`class="w-full"`), not a nested width.
841
+ `pt:item:checked` resolves per row. `flex-1` is what shares the row — the
842
+ list already spans the field, so the tiles do too.
604
843
 
605
844
  **Text-only tiles:** hide the box with `pt:icon="hidden"`. An empty `#icon`
606
845
  slot does not remove it: the default box is the slot's fallback and renders
@@ -613,10 +852,9 @@ whenever the slot is empty.
613
852
  <div class="w-full max-w-md rounded-(--bb-radius) border p-3">
614
853
  <BbCheckboxGroup
615
854
  v-model="cadence"
616
- class="w-full"
617
- input-direction="horizontal"
618
855
  :items="items"
619
856
  legend="Cadence"
857
+ list-layout="horizontal"
620
858
  name="cadence"
621
859
  pt:item="flex-1 rounded-(--bb-radius) border border-(--bb-border) px-3 py-2"
622
860
  pt:item:checked="border-(--bb-primary) bg-(--bb-muted)"
@@ -709,7 +947,6 @@ and the global map are in the [passthrough guide](./guides/passthrough.md).
709
947
  | `dependencies` | `unknown[] \| undefined` | | | Defines an array of dependencies that will trigger actions in the component upon change. |
710
948
  | `depsDebounceTime` | `number \| undefined` | | | Timeout used to debounce response to changes to dependencies. |
711
949
  | `description` | `string \| undefined` | | | Descriptive text displayed below the label and above the input. Unlike the hint it is always visible, and it is linked to the input via `aria-describedby` (after any `errors` / `warnings`, before the `hint`). |
712
- | `direction` | `InputDirection \| undefined` | | | Direction of the layout of the component. Can either be a predefined value or a pattern separated by a space like `xx xxxxx`. |
713
950
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
714
951
  | `enforceCoherence` | `boolean \| undefined` | `false` | | If coherence is enforced the input cannot have a modelValue that is incoherent with its current items. e.g. You cannot set v-model to a user that is not present in the items passed. modelValue will be reset upon incoherence. |
715
952
  | `errors` | `string \| string[] \| undefined` | | | Can be a string or an array of string containing the messages to display. They render in an `aria-live="polite"` region (announced when they appear) and, while the list is non-empty, are referenced FIRST from the control's `aria-describedby… |
@@ -719,15 +956,18 @@ and the global map are in the [passthrough guide](./guides/passthrough.md).
719
956
  | `hideLegend` | `boolean \| undefined` | `false` | | Visually hides the legend of the fieldset while maintaining accessibility. |
720
957
  | `hint` | `string \| undefined` | | | Text box to be displayed near the input, usually to indicate instructions. |
721
958
  | `id` | `string \| undefined` | | | The identifier of the component. |
722
- | `inputDirection` | `"horizontal" \| "vertical" \| undefined` | | | Defines the direction of the inputs in the fieldset. |
723
959
  | `inputPosition` | `"left" \| "center" \| "right" \| undefined` | `"left"` | | Sets the alignment of the input. Since inputs are inline block they can be aligned just as text can. |
960
+ | `itemLayout` | `FieldLayout \| undefined` | `'auto'` | | How each row's input and label sit together — the keywords of `layout`: `'auto'`, `'vertical'` (the input above the label), `'horizontal'` (50/50), `'label-fill'` (the label takes the row's free space — pair it with `list-layout="vertical-s… |
724
961
  | `itemProps` | `BbOptionGroupItemProps<T> \| undefined` | | | ADDITIONAL row fields resolved from each item — never its text or value (those stay on `itemText` / `itemValue`). Option groups accept one field: - `description` — a muted line under the option label, beside the control; it may wrap. Either… |
962
+ | `itemReverse` | `boolean \| undefined` | `false` | | Swaps each row's input and label from their natural order (input first), in every `item-layout`. `reverse` swaps the legend and the options instead. |
725
963
  | `items` | `T[] \| ((prefill: boolean, modelValue?: any) => T[] \| Promise<T[]>)` | | yes | Used to retrieve items; can be an array or a function. |
726
964
  | `itemText` | `ItemAccessor<T, string> \| undefined` | | | Defines a path that returns a property of the object to use as text or a function that returns a string. |
727
965
  | `itemValue` | `ItemAccessor<T> \| undefined` | | | Defines a path that returns a property of the object to use as value or a function that returns any value. |
966
+ | `layout` | `FieldLayout \| undefined` | | | How the label and the input sit together: `'vertical'`, `'horizontal'` (50/50), `'auto'`, `'label-fill'` (the label takes the free space, the input keeps its size), `'input-fill'` (the other way round), or a two-word ratio pattern like `'xx… |
728
967
  | `legend` | `string` | | yes | Text content of the legend. |
729
968
  | `legendMode` | `"outside" \| "inside" \| undefined` | | | Legend rendering mode. `'outside'` renders the legend above the fieldset in the normal flow; `'inside'` overlays the legend inside the fieldset border. Floating mode is not supported for option groups (checkbox, radio or switch) — there is … |
730
969
  | `legendPosition` | `"left" \| "center" \| "right" \| undefined` | | | Sets the text alignment of the label. |
970
+ | `listLayout` | `ListLayout \| undefined` | `'horizontal'` | | How the rows sit inside the list: `'horizontal'` (on a line, wrapping, each row its own size), `'vertical'` (stacked, each row its own size), `'vertical-stretch'` (stacked, each row as wide as the list) or `'tiles'` (equal-size boxes, wrapp… |
731
971
  | `loadingText` | `string \| undefined` | | | String displayed while items are being loaded. |
732
972
  | `max` | `number \| undefined` | | | Maximum number of selected items. |
733
973
  | `modelValue` | `any` | | yes | Used by v-model. Can be an array of any serializable type. |
@@ -815,6 +1055,7 @@ Set these on the element, or on a class you put on it, to retune this component
815
1055
  | --- | --- | --- |
816
1056
  | `--gap` | `24px` | Space between rows on one line. |
817
1057
  | `--row-gap` | `10px` | Space between wrapped lines. |
1058
+ | `--tile-min-width` | `96px` | How narrow a tile may get before the tiles wrap (`tiles` only). |
818
1059
 
819
1060
  ## Component tree
820
1061