bitboss-ui 3.0.0-beta.50 → 3.0.0-beta.51

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 (353) hide show
  1. package/README.md +79 -0
  2. package/bin/bitboss-ui-mcp.mjs +2 -1
  3. package/bin/bitboss-ui.mjs +25 -5
  4. package/dist/ai/BbAccordion.md +1 -1
  5. package/dist/ai/BbAlert.md +1 -1
  6. package/dist/ai/BbAsterisk.md +4 -2
  7. package/dist/ai/BbBadge.md +2 -2
  8. package/dist/ai/BbBadgeButton.md +3 -3
  9. package/dist/ai/BbBaseButton.md +8 -3
  10. package/dist/ai/BbBaseCheckboxIcon.md +48 -13
  11. package/dist/ai/BbBaseRadioIcon.md +49 -14
  12. package/dist/ai/BbBaseSwitchIcon.md +48 -13
  13. package/dist/ai/BbButton.md +6 -3
  14. package/dist/ai/BbCalendar.md +7 -5
  15. package/dist/ai/BbCheckbox.md +3 -3
  16. package/dist/ai/BbCheckboxGroup.md +8 -8
  17. package/dist/ai/BbCollapsible.md +4 -2
  18. package/dist/ai/BbColorInput.md +12 -9
  19. package/dist/ai/BbColorPalette.md +1 -1
  20. package/dist/ai/BbDatePicker.md +4 -4
  21. package/dist/ai/BbDatePickerInput.md +18 -12
  22. package/dist/ai/BbDialog.md +8 -6
  23. package/dist/ai/BbDropdown.md +10 -6
  24. package/dist/ai/BbDropdownButton.md +5 -5
  25. package/dist/ai/BbDropzone.md +4 -4
  26. package/dist/ai/BbForm.md +246 -11
  27. package/dist/ai/BbHoverCard.md +1 -1
  28. package/dist/ai/BbNumberInput.md +15 -9
  29. package/dist/ai/BbOffCanvas.md +3 -3
  30. package/dist/ai/BbPagination.md +2 -2
  31. package/dist/ai/BbPopover.md +3 -3
  32. package/dist/ai/BbProgress.md +1 -1
  33. package/dist/ai/BbRadio.md +3 -3
  34. package/dist/ai/BbRadioGroup.md +24 -16
  35. package/dist/ai/BbRating.md +3 -3
  36. package/dist/ai/BbSelect.md +16 -15
  37. package/dist/ai/BbSelectPopover.md +12 -11
  38. package/dist/ai/BbSlider.md +3 -3
  39. package/dist/ai/BbSwitch.md +3 -3
  40. package/dist/ai/BbSwitchGroup.md +8 -8
  41. package/dist/ai/BbTable.md +257 -52
  42. package/dist/ai/BbTabs.md +3 -3
  43. package/dist/ai/BbTabsRoot.md +3 -3
  44. package/dist/ai/BbTag.md +6 -6
  45. package/dist/ai/BbTextInput.md +12 -10
  46. package/dist/ai/BbTextarea.md +6 -6
  47. package/dist/ai/BbTimePicker.md +8 -4
  48. package/dist/ai/BbTimePickerInput.md +10 -9
  49. package/dist/ai/BbTooltip.md +2 -2
  50. package/dist/ai/CommonField.md +4 -4
  51. package/dist/ai/CommonFieldInput.md +2 -2
  52. package/dist/ai/CommonPopover.md +5 -5
  53. package/dist/ai/CommonTimeSelector.md +1 -1
  54. package/dist/ai/OptionsContainer.md +1 -1
  55. package/dist/ai/changelog.json +16 -3
  56. package/dist/ai/components.json +55 -52
  57. package/dist/ai/composables/useBbFormField.md +44 -10
  58. package/dist/ai/guides/agent-contract.md +4 -1
  59. package/dist/ai/guides/ai-router.md +23 -23
  60. package/dist/ai/guides/component-picker.md +2 -1
  61. package/dist/ai/guides/design-language.md +4 -2
  62. package/dist/ai/guides/installation-and-plugin-setup.md +34 -2
  63. package/dist/ai/guides/migration/from-async-validator.md +1252 -0
  64. package/dist/ai/guides/migration/v2-to-v3.md +14 -0
  65. package/dist/ai/guides/migration/v3-beta-tooling.md +14 -9
  66. package/dist/ai/guides/passthrough.md +2 -2
  67. package/dist/ai/guides/validated-forms.md +78 -5
  68. package/dist/ai/guides/validation-libraries.md +5 -0
  69. package/dist/ai/helpers/inertia/manifest.json +1 -1
  70. package/dist/ai/index.md +2 -1
  71. package/dist/ai/manifest/components/BbAccordion.json +1 -1
  72. package/dist/ai/manifest/components/BbAlert.json +1 -5
  73. package/dist/ai/manifest/components/BbAlert.removed.json +7 -0
  74. package/dist/ai/manifest/components/BbAvatar.json +0 -3
  75. package/dist/ai/manifest/components/BbAvatar.removed.json +6 -0
  76. package/dist/ai/manifest/components/BbBadge.json +2 -5
  77. package/dist/ai/manifest/components/BbBadge.removed.json +6 -0
  78. package/dist/ai/manifest/components/BbBadgeButton.json +3 -3
  79. package/dist/ai/manifest/components/BbBaseButton.json +3 -3
  80. package/dist/ai/manifest/components/BbBaseCheckboxIcon.json +0 -3
  81. package/dist/ai/manifest/components/BbBaseCheckboxIcon.removed.json +6 -0
  82. package/dist/ai/manifest/components/BbBaseRadioIcon.json +0 -3
  83. package/dist/ai/manifest/components/BbBaseRadioIcon.removed.json +6 -0
  84. package/dist/ai/manifest/components/BbBaseSwitchIcon.json +0 -3
  85. package/dist/ai/manifest/components/BbBaseSwitchIcon.removed.json +6 -0
  86. package/dist/ai/manifest/components/BbBreadcrumbs.json +0 -4
  87. package/dist/ai/manifest/components/BbBreadcrumbs.removed.json +7 -0
  88. package/dist/ai/manifest/components/BbButton.json +2 -9
  89. package/dist/ai/manifest/components/BbButton.removed.json +10 -0
  90. package/dist/ai/manifest/components/BbCalendar.json +4 -9
  91. package/dist/ai/manifest/components/BbCalendar.removed.json +8 -0
  92. package/dist/ai/manifest/components/BbCheckbox.json +3 -10
  93. package/dist/ai/manifest/components/BbCheckbox.removed.json +10 -0
  94. package/dist/ai/manifest/components/BbCheckboxGroup.json +8 -19
  95. package/dist/ai/manifest/components/BbCheckboxGroup.removed.json +14 -0
  96. package/dist/ai/manifest/components/BbCollapsible.json +1 -1
  97. package/dist/ai/manifest/components/BbColorInput.json +8 -17
  98. package/dist/ai/manifest/components/BbColorInput.removed.json +12 -0
  99. package/dist/ai/manifest/components/BbColorPalette.json +1 -11
  100. package/dist/ai/manifest/components/BbColorPalette.removed.json +13 -0
  101. package/dist/ai/manifest/components/BbDatePicker.json +4 -10
  102. package/dist/ai/manifest/components/BbDatePicker.removed.json +9 -0
  103. package/dist/ai/manifest/components/BbDatePickerInput.json +9 -24
  104. package/dist/ai/manifest/components/BbDatePickerInput.removed.json +18 -0
  105. package/dist/ai/manifest/components/BbDialog.json +5 -12
  106. package/dist/ai/manifest/components/BbDialog.removed.json +10 -0
  107. package/dist/ai/manifest/components/BbDropdown.json +6 -15
  108. package/dist/ai/manifest/components/BbDropdown.removed.json +12 -0
  109. package/dist/ai/manifest/components/BbDropdownButton.json +5 -15
  110. package/dist/ai/manifest/components/BbDropdownButton.removed.json +13 -0
  111. package/dist/ai/manifest/components/BbDropzone.json +4 -9
  112. package/dist/ai/manifest/components/BbDropzone.removed.json +8 -0
  113. package/dist/ai/manifest/components/BbForm.json +5 -8
  114. package/dist/ai/manifest/components/BbForm.removed.json +6 -0
  115. package/dist/ai/manifest/components/BbHoverCard.json +1 -1
  116. package/dist/ai/manifest/components/BbIcon.json +0 -4
  117. package/dist/ai/manifest/components/BbIcon.removed.json +7 -0
  118. package/dist/ai/manifest/components/BbNumberInput.json +9 -17
  119. package/dist/ai/manifest/components/BbNumberInput.removed.json +11 -0
  120. package/dist/ai/manifest/components/BbOffCanvas.json +3 -8
  121. package/dist/ai/manifest/components/BbOffCanvas.removed.json +8 -0
  122. package/dist/ai/manifest/components/BbPagination.json +2 -8
  123. package/dist/ai/manifest/components/BbPagination.removed.json +9 -0
  124. package/dist/ai/manifest/components/BbPopover.json +3 -16
  125. package/dist/ai/manifest/components/BbPopover.removed.json +16 -0
  126. package/dist/ai/manifest/components/BbProgress.json +1 -1
  127. package/dist/ai/manifest/components/BbRadio.json +3 -8
  128. package/dist/ai/manifest/components/BbRadio.removed.json +8 -0
  129. package/dist/ai/manifest/components/BbRadioGroup.json +9 -20
  130. package/dist/ai/manifest/components/BbRadioGroup.removed.json +14 -0
  131. package/dist/ai/manifest/components/BbRating.json +3 -10
  132. package/dist/ai/manifest/components/BbRating.removed.json +10 -0
  133. package/dist/ai/manifest/components/BbSelect.json +14 -27
  134. package/dist/ai/manifest/components/BbSelect.removed.json +16 -0
  135. package/dist/ai/manifest/components/BbSelectPopover.json +10 -20
  136. package/dist/ai/manifest/components/BbSelectPopover.removed.json +13 -0
  137. package/dist/ai/manifest/components/BbSlider.json +3 -12
  138. package/dist/ai/manifest/components/BbSlider.removed.json +12 -0
  139. package/dist/ai/manifest/components/BbSpinner.json +0 -3
  140. package/dist/ai/manifest/components/BbSpinner.removed.json +6 -0
  141. package/dist/ai/manifest/components/BbSwitch.json +3 -10
  142. package/dist/ai/manifest/components/BbSwitch.removed.json +10 -0
  143. package/dist/ai/manifest/components/BbSwitchGroup.json +8 -19
  144. package/dist/ai/manifest/components/BbSwitchGroup.removed.json +14 -0
  145. package/dist/ai/manifest/components/BbTable.json +22 -32
  146. package/dist/ai/manifest/components/BbTable.removed.json +16 -0
  147. package/dist/ai/manifest/components/BbTable.tree.json +18 -1
  148. package/dist/ai/manifest/components/BbTabs.json +3 -12
  149. package/dist/ai/manifest/components/BbTabs.removed.json +12 -0
  150. package/dist/ai/manifest/components/BbTabsList.json +0 -5
  151. package/dist/ai/manifest/components/BbTabsList.removed.json +8 -0
  152. package/dist/ai/manifest/components/BbTabsRoot.json +3 -13
  153. package/dist/ai/manifest/components/BbTabsRoot.removed.json +13 -0
  154. package/dist/ai/manifest/components/BbTag.json +6 -16
  155. package/dist/ai/manifest/components/BbTag.removed.json +13 -0
  156. package/dist/ai/manifest/components/BbTextInput.json +8 -19
  157. package/dist/ai/manifest/components/BbTextInput.removed.json +14 -0
  158. package/dist/ai/manifest/components/BbTextarea.json +6 -14
  159. package/dist/ai/manifest/components/BbTextarea.removed.json +11 -0
  160. package/dist/ai/manifest/components/BbTimePicker.json +3 -3
  161. package/dist/ai/manifest/components/BbTimePickerInput.json +8 -16
  162. package/dist/ai/manifest/components/BbTimePickerInput.removed.json +11 -0
  163. package/dist/ai/manifest/components/BbToast.json +0 -6
  164. package/dist/ai/manifest/components/BbToast.removed.json +9 -0
  165. package/dist/ai/manifest/components/BbTooltip.json +2 -8
  166. package/dist/ai/manifest/components/BbTooltip.removed.json +9 -0
  167. package/dist/ai/manifest/components/BbTree.json +0 -5
  168. package/dist/ai/manifest/components/BbTree.removed.json +8 -0
  169. package/dist/ai/manifest/components/CommonField.json +4 -4
  170. package/dist/ai/manifest/components/CommonFieldInput.json +2 -2
  171. package/dist/ai/manifest/components/CommonPopover.json +5 -5
  172. package/dist/ai/manifest/components/CommonTimeSelector.json +1 -1
  173. package/dist/ai/manifest/components/OptionsContainer.json +1 -1
  174. package/dist/ai/manifest/index.json +6 -5
  175. package/dist/ai/manifest/meta.json +51 -1
  176. package/dist/ai/manifest/types.advanced.json +2 -2
  177. package/dist/ai/recipes/inertia/record-form.md +15 -7
  178. package/dist/ai/recipes/nuxt/record-form.md +1 -0
  179. package/dist/ai/recipes/vue/record-form.md +1 -0
  180. package/dist/ai/source/BbAccordion.md +4 -5
  181. package/dist/ai/source/BbAlert.md +3 -6
  182. package/dist/ai/source/BbBadge.md +6 -14
  183. package/dist/ai/source/BbBadgeButton.md +6 -14
  184. package/dist/ai/source/BbBaseButton.md +11 -25
  185. package/dist/ai/source/BbButton.md +8 -17
  186. package/dist/ai/source/BbCalendar.md +14 -29
  187. package/dist/ai/source/BbCheckbox.md +7 -10
  188. package/dist/ai/source/BbCheckboxGroup.md +42 -69
  189. package/dist/ai/source/BbCollapsible.md +2 -7
  190. package/dist/ai/source/BbColorInput.md +21 -51
  191. package/dist/ai/source/BbColorPalette.md +4 -18
  192. package/dist/ai/source/BbDatePicker.md +15 -43
  193. package/dist/ai/source/BbDatePickerInput.md +26 -60
  194. package/dist/ai/source/BbDialog.md +16 -47
  195. package/dist/ai/source/BbDropdown.md +70 -54
  196. package/dist/ai/source/BbDropdownButton.md +4 -8
  197. package/dist/ai/source/BbDropdownGroup.md +22 -46
  198. package/dist/ai/source/BbDropzone.md +11 -16
  199. package/dist/ai/source/BbForm.md +19 -46
  200. package/dist/ai/source/BbHoverCard.md +3 -5
  201. package/dist/ai/source/BbHoverCardPortal.md +3 -5
  202. package/dist/ai/source/BbNumberInput.md +43 -46
  203. package/dist/ai/source/BbOffCanvas.md +11 -21
  204. package/dist/ai/source/BbPagination.md +7 -15
  205. package/dist/ai/source/BbPopover.md +27 -28
  206. package/dist/ai/source/BbPopoverPortal.md +11 -28
  207. package/dist/ai/source/BbProgress.md +3 -4
  208. package/dist/ai/source/BbRadio.md +10 -16
  209. package/dist/ai/source/BbRadioGroup.md +78 -76
  210. package/dist/ai/source/BbRating.md +7 -10
  211. package/dist/ai/source/BbSelect.md +45 -112
  212. package/dist/ai/source/BbSelectPopover.md +35 -101
  213. package/dist/ai/source/BbSlider.md +7 -10
  214. package/dist/ai/source/BbSwitchGroup.md +48 -13
  215. package/dist/ai/source/BbTable.md +637 -216
  216. package/dist/ai/source/BbTabs.md +12 -30
  217. package/dist/ai/source/BbTabsList.md +12 -30
  218. package/dist/ai/source/BbTabsPanels.md +12 -30
  219. package/dist/ai/source/BbTabsRoot.md +12 -30
  220. package/dist/ai/source/BbTag.md +15 -28
  221. package/dist/ai/source/BbTextInput.md +21 -36
  222. package/dist/ai/source/BbTextarea.md +16 -30
  223. package/dist/ai/source/BbTimePicker.md +12 -28
  224. package/dist/ai/source/BbTimePickerInput.md +25 -51
  225. package/dist/ai/source/BbTooltip.md +6 -14
  226. package/dist/ai/source/CommonField.md +14 -21
  227. package/dist/ai/source/CommonFieldInput.md +7 -8
  228. package/dist/ai/source/CommonPopover.md +18 -28
  229. package/dist/ai/source/CommonTimeSelector.md +4 -5
  230. package/dist/ai/source/OptionsContainer.md +4 -7
  231. package/dist/components/BbAccordion/types.d.ts +4 -5
  232. package/dist/components/BbAlert/types.d.ts +3 -6
  233. package/dist/components/BbBadge/types.d.ts +6 -14
  234. package/dist/components/BbBaseButton/types.d.ts +11 -25
  235. package/dist/components/BbButton/types.d.ts +8 -17
  236. package/dist/components/BbCalendar/CalendarMonthPanel.vue_vue_type_script_setup_true_lang.js +64 -62
  237. package/dist/components/BbCalendar/types.d.ts +14 -29
  238. package/dist/components/BbCalendar/useDayGrid.js +131 -130
  239. package/dist/components/BbCheckbox/types.d.ts +7 -10
  240. package/dist/components/BbCheckboxGroup/types.d.ts +28 -58
  241. package/dist/components/BbCollapsible/types.d.ts +2 -7
  242. package/dist/components/BbColorInput/types.d.ts +21 -51
  243. package/dist/components/BbColorPalette/types.d.ts +4 -18
  244. package/dist/components/BbDatePicker/types.d.ts +15 -43
  245. package/dist/components/BbDatePickerInput/types.d.ts +26 -60
  246. package/dist/components/BbDialog/types.d.ts +16 -47
  247. package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +6 -3
  248. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +267 -261
  249. package/dist/components/BbDropdown/BbDropdownList.vue.d.ts +2 -2
  250. package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +298 -301
  251. package/dist/components/BbDropdown/types.d.ts +22 -46
  252. package/dist/components/BbDropdown/useDropdownContext.d.ts +2 -0
  253. package/dist/components/BbDropdownButton/types.d.ts +4 -8
  254. package/dist/components/BbDropzone/types.d.ts +11 -16
  255. package/dist/components/BbForm/BbForm.vue.d.ts +19 -46
  256. package/dist/components/BbForm/BbFormCustomField.js +40 -34
  257. package/dist/components/BbForm/bindFormField.d.ts +16 -2
  258. package/dist/components/BbForm/bindFormField.js +52 -52
  259. package/dist/components/BbHoverCard/types.d.ts +3 -5
  260. package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +137 -134
  261. package/dist/components/BbNumberInput/platform.d.ts +7 -0
  262. package/dist/components/BbNumberInput/platform.js +7 -0
  263. package/dist/components/BbNumberInput/types.d.ts +28 -45
  264. package/dist/components/BbOffCanvas/types.d.ts +11 -21
  265. package/dist/components/BbPagination/types.d.ts +7 -15
  266. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +23 -20
  267. package/dist/components/BbPopover/types.d.ts +11 -28
  268. package/dist/components/BbProgress/types.d.ts +3 -4
  269. package/dist/components/BbRadio/types.d.ts +10 -16
  270. package/dist/components/BbRadioGroup/types.d.ts +30 -63
  271. package/dist/components/BbRating/types.d.ts +7 -10
  272. package/dist/components/BbSelect/types.d.ts +45 -112
  273. package/dist/components/BbSelectPopover/types.d.ts +35 -101
  274. package/dist/components/BbSlider/types.d.ts +7 -10
  275. package/dist/components/BbTable/BbTable.vue.d.ts +5 -6
  276. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +1007 -892
  277. package/dist/components/BbTable/BbTableDataRow.d.ts +6 -0
  278. package/dist/components/BbTable/BbTableDataRow.js +66 -29
  279. package/dist/components/BbTable/BbTableEdgeRow.d.ts +2 -0
  280. package/dist/components/BbTable/BbTableEdgeRow.js +2 -2
  281. package/dist/components/BbTable/types.d.ts +89 -179
  282. package/dist/components/BbTable/types.js +1 -0
  283. package/dist/components/BbTable/useRowReorder.d.ts +54 -0
  284. package/dist/components/BbTable/useRowReorder.js +429 -0
  285. package/dist/components/BbTable/utils.d.ts +9 -1
  286. package/dist/components/BbTable/utils.js +33 -29
  287. package/dist/components/BbTable/warroom/harness.d.ts +253 -0
  288. package/dist/components/BbTabs/types.d.ts +12 -30
  289. package/dist/components/BbTag/types.d.ts +15 -28
  290. package/dist/components/BbTextInput/types.d.ts +21 -36
  291. package/dist/components/BbTextarea/types.d.ts +16 -30
  292. package/dist/components/BbTimePicker/types.d.ts +12 -28
  293. package/dist/components/BbTimePickerInput/types.d.ts +25 -51
  294. package/dist/components/BbTooltip/types.d.ts +6 -14
  295. package/dist/components/CommonField/types.d.ts +14 -21
  296. package/dist/components/CommonFieldInput/types.d.ts +7 -8
  297. package/dist/components/CommonPopover/types.d.ts +18 -28
  298. package/dist/components/CommonTimeSelector/CommonTimeSelector.vue.d.ts +4 -5
  299. package/dist/components/OptionsContainer/types.d.ts +4 -7
  300. package/dist/composables/useBbFormField.d.ts +9 -0
  301. package/dist/composables/useBbFormField.js +1 -0
  302. package/dist/composables/useFormBinding.d.ts +5 -8
  303. package/dist/i18n/locales/cs.json +8 -1
  304. package/dist/i18n/locales/da.json +8 -1
  305. package/dist/i18n/locales/de-CH.json +8 -1
  306. package/dist/i18n/locales/de.json +8 -1
  307. package/dist/i18n/locales/el.json +8 -1
  308. package/dist/i18n/locales/en-GB.json +8 -1
  309. package/dist/i18n/locales/en.json +8 -1
  310. package/dist/i18n/locales/es-MX.json +8 -1
  311. package/dist/i18n/locales/es.json +8 -1
  312. package/dist/i18n/locales/fi.json +8 -1
  313. package/dist/i18n/locales/fr.json +8 -1
  314. package/dist/i18n/locales/hu.json +8 -1
  315. package/dist/i18n/locales/it.json +8 -1
  316. package/dist/i18n/locales/ja.json +8 -1
  317. package/dist/i18n/locales/ko.json +8 -1
  318. package/dist/i18n/locales/nb.json +8 -1
  319. package/dist/i18n/locales/nl.json +8 -1
  320. package/dist/i18n/locales/pl.json +8 -1
  321. package/dist/i18n/locales/pt-BR.json +8 -1
  322. package/dist/i18n/locales/pt-PT.json +8 -1
  323. package/dist/i18n/locales/ro.json +8 -1
  324. package/dist/i18n/locales/ru.json +8 -1
  325. package/dist/i18n/locales/sv.json +8 -1
  326. package/dist/i18n/locales/tr.json +8 -1
  327. package/dist/i18n/locales/uk.json +8 -1
  328. package/dist/i18n/locales/zh-CN.json +8 -1
  329. package/dist/i18n/locales/zh-HK.json +8 -1
  330. package/dist/i18n/locales/zh-TW.json +8 -1
  331. package/dist/llms-full.txt +2412 -382
  332. package/dist/llms-medium.txt +67 -29
  333. package/dist/llms.txt +3 -2
  334. package/dist/locale-blueprint.json +8 -1
  335. package/dist/styles.css +2 -2
  336. package/dist/types/ptComponentMap.d.ts +1 -1
  337. package/dist/utilities/functions/toggleInput.d.ts +1 -1
  338. package/dist/utilities/functions/toggleInput.js +2 -2
  339. package/dist/utils/focusVisibility.d.ts +9 -0
  340. package/dist/utils/focusVisibility.js +12 -0
  341. package/dist/utils/versionCheck.js +1 -1
  342. package/llms.txt +3 -2
  343. package/package.json +3 -2
  344. package/scripts/lib/agent-manifest.mjs +44 -11
  345. package/scripts/lib/collapsible-trigger-aria.mjs +116 -0
  346. package/scripts/lib/dropdown-default-slot.mjs +118 -0
  347. package/scripts/lib/eslint-disable.mjs +6 -0
  348. package/scripts/lib/eslint-plugin.d.ts +7 -1
  349. package/scripts/lib/eslint-plugin.mjs +309 -0
  350. package/scripts/lib/project-variant-dts.mjs +79 -15
  351. package/scripts/lib/recipe-excerpts.mjs +98 -0
  352. package/scripts/lib/required-in-form.mjs +240 -0
  353. package/scripts/lib/validate-bb-markup.mjs +16 -0
package/README.md CHANGED
@@ -550,6 +550,85 @@ reports what removing cannot change.
550
550
  `bitboss-ui check` reports the same as `renamed-item-field` and
551
551
  `redundant-item-accessor`.
552
552
 
553
+ #### A trigger in BbDropdown's default slot: `dropdown-default-slot`
554
+
555
+ `BbDropdown`'s default slot holds `<BbDropdownGroup>` companions, which render
556
+ nothing. A button written there renders with no activator wiring, so clicking
557
+ it opens nothing. `bitboss-ui/dropdown-default-slot` (error) reports a native
558
+ element or a library component other than `BbDropdownGroup` in that slot; the
559
+ trigger belongs in the activator slot:
560
+
561
+ ```vue
562
+ <template>
563
+ <BbDropdown :items="items">
564
+ <template #activator="{ props }">
565
+ <BbButton v-bind="props" text="Actions" />
566
+ </template>
567
+ </BbDropdown>
568
+ </template>
569
+ ```
570
+
571
+ With no slot to fill, `v-bb-dropdown="items"` on the trigger does the same.
572
+ Your own components there are not reported: one may render a group.
573
+ `bitboss-ui check` reports the same as `dropdown-default-slot`.
574
+
575
+ #### A BbCollapsible trigger without `aria-expanded`: `collapsible-trigger-aria`
576
+
577
+ `BbCollapsible` ships no trigger, so the button that opens it owns the
578
+ disclosure state. `bitboss-ui/collapsible-trigger-aria` (error) reports a
579
+ native element or a library component whose `@click` is exactly
580
+ `open = !open` on the ref a `BbCollapsible` binds, when it carries no
581
+ `aria-expanded`:
582
+
583
+ ```vue
584
+ <template>
585
+ <BbButton
586
+ aria-controls="advanced"
587
+ :aria-expanded="open"
588
+ text="Advanced options"
589
+ @click="open = !open"
590
+ />
591
+ <BbCollapsible id="advanced" v-model="open">…</BbCollapsible>
592
+ </template>
593
+ ```
594
+
595
+ Only that certain case is judged: a handler that calls a method, a toggle in
596
+ script, a switch bound to the same ref and your own components are never
597
+ reported. `BbAccordion` wires all of it for you.
598
+ `bitboss-ui check` reports the same as `collapsible-trigger-aria`.
599
+
600
+ #### `required` inside a BbForm: `no-required-in-form`
601
+
602
+ A `BbForm` posts `novalidate` (vee-validate owns validation), so `required` on
603
+ a form control inside one shows the asterisk and enforces nothing: an empty
604
+ field submits. `bitboss-ui/no-required-in-form` (error) reports a static
605
+ `required` or a bound `:required` on a library form control inside a
606
+ `<BbForm>` in the same template. Write the requirement where the form
607
+ validates; the asterisk follows:
608
+
609
+ ```vue
610
+ <template>
611
+ <BbForm :model="draft" @submit="save">
612
+ <BbTextInput name="email" label="Email" rules="required|email" />
613
+ <BbTextInput
614
+ name="vat"
615
+ label="VAT number"
616
+ :rules="{ required: isCompany }"
617
+ />
618
+ </BbForm>
619
+ </template>
620
+ ```
621
+
622
+ `--fix` rewrites a bare `required` to `rules="required"` when the control has
623
+ no `rules` and the form no `schema`; anything else is reported for you to move
624
+ (with a `schema`, require the key there). A control your own component renders
625
+ inside the form's slot is not seen. `required` beside a `:rules` function is
626
+ reported too, without a fix: the function checks a value only when there is
627
+ one, so register it (`defineRule('vat', isVat)`) and write
628
+ `rules="required|vat"`, or require the key in the schema. Outside a `BbForm`
629
+ `required` stays: a plain `<form>` validates natively.
630
+ `bitboss-ui check` reports the same as `required-in-form`.
631
+
553
632
  #### Rating star colours: `no-star-color`
554
633
 
555
634
  `bitboss-ui/no-star-color` flags any colour utility in `BbRating`'s `pt:item`,
@@ -1416,13 +1416,14 @@ function getComponent({
1416
1416
  // `removed` and `renamed` repeat each other with migration metadata; one
1417
1417
  // line per old name answers "what happened to X" (include 'history' for
1418
1418
  // the records the fixers use).
1419
+ // The agent file leaves both out (they are `<Name>.removed.json`).
1419
1420
  if (wanted.has('history')) {
1421
+ if (entry.removed) component.removed = entry.removed;
1420
1422
  if (entry.renamed) component.renamed = entry.renamed;
1421
1423
  component.cssRenames = cssRenameLines(entry, manifest);
1422
1424
  } else {
1423
1425
  const history = historyWithCss(entry, manifest);
1424
1426
  if (history.length) component.history = history;
1425
- delete component.removed;
1426
1427
  }
1427
1428
 
1428
1429
  const always = guide.filter(
@@ -97,7 +97,10 @@ import {
97
97
  handImportedStylesFindings,
98
98
  } from '../scripts/lib/hand-imported-styles.mjs';
99
99
  import { unknownLocaleFindings } from '../scripts/lib/locale-check.mjs';
100
- import { syncProjectVariantDts } from '../scripts/lib/project-variant-dts.mjs';
100
+ import {
101
+ syncNotice,
102
+ syncProjectVariantDts,
103
+ } from '../scripts/lib/project-variant-dts.mjs';
101
104
  import { refuseLinkedTarget } from '../scripts/lib/linked-target.mjs';
102
105
  import {
103
106
  ensureCheckUnknownComponents,
@@ -906,10 +909,10 @@ async function checkCommand(
906
909
  pathToFileURL(join(PACKAGE_ROOT, 'dist', 'project-dts.js')).href
907
910
  );
908
911
  const synced = syncProjectVariantDts(projectRoot, projectDts);
909
- if (synced.status === 'written' && !jsonMode)
910
- console.log(
911
- `[bitboss-ui] Wrote ${synced.path} from your build config: registered variant names now type-check. Commit it.`
912
- );
912
+ const notice = syncNotice(synced);
913
+ // Q65.2: a skip is said too (stderr under `--json`, like `--fix`'s lines).
914
+ if (notice && !(jsonMode && synced.status === 'written'))
915
+ (jsonMode ? console.error : console.log)(notice);
913
916
  } catch {
914
917
  // No built `dist/project-dts.js` (the library repo before a build):
915
918
  // nothing to write with, and nothing for `check` to fail over.
@@ -1500,6 +1503,23 @@ Commands:
1500
1503
  \`reactive-v-model\` (a \`v-model\` on a \`<script
1501
1504
  setup>\` \`const x = reactive(…)\`: a production build
1502
1505
  rejects the write-back; hold it in a \`ref()\`),
1506
+ \`text-null-value\` (a \`v-model\` on BbTextInput /
1507
+ BbTextarea bound to a ref typed \`string\` while the
1508
+ field writes \`null\` once emptied: widen it to
1509
+ \`ref<string | null>\`, or \`null-value="string"\`),
1510
+ \`dropdown-default-slot\` (a trigger in a BbDropdown's
1511
+ default slot, which holds BbDropdownGroup companions:
1512
+ it opens nothing; use \`#activator="{ props }"\` with
1513
+ \`v-bind="props"\`, or \`v-bb-dropdown\`),
1514
+ \`collapsible-trigger-aria\` (an element whose
1515
+ \`@click="open = !open"\` flips a BbCollapsible's
1516
+ ref, with no \`aria-expanded\`: add
1517
+ \`:aria-expanded="open"\` + \`aria-controls\`),
1518
+ \`required-in-form\` (\`required\` on a form control
1519
+ inside a \`BbForm\`: the asterisk shows but the form
1520
+ posts \`novalidate\`, so nothing enforces it; write
1521
+ \`rules="required"\` or make it required in the
1522
+ schema),
1503
1523
  \`form-v-model\` (\`v-model\`, \`:model-value\` or an
1504
1524
  \`@update:model-value\` listener on a \`BbForm\`: it
1505
1525
  takes its object one way, \`:model="draft"\`),
@@ -644,7 +644,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
644
644
  | --- | --- | --- | --- | --- |
645
645
  | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `header`, `panel`, `root`. States: `open`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbAccordionProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
646
646
  | `eager` | `boolean \| undefined` | `false` | | Renders the content at mount, while closed, instead of on the first open. Closed content is `inert` and `aria-hidden` either way. |
647
- | `headingLevel` | `1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | | | Wraps the header button in a heading of this level (`<h1>`–`<h6>`), the APG accordion shape, so the header joins the page's outline. Match the level to where the accordion sits (a section under an `<h2>` takes `3`). No visual change: the heading's margins and font are reset. Omitted, there is no heading. |
647
+ | `headingLevel` | `1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | | | Wraps the header button in a heading of this level (`<h1>`–`<h6>`, the APG accordion shape) so it joins the page outline: under an `<h2>`, pass `3`. Its margins and font are reset. Omitted, no heading. |
648
648
  | `id` | `string \| undefined` | | | The root's `id`; the header button's id (`<id>_header`) derives from it. Generated when omitted. |
649
649
  | `modelValue` | `boolean \| undefined` | `false` | | Used by v-model to trigger opening / closing the collapsible. An accordion with no `v-model` starts closed. |
650
650
  | `transitionDuration` | `number \| undefined` | `250` | | How long the transition has to last in milliseconds |
@@ -494,7 +494,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
494
494
 
495
495
  ## Slots
496
496
 
497
- - `default` — scope: `any` — The alert body — the shortest form, and the one to reach for: `<BbAlert>Something went wrong</BbAlert>`. Same idiom as the rest of the `text`-prop family (BbButton, BbBaseButton, BbTooltip, BbIndicator). When more than one source is present the most specific wins: `#text` > children > the `text` prop.
497
+ - `default` — scope: `any` — The alert body, the shortest form: `<BbAlert>Something went wrong</BbAlert>`. The most specific source wins: `#text` > children > the `text` prop.
498
498
  - `text` — scope: `BbAlertTextSlotProps` `{ text: string }` — Replaces the default alert body text. Wins over children and `text`.
499
499
  - `title` — scope: `BbAlertTitleSlotProps` `{ text: string }` — Replaces the default alert title text.
500
500
 
@@ -42,8 +42,10 @@ placed in a label is not a reliable way to convey a requirement. What actually
42
42
  carries mandatoriness to assistive tech is the **control itself**: the native
43
43
  `required` attribute when you set the `required` prop, and
44
44
  `aria-required="true"` when a `BbForm` makes the field mandatory (a `required`
45
- rule or the schema). Inside a form the browser never validates: only your
46
- explicit `required` prop puts the native attribute on.
45
+ rule or the schema). Inside a form the browser never validates, so there the
46
+ `required` prop is an error (`bitboss-ui/no-required-in-form`): it would mark
47
+ the field with nothing enforcing it; write `rules="required"` or require the
48
+ key in the schema.
47
49
 
48
50
  The practical consequence: the marker is a _visual_ affordance only, and the
49
51
  control's own required state is the real thing. Never ship a field that shows
@@ -858,9 +858,9 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
858
858
  | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `clear`, `icon`, `root`, `spinner`, `text`. States: `loading`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbBadgeProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
859
859
  | `append:icon` | `string \| undefined` | | | Icon rendered after the label. On a `clearable` badge it shares the trailing slot with the clear button: the icon shows at rest and the clear button reveals over it on hover/focus. |
860
860
  | `clearable` | `boolean \| undefined` | `false` | | Renders a trailing clear button and emits `click:clear` when pressed — turns the badge into a dismissible token (the pattern formerly served by `BbChip`). |
861
- | `clearableLabel` | `string \| undefined` | | | Accessible label for the clear button. Rarely needed: unset, the name is **composed** from the localized `badge.clearLabel` verb and the badge's own text — "Remove Italy", not a bare "Remove" — which is already the right name in almost every case. Composition is skipped when a `BbBadgeButton` owns the body (the verb then stands alone, so the two controls do not both announce the label). Set this only to override that composed name outright. |
861
+ | `clearableLabel` | `string \| undefined` | | | Accessible name of the clear button. Rarely needed: unset, it is composed from the localized `badge.clearLabel` verb and the badge text ("Remove Italy"), or the verb alone when a `BbBadgeButton` owns the body. |
862
862
  | `icon` | `string \| undefined` | | | Renders an icon-only badge. When set, `prepend:icon` / `append:icon` are ignored and the default slot becomes the badge's visually hidden accessible label — always provide it. |
863
- | `loading` | `boolean \| undefined` | `false` | | Shows a spinner in place of the leftmost icon, mirroring `BbButton`: it replaces the `icon` glyph or the `prepend:icon` when one is set; when the `append:icon` is the sole icon it replaces that instead (e.g. a select-activator chevron while the value saves), and with no icons at all it takes the leading slot. Adds a visually hidden loading announcement for screen readers. |
863
+ | `loading` | `boolean \| undefined` | `false` | | Shows a spinner in place of the leftmost icon (`icon` or `prepend:icon`; the `append:icon` when it is the only one; else the leading slot), as `BbButton` does, with a hidden loading announcement. |
864
864
  | `prepend:icon` | `string \| undefined` | | | Icon rendered before the label. |
865
865
  | `size` | `'xs'` \| `'sm'` \| `'md'` \| `'lg'` \| `'xl'` \| `'2xl'` | `"md"` | | Preset size of the badge. |
866
866
  | `variant` | `'outline'` \| `'primary'` \| `'destructive'` \| `'secondary'` \| `'none'` | `"primary"` | | Visual variant: `primary`, `secondary`, `destructive`, or `outline`. Register more via the vite plugin `badgeVariants` option and style `.bb-badge--<variant>` (set `--bg` / `--fg`). `'none'` renders no variant class — an escape hatch for a one-off look set through `--bg` / `--fg` on the element. |
@@ -21,7 +21,7 @@
21
21
  | `component` | `string \| undefined` | | | Inertia: the page component this visit resolves to. Rarely set by hand — the server normally decides it. |
22
22
  | `data` | `object \| undefined` | | | Request payload forwarded to Inertia when navigating via `href` in an Inertia-enabled app. Ignored when not using Inertia. |
23
23
  | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. - When rendering as a native button, sets the `disabled` attribute. - When rendering as a link (anchor/Inertia), removes `href`, adds `aria-disabled="true"` and `tabindex="-1"`, and prevents navigation: the link stays announced as a link but leaves the tab order. |
24
- | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download. Renders a plain `<a download>` doing a native navigation — never an Inertia/router visit — so file downloads (including same-origin, `blob:` and `data:` URLs) work. Pass a string to set the suggested filename. One of the native-anchor signals alongside `target` and `external`. |
24
+ | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download: a plain `<a download>` with a native navigation (never an Inertia or router visit), so `blob:` and `data:` files work too. A string sets the suggested filename. |
25
25
  | `exactActiveClass` | `string \| undefined` | `'router-link-exact-active'` | | CSS class applied when the component renders as a link and the target of the link and the url matches exactly. Ported for Inertia compatibility. |
26
26
  | `except` | `string[] \| undefined` | | | Inertia: the inverse of `only` — properties to EXCLUDE from a partial reload. Pass one or the other, not both. |
27
27
  | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception — the same intent as Nuxt's `NuxtLink` `external`. Use for links outside the SPA. One of the native-anchor signals alongside `target` and `download`. |
@@ -32,7 +32,7 @@
32
32
  | `onBefore` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia right before the request is sent. |
33
33
  | `onCancel` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a request is cancelled. |
34
34
  | `onCancelToken` | `((cancelToken: unknown) => void) \| undefined` | | | Receives the Inertia cancel token source when a request is initiated. Can be used to cancel the request. Typed platform-agnostically (matching BbButton) so the library's types never require `@inertiajs/vue3` to be installed. |
35
- | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Lifecycle hook invoked by Inertia when the server responds with VALIDATION errors — the ordinary 422 path. This is the only failure callback Inertia's `Link` accepts. `onHttpException` (server answered 5xx) and `onNetworkError` (the request never arrived) are `router.visit()` / `useForm()` options, NOT Link props — declaring them here would only paste bogus listeners onto the anchor. Use a form or an explicit visit when you need to tell them apart. |
35
+ | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Inertia's hook for a VALIDATION error answer (the 422 path): the only failure callback `Link` accepts. `onHttpException` (5xx) and `onNetworkError` are `router.visit()` / `useForm()` options, never Link props. |
36
36
  | `onFinish` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia after the request has finished (regardless of success or error). |
37
37
  | `only` | `string[] \| undefined` | | | Limits the properties that are preserved in Inertia partial reloads. |
38
38
  | `onPrefetched` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a prefetch for this link has completed and is cached. |
@@ -48,7 +48,7 @@
48
48
  | `queryStringArrayFormat` | `"brackets" \| "indices" \| undefined` | `'brackets'` | | Format to use when serializing array values into the query string for Inertia requests. |
49
49
  | `rel` | `string \| undefined` | | | Relationship between the current document and the linked resource. Useful for security when opening new tabs (e.g. `noopener noreferrer`). |
50
50
  | `replace` | `boolean \| undefined` | `false` | | Uses history replacement instead of push navigation. - With Vue Router (`to`), calls `router.replace`. - With Inertia (`href`), performs a replace visit. |
51
- | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element to render for the plain (non-link, non-router) button: - `'button'` (default) — real button semantics and keyboard behavior - `'div'` / `'span'` — a clickable box nested inside another button or link, where a nested `<button>` would be invalid markup - `'label'` — a file-upload trigger wrapping a visually hidden `<input>` Ignored when the component renders as a link/router (`href`/`to`) — those always render `<a>`/router-link. When set to a non-`button` tag the native `type`/`disabled` attributes are dropped (they're invalid off a `<button>`); disabled state is still conveyed via the `bb-base-button--disabled` class. |
51
+ | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element for the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside another button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
52
52
  | `target` | `string \| undefined` | | | Target browsing context for anchor/Inertia links (e.g. `_self`, `_blank`). Ignored when rendering as a native button. |
53
53
  | `to` | `string \| wt \| bt \| undefined` | | | Route location to navigate to. When provided (and not disabled), the component renders as a Vue Router link. |
54
54
  | `type` | `"button" \| "submit" \| "reset" \| undefined` | `'button'` | | Native `type` attribute used when rendering as a button (e.g. `button`, `submit`, `reset`). |
@@ -239,6 +239,11 @@ destination onto these props instead of an `@click` handler:
239
239
  any target other than `_self` (`_blank`, named windows, …) automatically
240
240
  merges `noopener noreferrer` into `rel`.
241
241
  - **`to`** — a Vue Router `RouteLocationRaw`, rendered as a router/Nuxt link.
242
+ - **`download`** — marks an `href` link as a download: a plain `<a download>`
243
+ doing a native navigation, never an Inertia / router visit, so file
244
+ downloads work (same-origin, `blob:` and `data:` URLs included). A string
245
+ sets the suggested filename. It is a native-anchor signal like `target` and
246
+ `external`.
242
247
  - **`replace`** — history replacement (`router.replace` under Vue Router, a
243
248
  replace visit under Inertia).
244
249
  - **Inertia visit options** ride along with `href`: request shape (`method`,
@@ -519,7 +524,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
519
524
  | `component` | `string \| undefined` | | | Inertia: the page component this visit resolves to. Rarely set by hand — the server normally decides it. |
520
525
  | `data` | `object \| undefined` | | | Request payload forwarded to Inertia when navigating via `href` in an Inertia-enabled app. Ignored when not using Inertia. |
521
526
  | `disabled` | `boolean \| undefined` | `false` | | Disables user interaction. - When rendering as a native button, sets the `disabled` attribute. - When rendering as a link (anchor/Inertia), removes `href`, adds `aria-disabled="true"` and `tabindex="-1"`, and prevents navigation: the link stays announced as a link but leaves the tab order. |
522
- | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download. Renders a plain `<a download>` doing a native navigation — never an Inertia/router visit — so file downloads (including same-origin, `blob:` and `data:` URLs) work. Pass a string to set the suggested filename. One of the native-anchor signals alongside `target` and `external`. |
527
+ | `download` | `string \| boolean \| undefined` | | | Marks an `href` link as a download: a plain `<a download>` with a native navigation (never an Inertia or router visit), so `blob:` and `data:` files work too. A string sets the suggested filename. |
523
528
  | `exactActiveClass` | `string \| undefined` | `'router-link-exact-active'` | | CSS class applied when the component renders as a link and the target of the link and the url matches exactly. Ported for Inertia compatibility. |
524
529
  | `except` | `string[] \| undefined` | | | Inertia: the inverse of `only` — properties to EXCLUDE from a partial reload. Pass one or the other, not both. |
525
530
  | `external` | `boolean \| undefined` | `false` | | Forces an `href` link to render as a plain `<a>` (native navigation), bypassing Inertia/router interception — the same intent as Nuxt's `NuxtLink` `external`. Use for links outside the SPA. One of the native-anchor signals alongside `target` and `download`. |
@@ -530,7 +535,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
530
535
  | `onBefore` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia right before the request is sent. |
531
536
  | `onCancel` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a request is cancelled. |
532
537
  | `onCancelToken` | `((cancelToken: unknown) => void) \| undefined` | | | Receives the Inertia cancel token source when a request is initiated. Can be used to cancel the request. Typed platform-agnostically (matching BbButton) so the library's types never require `@inertiajs/vue3` to be installed. |
533
- | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Lifecycle hook invoked by Inertia when the server responds with VALIDATION errors — the ordinary 422 path. This is the only failure callback Inertia's `Link` accepts. `onHttpException` (server answered 5xx) and `onNetworkError` (the request never arrived) are `router.visit()` / `useForm()` options, NOT Link props — declaring them here would only paste bogus listeners onto the anchor. Use a form or an explicit visit when you need to tell them apart. |
538
+ | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Inertia's hook for a VALIDATION error answer (the 422 path): the only failure callback `Link` accepts. `onHttpException` (5xx) and `onNetworkError` are `router.visit()` / `useForm()` options, never Link props. |
534
539
  | `onFinish` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia after the request has finished (regardless of success or error). |
535
540
  | `only` | `string[] \| undefined` | | | Limits the properties that are preserved in Inertia partial reloads. |
536
541
  | `onPrefetched` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a prefetch for this link has completed and is cached. |
@@ -546,7 +551,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
546
551
  | `queryStringArrayFormat` | `"brackets" \| "indices" \| undefined` | `'brackets'` | | Format to use when serializing array values into the query string for Inertia requests. |
547
552
  | `rel` | `string \| undefined` | | | Relationship between the current document and the linked resource. Useful for security when opening new tabs (e.g. `noopener noreferrer`). |
548
553
  | `replace` | `boolean \| undefined` | `false` | | Uses history replacement instead of push navigation. - With Vue Router (`to`), calls `router.replace`. - With Inertia (`href`), performs a replace visit. |
549
- | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element to render for the plain (non-link, non-router) button: - `'button'` (default) — real button semantics and keyboard behavior - `'div'` / `'span'` — a clickable box nested inside another button or link, where a nested `<button>` would be invalid markup - `'label'` — a file-upload trigger wrapping a visually hidden `<input>` Ignored when the component renders as a link/router (`href`/`to`) — those always render `<a>`/router-link. When set to a non-`button` tag the native `type`/`disabled` attributes are dropped (they're invalid off a `<button>`); disabled state is still conveyed via the `bb-base-button--disabled` class. |
554
+ | `tag` | `'button'` \| `'div'` \| `'span'` \| `'label'` | `'button'` | | Element for the plain (non-link) button: `'button'` (default), `'div'` / `'span'` (a clickable box inside another button or link) or `'label'` (a file-upload trigger). Ignored with `href` / `to`. Off `button`, native `type` / `disabled` drop (disabled stays a class). |
550
555
  | `target` | `string \| undefined` | | | Target browsing context for anchor/Inertia links (e.g. `_self`, `_blank`). Ignored when rendering as a native button. |
551
556
  | `text` | `string \| undefined` | | | Fallback text content rendered when no default slot is provided. |
552
557
  | `to` | `string \| wt \| bt \| undefined` | | | Route location to navigate to. When provided (and not disabled), the component renders as a Vue Router link. |
@@ -41,11 +41,25 @@ the glyph in for the visuals.
41
41
  ### Compose it into a custom control
42
42
 
43
43
  The glyph is decorative (`aria-hidden`), so it **must** be paired with a real
44
- control. The reliable pattern: a `<label>` wrapping a visually-hidden native
45
- `<input type="checkbox">` (keep it in the a11y tree with `sr-only`, never
46
- `display:none`) plus the glyph reflecting `checked`. You get native keyboard
47
- support, focus, and form submission for free, and total freedom over the layout
48
- around it.
44
+ control. The reliable pattern: a `<label>` wrapping a native
45
+ `<input type="checkbox">` stacked under the glyph, the glyph reflecting
46
+ `checked`. You get native keyboard support, focus, and form submission for
47
+ free, and total freedom over the layout around it.
48
+
49
+ **Stack the input under the glyph; never shrink it to `sr-only`.** Put both in
50
+ one grid cell (`grid` on a wrapper, `col-start-1 row-start-1` on each). The
51
+ input is `opacity-0` and fills the cell (`size-full min-w-0 m-0
52
+ appearance-none`); the glyph is `pointer-events-none`. Test runners click the
53
+ input itself and first check that it is the element under the pointer
54
+ (Playwright's `getByLabel(…).check()`): a 1px clipped `sr-only` input is never
55
+ that element, so the click times out. Stacked, the pointer, a label click and
56
+ the test runner all reach the input. Never `display:none` or `visibility:
57
+ hidden` either: the input must stay in the accessibility tree and focusable.
58
+
59
+ **Show keyboard focus on the glyph.** The input is invisible, so pass its
60
+ focus to the glyph's `focus-visible` prop: on the input's `focus`, keep
61
+ whether it `matches(':focus-visible')`; clear it on `blur` (the example does
62
+ this).
49
63
 
50
64
  **Invoice alert channel cards**
51
65
 
@@ -71,14 +85,27 @@ around it.
71
85
  : 'border-(--bb-border) hover:bg-(--bb-surface-hover)'
72
86
  "
73
87
  >
74
- <!-- Glyph is decorative; the sr-only input owns focus, keyboard, and form state. -->
75
- <input
76
- v-model="selected"
77
- class="sr-only"
78
- type="checkbox"
79
- :value="channel.id"
80
- />
81
- <BbBaseCheckboxIcon :checked="selected.includes(channel.id)" />
88
+ <!--
89
+ The input and the glyph share one grid cell. The input is invisible
90
+ but fills the cell, so a click (and a test runner's "is the input the
91
+ hit target" check) lands on it; the glyph lets the pointer through
92
+ and shows the input's state, keyboard focus included.
93
+ -->
94
+ <span class="grid shrink-0">
95
+ <input
96
+ v-model="selected"
97
+ class="col-start-1 row-start-1 m-0 size-full min-w-0 cursor-pointer appearance-none opacity-0"
98
+ type="checkbox"
99
+ :value="channel.id"
100
+ @blur="focusVisible = null"
101
+ @focus="onFocus(channel.id, $event)"
102
+ />
103
+ <BbBaseCheckboxIcon
104
+ :checked="selected.includes(channel.id)"
105
+ class="pointer-events-none col-start-1 row-start-1"
106
+ :focus-visible="focusVisible === channel.id"
107
+ />
108
+ </span>
82
109
  <span class="flex min-w-0 flex-col">
83
110
  <span class="text-sm font-medium">{{ channel.label }}</span>
84
111
  <span class="text-xs text-(--bb-text-muted)">{{ channel.hint }}</span>
@@ -101,6 +128,14 @@ const channels: Channel[] = [
101
128
  ];
102
129
 
103
130
  const selected = ref<string[]>(['email']);
131
+
132
+ // The ring shows for keyboard focus only, as the library's own toggles do.
133
+ const focusVisible = ref<string | null>(null);
134
+ function onFocus(id: string, event: FocusEvent) {
135
+ focusVisible.value = (event.target as HTMLElement).matches(':focus-visible')
136
+ ? id
137
+ : null;
138
+ }
104
139
  </script>
105
140
  ```
106
141
 
@@ -40,11 +40,25 @@ the glyph in for the visuals.
40
40
  ### Compose it into a custom control
41
41
 
42
42
  The glyph is decorative (`aria-hidden`), so it **must** be paired with a real
43
- control. The reliable pattern: a `<label>` wrapping a visually-hidden native
44
- `<input type="radio">` (keep it in the a11y tree with `sr-only`, never
45
- `display:none`), all radios in the group sharing one `name`, plus the glyph
46
- reflecting `checked`. Native radios give you exclusive selection, roving focus
47
- and arrow-key navigation for free.
43
+ control. The reliable pattern: a `<label>` wrapping a native
44
+ `<input type="radio">` stacked under the glyph, all radios in the group
45
+ sharing one `name`, the glyph reflecting `checked`. Native radios give you
46
+ exclusive selection, roving focus and arrow-key navigation for free.
47
+
48
+ **Stack the input under the glyph; never shrink it to `sr-only`.** Put both in
49
+ one grid cell (`grid` on a wrapper, `col-start-1 row-start-1` on each). The
50
+ input is `opacity-0` and fills the cell (`size-full min-w-0 m-0
51
+ appearance-none`); the glyph is `pointer-events-none`. Test runners click the
52
+ input itself and first check that it is the element under the pointer
53
+ (Playwright's `getByLabel(…).check()`): a 1px clipped `sr-only` input is never
54
+ that element, so the click times out. Stacked, the pointer, a label click and
55
+ the test runner all reach the input. Never `display:none` or `visibility:
56
+ hidden` either: the input must stay in the accessibility tree and focusable.
57
+
58
+ **Show keyboard focus on the glyph.** The input is invisible, so pass its
59
+ focus to the glyph's `focus-visible` prop: on the input's `focus`, keep
60
+ whether it `matches(':focus-visible')`; clear it on `blur` (the example does
61
+ this).
48
62
 
49
63
  **Workspace plan cards**
50
64
 
@@ -70,15 +84,28 @@ and arrow-key navigation for free.
70
84
  : 'border-(--bb-border) hover:bg-(--bb-surface-hover)'
71
85
  "
72
86
  >
73
- <!-- Native radios sharing `name` give exclusive selection + arrow keys. -->
74
- <input
75
- v-model="selected"
76
- class="sr-only"
77
- name="plan"
78
- type="radio"
79
- :value="plan.id"
80
- />
81
- <BbBaseRadioIcon :checked="selected === plan.id" />
87
+ <!--
88
+ Native radios sharing `name` give exclusive selection and arrow
89
+ keys. Each one shares a grid cell with its glyph: invisible, but
90
+ filling the cell, so clicks and test runners hit the input; the
91
+ glyph lets the pointer through and shows the input's state.
92
+ -->
93
+ <span class="grid shrink-0">
94
+ <input
95
+ v-model="selected"
96
+ class="col-start-1 row-start-1 m-0 size-full min-w-0 cursor-pointer appearance-none opacity-0"
97
+ name="plan"
98
+ type="radio"
99
+ :value="plan.id"
100
+ @blur="focusVisible = null"
101
+ @focus="onFocus(plan.id, $event)"
102
+ />
103
+ <BbBaseRadioIcon
104
+ :checked="selected === plan.id"
105
+ class="pointer-events-none col-start-1 row-start-1"
106
+ :focus-visible="focusVisible === plan.id"
107
+ />
108
+ </span>
82
109
  <span class="flex min-w-0 flex-1 items-start justify-between gap-3">
83
110
  <span class="flex min-w-0 flex-col">
84
111
  <span class="text-sm font-medium">{{ plan.label }}</span>
@@ -121,6 +148,14 @@ const plans: Plan[] = [
121
148
  ];
122
149
 
123
150
  const selected = ref('team');
151
+
152
+ // The ring shows for keyboard focus only, as the library's own toggles do.
153
+ const focusVisible = ref<string | null>(null);
154
+ function onFocus(id: string, event: FocusEvent) {
155
+ focusVisible.value = (event.target as HTMLElement).matches(':focus-visible')
156
+ ? id
157
+ : null;
158
+ }
124
159
  </script>
125
160
  ```
126
161
 
@@ -41,10 +41,25 @@ checkbox with `role="switch"`) and drop the glyph in for the visuals.
41
41
  ### Compose it into a custom control
42
42
 
43
43
  The glyph is decorative (`aria-hidden`), so it **must** be paired with a real
44
- control. The reliable pattern: a `<label>` wrapping a visually-hidden native
45
- `<input type="checkbox" role="switch">` (keep it in the a11y tree with `sr-only`,
46
- never `display:none`) plus the glyph reflecting `checked`. You get native
47
- keyboard support, focus, and switch semantics for free.
44
+ control. The reliable pattern: a `<label>` wrapping a native
45
+ `<input type="checkbox" role="switch">` stacked under the glyph, the glyph
46
+ reflecting `checked`. You get native keyboard support, focus, and switch
47
+ semantics for free.
48
+
49
+ **Stack the input under the glyph; never shrink it to `sr-only`.** Put both in
50
+ one grid cell (`grid` on a wrapper, `col-start-1 row-start-1` on each). The
51
+ input is `opacity-0` and fills the cell (`size-full min-w-0 m-0
52
+ appearance-none`); the glyph is `pointer-events-none`. Test runners click the
53
+ input itself and first check that it is the element under the pointer
54
+ (Playwright's `getByLabel(…).check()`): a 1px clipped `sr-only` input is never
55
+ that element, so the click times out. Stacked, the pointer, a label click and
56
+ the test runner all reach the input. Never `display:none` or `visibility:
57
+ hidden` either: the input must stay in the accessibility tree and focusable.
58
+
59
+ **Show keyboard focus on the glyph.** The input is invisible, so pass its
60
+ focus to the glyph's `focus-visible` prop: on the input's `focus`, keep
61
+ whether it `matches(':focus-visible')`; clear it on `blur` (the example does
62
+ this).
48
63
 
49
64
  **Feature flag rows**
50
65
 
@@ -68,21 +83,33 @@ keyboard support, focus, and switch semantics for free.
68
83
  <span class="text-sm font-medium">{{ flag.label }}</span>
69
84
  <span class="text-xs text-(--bb-text-muted)">{{ flag.hint }}</span>
70
85
  </span>
71
- <!-- Glyph tracks state; the checkbox + role="switch" owns a11y. -->
72
- <input
73
- v-model="state[flag.id]"
74
- class="sr-only"
75
- role="switch"
76
- type="checkbox"
77
- />
78
- <BbBaseSwitchIcon :checked="state[flag.id]" />
86
+ <!--
87
+ The input (checkbox + role="switch") and the glyph share one grid
88
+ cell: invisible, the input fills it, so clicks and test runners hit
89
+ the input; the glyph lets the pointer through and shows its state.
90
+ -->
91
+ <span class="grid shrink-0">
92
+ <input
93
+ v-model="state[flag.id]"
94
+ class="col-start-1 row-start-1 m-0 size-full min-w-0 cursor-pointer appearance-none opacity-0"
95
+ role="switch"
96
+ type="checkbox"
97
+ @blur="focusVisible = null"
98
+ @focus="onFocus(flag.id, $event)"
99
+ />
100
+ <BbBaseSwitchIcon
101
+ :checked="state[flag.id]"
102
+ class="pointer-events-none col-start-1 row-start-1"
103
+ :focus-visible="focusVisible === flag.id"
104
+ />
105
+ </span>
79
106
  </label>
80
107
  </div>
81
108
  </div>
82
109
  </template>
83
110
 
84
111
  <script setup lang="ts">
85
- import { reactive } from 'vue';
112
+ import { reactive, ref } from 'vue';
86
113
  import { BbBaseSwitchIcon } from 'bitboss-ui';
87
114
 
88
115
  type Flag = { id: string; label: string; hint: string };
@@ -111,6 +138,14 @@ const state = reactive<Record<string, boolean>>({
111
138
  summaries: false,
112
139
  analytics: false,
113
140
  });
141
+
142
+ // The ring shows for keyboard focus only, as the library's own toggles do.
143
+ const focusVisible = ref<string | null>(null);
144
+ function onFocus(id: string, event: FocusEvent) {
145
+ focusVisible.value = (event.target as HTMLElement).matches(':focus-visible')
146
+ ? id
147
+ : null;
148
+ }
114
149
  </script>
115
150
  ```
116
151
 
@@ -60,7 +60,10 @@ disguised as text — pair it with `href`/`to`, not with heavy actions.
60
60
  behaviour and accessibility but takes no look, so your own classes don't have
61
61
  to fight a built-in one. You then own hover, focus and disabled. It is fine for
62
62
  a one-off; keep the inline styling short, and when the same look shows up
63
- again, register it as a real variant instead.
63
+ again, register it as a real variant instead: the plugin's `buttonVariants`
64
+ option types the name, and you style `.bb-button--<name>` (the library ships
65
+ no CSS for it). The same holds for `BbDropdownButton`, whose `variant` styles
66
+ both halves of the split control.
64
67
 
65
68
  **`link` changes color, not layout.** It keeps the button box: the size scale's
66
69
  height and horizontal padding still apply, so it occupies button space and never
@@ -1138,7 +1141,7 @@ Anti-patterns:
1138
1141
  | `onBefore` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia right before the request is sent. |
1139
1142
  | `onCancel` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a request is cancelled. |
1140
1143
  | `onCancelToken` | `((cancelToken: unknown) => void) \| undefined` | | | Receives the Inertia cancel token source when a request is initiated. Can be used to cancel the request. Typed platform-agnostically (matching BbButton) so the library's types never require `@inertiajs/vue3` to be installed. |
1141
- | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Lifecycle hook invoked by Inertia when the server responds with VALIDATION errors — the ordinary 422 path. This is the only failure callback Inertia's `Link` accepts. `onHttpException` (server answered 5xx) and `onNetworkError` (the request never arrived) are `router.visit()` / `useForm()` options, NOT Link props — declaring them here would only paste bogus listeners onto the anchor. Use a form or an explicit visit when you need to tell them apart. |
1144
+ | `onError` | `((errors: Record<string, string>) => void) \| undefined` | | | Inertia's hook for a VALIDATION error answer (the 422 path): the only failure callback `Link` accepts. `onHttpException` (5xx) and `onNetworkError` are `router.visit()` / `useForm()` options, never Link props. |
1142
1145
  | `onFinish` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia after the request has finished (regardless of success or error). |
1143
1146
  | `only` | `string[] \| undefined` | | | Limits the properties that are preserved in Inertia partial reloads. |
1144
1147
  | `onPrefetched` | `(() => void) \| undefined` | | | Lifecycle hook invoked by Inertia when a prefetch for this link has completed and is cached. |
@@ -1161,7 +1164,7 @@ Anti-patterns:
1161
1164
  | `to` | `string \| wt \| bt \| undefined` | | | Route Location the link should navigate to when clicked on. |
1162
1165
  | `trueValue` | `any` | `true` | | Value emitted when the toggle is activated. |
1163
1166
  | `type` | `"button" \| "submit" \| "reset" \| undefined` | | | Gets the classification and default behavior of the button. |
1164
- | `variant` | `'outline'` \| `'primary'` \| `'destructive'` \| `'secondary'` \| `'none'` \| `'ghost'` \| `'link'` | `"primary"` | | Visual variant of the button. Controls colors and surface style while keeping spacing and sizing unchanged. `'none'` is a blank starting point: the button keeps its structure and behaviour but takes no look, so your own classes don't fight a built-in one. You then own hover, focus and disabled. Fine for a one-off; keep inline styling short, and register a real variant when the look recurs: the plugin's `buttonVariants` option types the name, and you style `.bb-button--<name>` (the library ships no CSS for it). |
1167
+ | `variant` | `'outline'` \| `'primary'` \| `'destructive'` \| `'secondary'` \| `'none'` \| `'ghost'` \| `'link'` | `"primary"` | | Visual variant of the button: colours and surface only, never spacing or size. `'none'` takes no look (you own hover, focus and disabled); register a real one with the plugin's `buttonVariants` and style `.bb-button--<name>` (no CSS ships for it). |
1165
1168
  | `viewTransition` | `boolean \| undefined` | `false` | | Inertia: run the page swap inside a View Transition, where the browser supports one. |
1166
1169
 
1167
1170
  ## Passthrough parts and states
@@ -235,7 +235,9 @@ plus seconds with `seconds` and an AM/PM column with `ampm`; `step` sets the
235
235
  minute granularity. With `floating` the day stays plain and the time travels
236
236
  only through `v-model:time` (`HH:mm` / `HH:mm:ss`); otherwise the time lives
237
237
  inside `modelValue` and `time` mirrors it. In `range` + `datetime`
238
- `v-model:active-segment` says which end the rail edits.
238
+ `v-model:active-segment` says which end the rail edits. A `min` / `max` with
239
+ a time (`YYYY-MM-DDTHH:mm`) disables the days outside it and, on its own day,
240
+ the times before `min` / after `max` on the rail.
239
241
 
240
242
  The calendar takes its natural width (grid plus rail, about 335px; wider with
241
243
  seconds or AM/PM) or the room it is given, whichever is less. When its own box
@@ -594,16 +596,16 @@ the page is `BbDatePicker`.
594
596
  | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `the locale's week start` | | First day of the week (0 = Sunday … 6 = Saturday). Omitted, the active locale's week start: Monday for `it`, `de`, `fr`, `en-gb`…, Sunday for bare `en` (US) and `pt` (Brazil). |
595
597
  | `floating` | `boolean \| undefined` | `false` | | Emit plain calendar strings instead of zoned ISO instants. Implied (and forced) by `type="month"` and `type="year"`. |
596
598
  | `max` | `string \| undefined` | | | Maximum selectable value, in this `type`'s shape. A finer bound is accepted and ceiled to the unit, with a warning. With `datetime`, on its own day the time rail disables the times after it. |
597
- | `min` | `string \| undefined` | | | Minimum selectable value, in this `type`'s shape — `YYYY-MM-DD` (`YYYY-MM-DDTHH:mm` with `datetime`, `YYYY-MM` with `month`, `YYYY` with `year`). A finer bound is accepted and floored to the unit, with a warning. With `datetime`, the grid disables the days before it and, on its own day, the time rail disables the times before it. |
598
- | `modelValue` | `string \| string[] \| null` | | yes | v-model value. `null` for empty, a single string for single selection, or a two-element `[start, end]` array when `range` is true (a sorted array under `multiple`). The string shape follows `type`: `YYYY-MM-DD` when `floating` (`YYYY-MM` for `month`, `YYYY` for `year`), otherwise a zoned ISO instant carrying the time inline with `type="datetime"`. |
599
- | `multiple` | `boolean \| undefined` | `false` | | Enables multi-selection: every pick toggles a value in or out of a sorted array. Works in whatever `type` selects — days, months or years — and requires an array `modelValue` (use `[]` for empty, never `null`). Mutually exclusive with `range` (two different selection models) and with `type="datetime"` (the time rail edits one value; with several there is no one it could mean). Both combinations throw. |
599
+ | `min` | `string \| undefined` | | | Minimum selectable value in this `type`'s shape (`YYYY-MM-DD`; `YYYY-MM-DDTHH:mm` with `datetime`, `YYYY-MM` with `month`, `YYYY` with `year`). A finer bound is floored to the unit, with a warning. With `datetime` the rail also disables the earlier times on its day. |
600
+ | `modelValue` | `string \| string[] \| null` | | yes | v-model: `null` when empty, a string, a `[start, end]` pair with `range`, or a sorted array with `multiple`. The string is `YYYY-MM-DD` when `floating` (`YYYY-MM` / `YYYY` for `month` / `year`), otherwise a zoned ISO instant (carrying the time under `datetime`). |
601
+ | `multiple` | `boolean \| undefined` | `false` | | Multi-selection: each pick toggles a value in a sorted array (days, months or years). Needs an array `modelValue` (`[]` when empty, never `null`). Throws with `range` or `type="datetime"`. |
600
602
  | `range` | `boolean \| undefined` | `false` | | Enables range selection (`[start, end]`). |
601
603
  | `readonly` | `boolean \| undefined` | `false` | | Shows the calendar and lets the user page through it, but no pick changes the value. |
602
604
  | `seconds` | `boolean \| undefined` | `false` | | Adds the seconds column (requires `type="datetime"`). |
603
605
  | `selectable` | `((date: string) => boolean) \| undefined` | | | Predicate invoked per candidate; return `false` to disable it. Receives the value in this `type`'s shape — `YYYY-MM` under `month`, `YYYY` under `year`. |
604
606
  | `step` | `number \| undefined` | `1` | | Minute-column granularity and arrow stepping (requires `type="datetime"`). |
605
607
  | `time` | `string \| string[] \| null \| undefined` | | | Companion time model (`v-model:time`), 24-hour `HH:mm` / `HH:mm:ss`. With `floating` it is the only carrier of the time; otherwise it mirrors the time inside `modelValue`. Requires `type="datetime"`. |
606
- | `type` | `'date'` \| `'month'` \| `'year'` \| `'datetime'` | `"date"` | | What the calendar selects, and therefore what it emits. - `date` (default) — a day. - `datetime` — a day plus a time rail beside the grid. Non-floating values carry the time inside `modelValue`; with `floating` the time travels only through `time`. - `month` — a month grid emitting `YYYY-MM`. - `year` — a year list emitting `YYYY`. `month`/`year` are calendar labels, not instants: they force `floating` and ignore `utc`, and the time props (`seconds`, `ampm`, `step`, `time`) are inert outside `datetime`. |
608
+ | `type` | `'date'` \| `'month'` \| `'year'` \| `'datetime'` | `"date"` | | What the calendar selects and emits: `date` (a day), `datetime` (a day plus a time rail), `month` (`YYYY-MM`) or `year` (`YYYY`). `month` / `year` force `floating`, ignore `utc`, and leave the time props inert. |
607
609
  | `utc` | `boolean \| undefined` | `false` | | Emit UTC (`...Z`) instead of the local offset. Ignored with `floating`, and therefore with `type="month"`/`type="year"`. |
608
610
 
609
611
  ## Passthrough parts and states