@dashforge/tw 1.7.0 → 2.0.0

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 (392) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/dist/{src/components → components}/Accordion/accordion.variants.d.ts +3 -3
  3. package/dist/{src/components → components}/AppShell/appShell.types.d.ts +8 -0
  4. package/dist/{src/components → components}/AppShell/appShell.variants.d.ts +39 -2
  5. package/dist/{src/components → components}/Box/box.types.d.ts +19 -1
  6. package/dist/{src/components → components}/Box/box.variants.d.ts +18 -0
  7. package/dist/{src/components → components}/Button/button.types.d.ts +21 -0
  8. package/dist/{src/components → components}/Button/button.variants.d.ts +60 -0
  9. package/dist/{src/components → components}/Calendar/calendar.types.d.ts +52 -2
  10. package/dist/{src/components → components}/Chip/chip.types.d.ts +42 -0
  11. package/dist/{src/components → components}/Chip/chip.variants.d.ts +9 -0
  12. package/dist/{src/components → components}/Grid/grid.types.d.ts +42 -0
  13. package/dist/components/Kbd/Kbd.d.ts +13 -0
  14. package/dist/components/Kbd/kbd.types.d.ts +62 -0
  15. package/dist/components/Kbd/kbd.variants.d.ts +54 -0
  16. package/dist/{src/components → components}/Select/Select.d.ts +1 -1
  17. package/dist/{src/components → components}/Slider/Slider.d.ts +1 -1
  18. package/dist/{src/components → components}/Stack/stack.types.d.ts +42 -0
  19. package/dist/{src/components → components}/Stack/stack.variants.d.ts +8 -0
  20. package/dist/{src/components → components}/TopBar/TopBar.d.ts +16 -1
  21. package/dist/components/TopBar/topBar.types.d.ts +142 -0
  22. package/dist/components/TopBar/topBar.variants.d.ts +136 -0
  23. package/dist/components/_shared/resolveValidationState.d.ts +52 -0
  24. package/dist/index.d.ts +212 -1
  25. package/dist/index.esm.js +845 -124
  26. package/package.json +10 -8
  27. package/dist/src/components/Accordion/Accordion.d.ts.map +0 -1
  28. package/dist/src/components/Accordion/accordion.types.d.ts.map +0 -1
  29. package/dist/src/components/Accordion/accordion.variants.d.ts.map +0 -1
  30. package/dist/src/components/Alert/Alert.d.ts.map +0 -1
  31. package/dist/src/components/Alert/alert.types.d.ts.map +0 -1
  32. package/dist/src/components/Alert/alert.variants.d.ts.map +0 -1
  33. package/dist/src/components/AppShell/AppShell.d.ts.map +0 -1
  34. package/dist/src/components/AppShell/appShell.types.d.ts.map +0 -1
  35. package/dist/src/components/AppShell/appShell.variants.d.ts.map +0 -1
  36. package/dist/src/components/AspectRatio/AspectRatio.d.ts.map +0 -1
  37. package/dist/src/components/AspectRatio/aspectRatio.types.d.ts.map +0 -1
  38. package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +0 -1
  39. package/dist/src/components/Autocomplete/autocomplete.props.type-test.d.ts.map +0 -1
  40. package/dist/src/components/Autocomplete/autocomplete.types.d.ts.map +0 -1
  41. package/dist/src/components/Autocomplete/autocomplete.variants.d.ts.map +0 -1
  42. package/dist/src/components/Avatar/Avatar.d.ts.map +0 -1
  43. package/dist/src/components/Avatar/avatar.types.d.ts.map +0 -1
  44. package/dist/src/components/Avatar/avatar.variants.d.ts.map +0 -1
  45. package/dist/src/components/Badge/Badge.d.ts.map +0 -1
  46. package/dist/src/components/Badge/badge.types.d.ts.map +0 -1
  47. package/dist/src/components/Badge/badge.variants.d.ts.map +0 -1
  48. package/dist/src/components/Box/Box.d.ts.map +0 -1
  49. package/dist/src/components/Box/box.types.d.ts.map +0 -1
  50. package/dist/src/components/Box/box.variants.d.ts.map +0 -1
  51. package/dist/src/components/Breadcrumbs/Breadcrumbs.d.ts.map +0 -1
  52. package/dist/src/components/Breadcrumbs/breadcrumbs.types.d.ts.map +0 -1
  53. package/dist/src/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +0 -1
  54. package/dist/src/components/Button/Button.d.ts.map +0 -1
  55. package/dist/src/components/Button/button.types.d.ts.map +0 -1
  56. package/dist/src/components/Button/button.variants.d.ts.map +0 -1
  57. package/dist/src/components/Calendar/Calendar.d.ts.map +0 -1
  58. package/dist/src/components/Calendar/calendar.types.d.ts.map +0 -1
  59. package/dist/src/components/Calendar/calendar.variants.d.ts.map +0 -1
  60. package/dist/src/components/Card/Card.d.ts.map +0 -1
  61. package/dist/src/components/Card/card.types.d.ts.map +0 -1
  62. package/dist/src/components/Checkbox/Checkbox.d.ts.map +0 -1
  63. package/dist/src/components/Checkbox/checkbox.types.d.ts.map +0 -1
  64. package/dist/src/components/Checkbox/checkbox.variants.d.ts.map +0 -1
  65. package/dist/src/components/Chip/Chip.d.ts.map +0 -1
  66. package/dist/src/components/Chip/chip.types.d.ts.map +0 -1
  67. package/dist/src/components/Chip/chip.variants.d.ts.map +0 -1
  68. package/dist/src/components/ConfirmDialog/ConfirmDialog.d.ts.map +0 -1
  69. package/dist/src/components/ConfirmDialog/confirmDialog.types.d.ts.map +0 -1
  70. package/dist/src/components/ConfirmDialog/confirmDialog.variants.d.ts.map +0 -1
  71. package/dist/src/components/Container/Container.d.ts.map +0 -1
  72. package/dist/src/components/Container/container.types.d.ts.map +0 -1
  73. package/dist/src/components/Container/container.variants.d.ts.map +0 -1
  74. package/dist/src/components/DataGrid/DataGrid.d.ts.map +0 -1
  75. package/dist/src/components/DataGrid/dataGrid.types.d.ts.map +0 -1
  76. package/dist/src/components/DataGrid/dataGrid.variants.d.ts.map +0 -1
  77. package/dist/src/components/DataGrid/filters/ColumnFilters.d.ts.map +0 -1
  78. package/dist/src/components/DataGrid/reorder/useColumnReorder.d.ts.map +0 -1
  79. package/dist/src/components/DataGrid/resize/useColumnResize.d.ts.map +0 -1
  80. package/dist/src/components/DataGrid/visibility/ColumnVisibilityMenu.d.ts.map +0 -1
  81. package/dist/src/components/DatePicker/DatePicker.d.ts.map +0 -1
  82. package/dist/src/components/DatePicker/datePicker.types.d.ts.map +0 -1
  83. package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +0 -1
  84. package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +0 -1
  85. package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +0 -1
  86. package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts.map +0 -1
  87. package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  88. package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +0 -1
  89. package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +0 -1
  90. package/dist/src/components/Dialog/Dialog.d.ts.map +0 -1
  91. package/dist/src/components/Dialog/dialog.types.d.ts.map +0 -1
  92. package/dist/src/components/Dialog/dialog.variants.d.ts.map +0 -1
  93. package/dist/src/components/Divider/Divider.d.ts.map +0 -1
  94. package/dist/src/components/Divider/divider.types.d.ts.map +0 -1
  95. package/dist/src/components/Divider/divider.variants.d.ts.map +0 -1
  96. package/dist/src/components/Drawer/Drawer.d.ts.map +0 -1
  97. package/dist/src/components/Drawer/drawer.types.d.ts.map +0 -1
  98. package/dist/src/components/Drawer/drawer.variants.d.ts.map +0 -1
  99. package/dist/src/components/Grid/Grid.d.ts.map +0 -1
  100. package/dist/src/components/Grid/grid.types.d.ts.map +0 -1
  101. package/dist/src/components/Grid/grid.variants.d.ts.map +0 -1
  102. package/dist/src/components/IconButton/IconButton.d.ts.map +0 -1
  103. package/dist/src/components/IconButton/iconButton.types.d.ts.map +0 -1
  104. package/dist/src/components/IconButton/iconButton.variants.d.ts.map +0 -1
  105. package/dist/src/components/Image/Image.d.ts.map +0 -1
  106. package/dist/src/components/Image/image.types.d.ts.map +0 -1
  107. package/dist/src/components/Image/image.variants.d.ts.map +0 -1
  108. package/dist/src/components/LeftNav/LeftNav.d.ts.map +0 -1
  109. package/dist/src/components/LeftNav/leftNav.types.d.ts.map +0 -1
  110. package/dist/src/components/LeftNav/leftNav.variants.d.ts.map +0 -1
  111. package/dist/src/components/Link/Link.d.ts.map +0 -1
  112. package/dist/src/components/Link/link.types.d.ts.map +0 -1
  113. package/dist/src/components/Link/link.variants.d.ts.map +0 -1
  114. package/dist/src/components/Menu/Menu.d.ts.map +0 -1
  115. package/dist/src/components/Menu/menu.types.d.ts.map +0 -1
  116. package/dist/src/components/Menu/menu.variants.d.ts.map +0 -1
  117. package/dist/src/components/NumberField/NumberField.d.ts.map +0 -1
  118. package/dist/src/components/NumberField/numberField.types.d.ts.map +0 -1
  119. package/dist/src/components/NumberField/numberField.variants.d.ts.map +0 -1
  120. package/dist/src/components/OTPField/OTPField.d.ts.map +0 -1
  121. package/dist/src/components/OTPField/otpField.types.d.ts.map +0 -1
  122. package/dist/src/components/OTPField/otpField.variants.d.ts.map +0 -1
  123. package/dist/src/components/Pagination/Pagination.d.ts.map +0 -1
  124. package/dist/src/components/Pagination/pagination.helpers.d.ts.map +0 -1
  125. package/dist/src/components/Pagination/pagination.types.d.ts.map +0 -1
  126. package/dist/src/components/Pagination/pagination.variants.d.ts.map +0 -1
  127. package/dist/src/components/Popover/Popover.d.ts.map +0 -1
  128. package/dist/src/components/Popover/popover.types.d.ts.map +0 -1
  129. package/dist/src/components/Popover/popover.variants.d.ts.map +0 -1
  130. package/dist/src/components/Progress/Progress.d.ts.map +0 -1
  131. package/dist/src/components/Progress/progress.types.d.ts.map +0 -1
  132. package/dist/src/components/Progress/progress.variants.d.ts.map +0 -1
  133. package/dist/src/components/RadioGroup/RadioGroup.d.ts.map +0 -1
  134. package/dist/src/components/RadioGroup/radioGroup.types.d.ts.map +0 -1
  135. package/dist/src/components/RadioGroup/radioGroup.variants.d.ts.map +0 -1
  136. package/dist/src/components/Select/Select.d.ts.map +0 -1
  137. package/dist/src/components/Select/select.types.d.ts.map +0 -1
  138. package/dist/src/components/Select/select.variants.d.ts.map +0 -1
  139. package/dist/src/components/Skeleton/Skeleton.d.ts.map +0 -1
  140. package/dist/src/components/Skeleton/skeleton.types.d.ts.map +0 -1
  141. package/dist/src/components/Skeleton/skeleton.variants.d.ts.map +0 -1
  142. package/dist/src/components/Slider/Slider.d.ts.map +0 -1
  143. package/dist/src/components/Slider/slider.types.d.ts.map +0 -1
  144. package/dist/src/components/Slider/slider.variants.d.ts.map +0 -1
  145. package/dist/src/components/Snackbar/Snackbar.d.ts.map +0 -1
  146. package/dist/src/components/Snackbar/snackbar.types.d.ts.map +0 -1
  147. package/dist/src/components/Snackbar/snackbar.variants.d.ts.map +0 -1
  148. package/dist/src/components/Spinner/Spinner.d.ts.map +0 -1
  149. package/dist/src/components/Spinner/spinner.types.d.ts.map +0 -1
  150. package/dist/src/components/Spinner/spinner.variants.d.ts.map +0 -1
  151. package/dist/src/components/Stack/Stack.d.ts.map +0 -1
  152. package/dist/src/components/Stack/stack.types.d.ts.map +0 -1
  153. package/dist/src/components/Stack/stack.variants.d.ts.map +0 -1
  154. package/dist/src/components/Stepper/Step.d.ts.map +0 -1
  155. package/dist/src/components/Stepper/Stepper.d.ts.map +0 -1
  156. package/dist/src/components/Stepper/StepperContext.d.ts.map +0 -1
  157. package/dist/src/components/Stepper/stepper.types.d.ts.map +0 -1
  158. package/dist/src/components/Stepper/stepper.variants.d.ts.map +0 -1
  159. package/dist/src/components/Stepper/useStep.d.ts.map +0 -1
  160. package/dist/src/components/Switch/Switch.d.ts.map +0 -1
  161. package/dist/src/components/Switch/switch.types.d.ts.map +0 -1
  162. package/dist/src/components/Switch/switch.variants.d.ts.map +0 -1
  163. package/dist/src/components/Table/Table.d.ts.map +0 -1
  164. package/dist/src/components/Table/cells/RenderButton.d.ts.map +0 -1
  165. package/dist/src/components/Table/cells/RenderChip.d.ts.map +0 -1
  166. package/dist/src/components/Table/cells/RenderText.d.ts.map +0 -1
  167. package/dist/src/components/Table/cells/RenderTwoLine.d.ts.map +0 -1
  168. package/dist/src/components/Table/cells/RowActionsMenu.d.ts.map +0 -1
  169. package/dist/src/components/Table/cells/index.d.ts.map +0 -1
  170. package/dist/src/components/Table/table.types.d.ts.map +0 -1
  171. package/dist/src/components/Table/table.variants.d.ts.map +0 -1
  172. package/dist/src/components/Tabs/Tabs.d.ts.map +0 -1
  173. package/dist/src/components/Tabs/tabs.types.d.ts.map +0 -1
  174. package/dist/src/components/Tabs/tabs.variants.d.ts.map +0 -1
  175. package/dist/src/components/Tabs/useTabs.d.ts.map +0 -1
  176. package/dist/src/components/TextField/TextField.d.ts.map +0 -1
  177. package/dist/src/components/TextField/textField.types.d.ts.map +0 -1
  178. package/dist/src/components/TextField/textField.variants.d.ts.map +0 -1
  179. package/dist/src/components/Textarea/Textarea.d.ts.map +0 -1
  180. package/dist/src/components/Textarea/textarea.types.d.ts.map +0 -1
  181. package/dist/src/components/Textarea/textarea.variants.d.ts.map +0 -1
  182. package/dist/src/components/TimePicker/TimePicker.d.ts.map +0 -1
  183. package/dist/src/components/TimePicker/timePicker.types.d.ts.map +0 -1
  184. package/dist/src/components/TimePicker/timePicker.variants.d.ts.map +0 -1
  185. package/dist/src/components/Tooltip/Tooltip.d.ts.map +0 -1
  186. package/dist/src/components/Tooltip/tooltip.types.d.ts.map +0 -1
  187. package/dist/src/components/Tooltip/tooltip.variants.d.ts.map +0 -1
  188. package/dist/src/components/TopBar/TopBar.d.ts.map +0 -1
  189. package/dist/src/components/TopBar/topBar.types.d.ts +0 -73
  190. package/dist/src/components/TopBar/topBar.types.d.ts.map +0 -1
  191. package/dist/src/components/TopBar/topBar.variants.d.ts +0 -79
  192. package/dist/src/components/TopBar/topBar.variants.d.ts.map +0 -1
  193. package/dist/src/components/Typography/Typography.d.ts.map +0 -1
  194. package/dist/src/components/Typography/typography.types.d.ts.map +0 -1
  195. package/dist/src/components/Typography/typography.variants.d.ts.map +0 -1
  196. package/dist/src/components/Video/Video.d.ts.map +0 -1
  197. package/dist/src/components/Video/video.types.d.ts.map +0 -1
  198. package/dist/src/components/Video/video.variants.d.ts.map +0 -1
  199. package/dist/src/components/VisuallyHidden/VisuallyHidden.d.ts.map +0 -1
  200. package/dist/src/components/VisuallyHidden/visuallyHidden.types.d.ts.map +0 -1
  201. package/dist/src/components/_shared/data/getNestedValue.d.ts.map +0 -1
  202. package/dist/src/components/_shared/data/useColumnAutoDetect.d.ts.map +0 -1
  203. package/dist/src/components/_shared/data/useControllableState.d.ts.map +0 -1
  204. package/dist/src/components/_shared/data/useDebouncedValue.d.ts.map +0 -1
  205. package/dist/src/components/_shared/data/useTableFilter.d.ts.map +0 -1
  206. package/dist/src/components/_shared/data/useTableSearch.d.ts.map +0 -1
  207. package/dist/src/components/_shared/data/useTableSelection.d.ts.map +0 -1
  208. package/dist/src/components/_shared/data/useTableSort.d.ts.map +0 -1
  209. package/dist/src/components/_shared/data/useVirtualizer.d.ts.map +0 -1
  210. package/dist/src/components/_shared/fieldTooltip.d.ts.map +0 -1
  211. package/dist/src/components/_shared/resolveValidationState.d.ts +0 -42
  212. package/dist/src/components/_shared/resolveValidationState.d.ts.map +0 -1
  213. package/dist/src/components/_shared/severity/index.d.ts.map +0 -1
  214. package/dist/src/components/_shared/severity/severity.types.d.ts.map +0 -1
  215. package/dist/src/components/_shared/severity/severityIcons.d.ts.map +0 -1
  216. package/dist/src/components/_shared/severity/severityVariants.d.ts.map +0 -1
  217. package/dist/src/hooks/useAccessState.d.ts.map +0 -1
  218. package/dist/src/hooks/useStandaloneFieldWarning.d.ts.map +0 -1
  219. package/dist/src/index.d.ts +0 -199
  220. package/dist/src/index.d.ts.map +0 -1
  221. package/dist/src/utils/cn.d.ts.map +0 -1
  222. /package/dist/{src/components → components}/Accordion/Accordion.d.ts +0 -0
  223. /package/dist/{src/components → components}/Accordion/accordion.types.d.ts +0 -0
  224. /package/dist/{src/components → components}/Alert/Alert.d.ts +0 -0
  225. /package/dist/{src/components → components}/Alert/alert.types.d.ts +0 -0
  226. /package/dist/{src/components → components}/Alert/alert.variants.d.ts +0 -0
  227. /package/dist/{src/components → components}/AppShell/AppShell.d.ts +0 -0
  228. /package/dist/{src/components → components}/AspectRatio/AspectRatio.d.ts +0 -0
  229. /package/dist/{src/components → components}/AspectRatio/aspectRatio.types.d.ts +0 -0
  230. /package/dist/{src/components → components}/Autocomplete/Autocomplete.d.ts +0 -0
  231. /package/dist/{src/components → components}/Autocomplete/autocomplete.props.type-test.d.ts +0 -0
  232. /package/dist/{src/components → components}/Autocomplete/autocomplete.types.d.ts +0 -0
  233. /package/dist/{src/components → components}/Autocomplete/autocomplete.variants.d.ts +0 -0
  234. /package/dist/{src/components → components}/Avatar/Avatar.d.ts +0 -0
  235. /package/dist/{src/components → components}/Avatar/avatar.types.d.ts +0 -0
  236. /package/dist/{src/components → components}/Avatar/avatar.variants.d.ts +0 -0
  237. /package/dist/{src/components → components}/Badge/Badge.d.ts +0 -0
  238. /package/dist/{src/components → components}/Badge/badge.types.d.ts +0 -0
  239. /package/dist/{src/components → components}/Badge/badge.variants.d.ts +0 -0
  240. /package/dist/{src/components → components}/Box/Box.d.ts +0 -0
  241. /package/dist/{src/components → components}/Breadcrumbs/Breadcrumbs.d.ts +0 -0
  242. /package/dist/{src/components → components}/Breadcrumbs/breadcrumbs.types.d.ts +0 -0
  243. /package/dist/{src/components → components}/Breadcrumbs/breadcrumbs.variants.d.ts +0 -0
  244. /package/dist/{src/components → components}/Button/Button.d.ts +0 -0
  245. /package/dist/{src/components → components}/Calendar/Calendar.d.ts +0 -0
  246. /package/dist/{src/components → components}/Calendar/calendar.variants.d.ts +0 -0
  247. /package/dist/{src/components → components}/Card/Card.d.ts +0 -0
  248. /package/dist/{src/components → components}/Card/card.types.d.ts +0 -0
  249. /package/dist/{src/components → components}/Checkbox/Checkbox.d.ts +0 -0
  250. /package/dist/{src/components → components}/Checkbox/checkbox.types.d.ts +0 -0
  251. /package/dist/{src/components → components}/Checkbox/checkbox.variants.d.ts +0 -0
  252. /package/dist/{src/components → components}/Chip/Chip.d.ts +0 -0
  253. /package/dist/{src/components → components}/ConfirmDialog/ConfirmDialog.d.ts +0 -0
  254. /package/dist/{src/components → components}/ConfirmDialog/confirmDialog.types.d.ts +0 -0
  255. /package/dist/{src/components → components}/ConfirmDialog/confirmDialog.variants.d.ts +0 -0
  256. /package/dist/{src/components → components}/Container/Container.d.ts +0 -0
  257. /package/dist/{src/components → components}/Container/container.types.d.ts +0 -0
  258. /package/dist/{src/components → components}/Container/container.variants.d.ts +0 -0
  259. /package/dist/{src/components → components}/DataGrid/DataGrid.d.ts +0 -0
  260. /package/dist/{src/components → components}/DataGrid/dataGrid.types.d.ts +0 -0
  261. /package/dist/{src/components → components}/DataGrid/dataGrid.variants.d.ts +0 -0
  262. /package/dist/{src/components → components}/DataGrid/filters/ColumnFilters.d.ts +0 -0
  263. /package/dist/{src/components → components}/DataGrid/reorder/useColumnReorder.d.ts +0 -0
  264. /package/dist/{src/components → components}/DataGrid/resize/useColumnResize.d.ts +0 -0
  265. /package/dist/{src/components → components}/DataGrid/visibility/ColumnVisibilityMenu.d.ts +0 -0
  266. /package/dist/{src/components → components}/DatePicker/DatePicker.d.ts +0 -0
  267. /package/dist/{src/components → components}/DatePicker/datePicker.types.d.ts +0 -0
  268. /package/dist/{src/components → components}/DatePicker/datePicker.variants.d.ts +0 -0
  269. /package/dist/{src/components → components}/DateRangePicker/DateRangePicker.d.ts +0 -0
  270. /package/dist/{src/components → components}/DateRangePicker/dateRangePicker.types.d.ts +0 -0
  271. /package/dist/{src/components → components}/DateRangePicker/dateRangePicker.variants.d.ts +0 -0
  272. /package/dist/{src/components → components}/DateTimePicker/DateTimePicker.d.ts +0 -0
  273. /package/dist/{src/components → components}/DateTimePicker/dateTimePicker.types.d.ts +0 -0
  274. /package/dist/{src/components → components}/DateTimePicker/dateTimePicker.variants.d.ts +0 -0
  275. /package/dist/{src/components → components}/Dialog/Dialog.d.ts +0 -0
  276. /package/dist/{src/components → components}/Dialog/dialog.types.d.ts +0 -0
  277. /package/dist/{src/components → components}/Dialog/dialog.variants.d.ts +0 -0
  278. /package/dist/{src/components → components}/Divider/Divider.d.ts +0 -0
  279. /package/dist/{src/components → components}/Divider/divider.types.d.ts +0 -0
  280. /package/dist/{src/components → components}/Divider/divider.variants.d.ts +0 -0
  281. /package/dist/{src/components → components}/Drawer/Drawer.d.ts +0 -0
  282. /package/dist/{src/components → components}/Drawer/drawer.types.d.ts +0 -0
  283. /package/dist/{src/components → components}/Drawer/drawer.variants.d.ts +0 -0
  284. /package/dist/{src/components → components}/Grid/Grid.d.ts +0 -0
  285. /package/dist/{src/components → components}/Grid/grid.variants.d.ts +0 -0
  286. /package/dist/{src/components → components}/IconButton/IconButton.d.ts +0 -0
  287. /package/dist/{src/components → components}/IconButton/iconButton.types.d.ts +0 -0
  288. /package/dist/{src/components → components}/IconButton/iconButton.variants.d.ts +0 -0
  289. /package/dist/{src/components → components}/Image/Image.d.ts +0 -0
  290. /package/dist/{src/components → components}/Image/image.types.d.ts +0 -0
  291. /package/dist/{src/components → components}/Image/image.variants.d.ts +0 -0
  292. /package/dist/{src/components → components}/LeftNav/LeftNav.d.ts +0 -0
  293. /package/dist/{src/components → components}/LeftNav/leftNav.types.d.ts +0 -0
  294. /package/dist/{src/components → components}/LeftNav/leftNav.variants.d.ts +0 -0
  295. /package/dist/{src/components → components}/Link/Link.d.ts +0 -0
  296. /package/dist/{src/components → components}/Link/link.types.d.ts +0 -0
  297. /package/dist/{src/components → components}/Link/link.variants.d.ts +0 -0
  298. /package/dist/{src/components → components}/Menu/Menu.d.ts +0 -0
  299. /package/dist/{src/components → components}/Menu/menu.types.d.ts +0 -0
  300. /package/dist/{src/components → components}/Menu/menu.variants.d.ts +0 -0
  301. /package/dist/{src/components → components}/NumberField/NumberField.d.ts +0 -0
  302. /package/dist/{src/components → components}/NumberField/numberField.types.d.ts +0 -0
  303. /package/dist/{src/components → components}/NumberField/numberField.variants.d.ts +0 -0
  304. /package/dist/{src/components → components}/OTPField/OTPField.d.ts +0 -0
  305. /package/dist/{src/components → components}/OTPField/otpField.types.d.ts +0 -0
  306. /package/dist/{src/components → components}/OTPField/otpField.variants.d.ts +0 -0
  307. /package/dist/{src/components → components}/Pagination/Pagination.d.ts +0 -0
  308. /package/dist/{src/components → components}/Pagination/pagination.helpers.d.ts +0 -0
  309. /package/dist/{src/components → components}/Pagination/pagination.types.d.ts +0 -0
  310. /package/dist/{src/components → components}/Pagination/pagination.variants.d.ts +0 -0
  311. /package/dist/{src/components → components}/Popover/Popover.d.ts +0 -0
  312. /package/dist/{src/components → components}/Popover/popover.types.d.ts +0 -0
  313. /package/dist/{src/components → components}/Popover/popover.variants.d.ts +0 -0
  314. /package/dist/{src/components → components}/Progress/Progress.d.ts +0 -0
  315. /package/dist/{src/components → components}/Progress/progress.types.d.ts +0 -0
  316. /package/dist/{src/components → components}/Progress/progress.variants.d.ts +0 -0
  317. /package/dist/{src/components → components}/RadioGroup/RadioGroup.d.ts +0 -0
  318. /package/dist/{src/components → components}/RadioGroup/radioGroup.types.d.ts +0 -0
  319. /package/dist/{src/components → components}/RadioGroup/radioGroup.variants.d.ts +0 -0
  320. /package/dist/{src/components → components}/Select/select.types.d.ts +0 -0
  321. /package/dist/{src/components → components}/Select/select.variants.d.ts +0 -0
  322. /package/dist/{src/components → components}/Skeleton/Skeleton.d.ts +0 -0
  323. /package/dist/{src/components → components}/Skeleton/skeleton.types.d.ts +0 -0
  324. /package/dist/{src/components → components}/Skeleton/skeleton.variants.d.ts +0 -0
  325. /package/dist/{src/components → components}/Slider/slider.types.d.ts +0 -0
  326. /package/dist/{src/components → components}/Slider/slider.variants.d.ts +0 -0
  327. /package/dist/{src/components → components}/Snackbar/Snackbar.d.ts +0 -0
  328. /package/dist/{src/components → components}/Snackbar/snackbar.types.d.ts +0 -0
  329. /package/dist/{src/components → components}/Snackbar/snackbar.variants.d.ts +0 -0
  330. /package/dist/{src/components → components}/Spinner/Spinner.d.ts +0 -0
  331. /package/dist/{src/components → components}/Spinner/spinner.types.d.ts +0 -0
  332. /package/dist/{src/components → components}/Spinner/spinner.variants.d.ts +0 -0
  333. /package/dist/{src/components → components}/Stack/Stack.d.ts +0 -0
  334. /package/dist/{src/components → components}/Stepper/Step.d.ts +0 -0
  335. /package/dist/{src/components → components}/Stepper/Stepper.d.ts +0 -0
  336. /package/dist/{src/components → components}/Stepper/StepperContext.d.ts +0 -0
  337. /package/dist/{src/components → components}/Stepper/stepper.types.d.ts +0 -0
  338. /package/dist/{src/components → components}/Stepper/stepper.variants.d.ts +0 -0
  339. /package/dist/{src/components → components}/Stepper/useStep.d.ts +0 -0
  340. /package/dist/{src/components → components}/Switch/Switch.d.ts +0 -0
  341. /package/dist/{src/components → components}/Switch/switch.types.d.ts +0 -0
  342. /package/dist/{src/components → components}/Switch/switch.variants.d.ts +0 -0
  343. /package/dist/{src/components → components}/Table/Table.d.ts +0 -0
  344. /package/dist/{src/components → components}/Table/cells/RenderButton.d.ts +0 -0
  345. /package/dist/{src/components → components}/Table/cells/RenderChip.d.ts +0 -0
  346. /package/dist/{src/components → components}/Table/cells/RenderText.d.ts +0 -0
  347. /package/dist/{src/components → components}/Table/cells/RenderTwoLine.d.ts +0 -0
  348. /package/dist/{src/components → components}/Table/cells/RowActionsMenu.d.ts +0 -0
  349. /package/dist/{src/components → components}/Table/cells/index.d.ts +0 -0
  350. /package/dist/{src/components → components}/Table/table.types.d.ts +0 -0
  351. /package/dist/{src/components → components}/Table/table.variants.d.ts +0 -0
  352. /package/dist/{src/components → components}/Tabs/Tabs.d.ts +0 -0
  353. /package/dist/{src/components → components}/Tabs/tabs.types.d.ts +0 -0
  354. /package/dist/{src/components → components}/Tabs/tabs.variants.d.ts +0 -0
  355. /package/dist/{src/components → components}/Tabs/useTabs.d.ts +0 -0
  356. /package/dist/{src/components → components}/TextField/TextField.d.ts +0 -0
  357. /package/dist/{src/components → components}/TextField/textField.types.d.ts +0 -0
  358. /package/dist/{src/components → components}/TextField/textField.variants.d.ts +0 -0
  359. /package/dist/{src/components → components}/Textarea/Textarea.d.ts +0 -0
  360. /package/dist/{src/components → components}/Textarea/textarea.types.d.ts +0 -0
  361. /package/dist/{src/components → components}/Textarea/textarea.variants.d.ts +0 -0
  362. /package/dist/{src/components → components}/TimePicker/TimePicker.d.ts +0 -0
  363. /package/dist/{src/components → components}/TimePicker/timePicker.types.d.ts +0 -0
  364. /package/dist/{src/components → components}/TimePicker/timePicker.variants.d.ts +0 -0
  365. /package/dist/{src/components → components}/Tooltip/Tooltip.d.ts +0 -0
  366. /package/dist/{src/components → components}/Tooltip/tooltip.types.d.ts +0 -0
  367. /package/dist/{src/components → components}/Tooltip/tooltip.variants.d.ts +0 -0
  368. /package/dist/{src/components → components}/Typography/Typography.d.ts +0 -0
  369. /package/dist/{src/components → components}/Typography/typography.types.d.ts +0 -0
  370. /package/dist/{src/components → components}/Typography/typography.variants.d.ts +0 -0
  371. /package/dist/{src/components → components}/Video/Video.d.ts +0 -0
  372. /package/dist/{src/components → components}/Video/video.types.d.ts +0 -0
  373. /package/dist/{src/components → components}/Video/video.variants.d.ts +0 -0
  374. /package/dist/{src/components → components}/VisuallyHidden/VisuallyHidden.d.ts +0 -0
  375. /package/dist/{src/components → components}/VisuallyHidden/visuallyHidden.types.d.ts +0 -0
  376. /package/dist/{src/components → components}/_shared/data/getNestedValue.d.ts +0 -0
  377. /package/dist/{src/components → components}/_shared/data/useColumnAutoDetect.d.ts +0 -0
  378. /package/dist/{src/components → components}/_shared/data/useControllableState.d.ts +0 -0
  379. /package/dist/{src/components → components}/_shared/data/useDebouncedValue.d.ts +0 -0
  380. /package/dist/{src/components → components}/_shared/data/useTableFilter.d.ts +0 -0
  381. /package/dist/{src/components → components}/_shared/data/useTableSearch.d.ts +0 -0
  382. /package/dist/{src/components → components}/_shared/data/useTableSelection.d.ts +0 -0
  383. /package/dist/{src/components → components}/_shared/data/useTableSort.d.ts +0 -0
  384. /package/dist/{src/components → components}/_shared/data/useVirtualizer.d.ts +0 -0
  385. /package/dist/{src/components → components}/_shared/fieldTooltip.d.ts +0 -0
  386. /package/dist/{src/components → components}/_shared/severity/index.d.ts +0 -0
  387. /package/dist/{src/components → components}/_shared/severity/severity.types.d.ts +0 -0
  388. /package/dist/{src/components → components}/_shared/severity/severityIcons.d.ts +0 -0
  389. /package/dist/{src/components → components}/_shared/severity/severityVariants.d.ts +0 -0
  390. /package/dist/{src/hooks → hooks}/useAccessState.d.ts +0 -0
  391. /package/dist/{src/hooks → hooks}/useStandaloneFieldWarning.d.ts +0 -0
  392. /package/dist/{src/utils → utils}/cn.d.ts +0 -0
package/dist/index.esm.js CHANGED
@@ -373,7 +373,13 @@ Spinner.displayName = 'Spinner';
373
373
  secondary: '',
374
374
  success: '',
375
375
  warning: '',
376
- danger: ''
376
+ danger: '',
377
+ /**
378
+ * For a surface that is dark regardless of the theme: a dark hero,
379
+ * an ink footer, an inverted panel. Reads the theme-invariant
380
+ * `inverse` token role, so it does NOT flip with light/dark the way
381
+ * a `neutral`-based treatment would. See kensaadi/dashforge#140.
382
+ */ inverse: ''
377
383
  },
378
384
  size: {
379
385
  sm: 'h-8 px-3 text-sm',
@@ -385,6 +391,18 @@ Spinner.displayName = 'Spinner';
385
391
  },
386
392
  loading: {
387
393
  true: 'cursor-wait'
394
+ },
395
+ /**
396
+ * Toggle state. Styling lives in `compoundVariants` because "on" reads
397
+ * differently on a filled button than on a quiet one: the quiet
398
+ * variants gain a surface, `solid` deepens the one it already has.
399
+ *
400
+ * The treatment is not invented. It is LeftNav's `itemActive`
401
+ * (`bg-*-100 text-*-900 font-medium`), the catalog's existing way of
402
+ * saying "this is the current thing". kensaadi/dashforge#63 gap C.
403
+ */ pressed: {
404
+ true: '',
405
+ false: ''
388
406
  }
389
407
  },
390
408
  compoundVariants: [
@@ -491,6 +509,136 @@ Spinner.displayName = 'Spinner';
491
509
  variant: 'link',
492
510
  color: 'danger',
493
511
  class: 'text-danger-700 hover:text-danger-800'
512
+ },
513
+ // ───── × inverse ─────
514
+ //
515
+ // `focus-visible:ring-offset-transparent` on every row is load-bearing.
516
+ // The base sets `focus-visible:ring-offset-2`, and neither the preset
517
+ // nor the base declares a ring-offset COLOUR, so it falls through to
518
+ // Tailwind v4's default of `#fff`. On a dark surface that paints a
519
+ // white halo around the button the moment it takes keyboard focus.
520
+ {
521
+ variant: 'solid',
522
+ color: 'inverse',
523
+ class: 'bg-inverse-50 text-inverse-900 hover:bg-inverse-200 active:bg-inverse-300 ' + 'focus-visible:ring-inverse-50 focus-visible:ring-offset-transparent'
524
+ },
525
+ {
526
+ variant: 'outline',
527
+ color: 'inverse',
528
+ class: 'border-inverse-50/40 text-inverse-50 hover:bg-inverse-50/10 ' + 'focus-visible:ring-inverse-50 focus-visible:ring-offset-transparent'
529
+ },
530
+ {
531
+ variant: 'ghost',
532
+ color: 'inverse',
533
+ class: 'text-inverse-50 hover:bg-inverse-50/10 ' + 'focus-visible:ring-inverse-50 focus-visible:ring-offset-transparent'
534
+ },
535
+ {
536
+ variant: 'link',
537
+ color: 'inverse',
538
+ class: 'text-inverse-50 hover:text-inverse-200'
539
+ },
540
+ // ───── pressed × color, on the quiet variants ─────
541
+ //
542
+ // `outline` and `ghost` have no surface of their own, so an "on" toggle
543
+ // gains one. Shape borrowed from LeftNav's `itemActive` rather than
544
+ // invented, so a pressed Button and an active nav row read the same.
545
+ {
546
+ variant: [
547
+ 'outline',
548
+ 'ghost'
549
+ ],
550
+ pressed: true,
551
+ color: 'primary',
552
+ class: 'bg-primary-100 text-primary-900 font-medium hover:bg-primary-200'
553
+ },
554
+ {
555
+ variant: [
556
+ 'outline',
557
+ 'ghost'
558
+ ],
559
+ pressed: true,
560
+ color: 'secondary',
561
+ class: 'bg-secondary-100 text-secondary-900 font-medium hover:bg-secondary-200'
562
+ },
563
+ {
564
+ variant: [
565
+ 'outline',
566
+ 'ghost'
567
+ ],
568
+ pressed: true,
569
+ color: 'success',
570
+ class: 'bg-success-100 text-success-900 font-medium hover:bg-success-200'
571
+ },
572
+ {
573
+ variant: [
574
+ 'outline',
575
+ 'ghost'
576
+ ],
577
+ pressed: true,
578
+ color: 'warning',
579
+ class: 'bg-warning-100 text-warning-900 font-medium hover:bg-warning-200'
580
+ },
581
+ {
582
+ variant: [
583
+ 'outline',
584
+ 'ghost'
585
+ ],
586
+ pressed: true,
587
+ color: 'danger',
588
+ class: 'bg-danger-100 text-danger-900 font-medium hover:bg-danger-200'
589
+ },
590
+ {
591
+ // `inverse` keeps its own idiom: a wash over the dark surface rather
592
+ // than a tint from a scale that does not follow the theme. The hover
593
+ // rows above use `/10`, so "on" sits one step up at `/20`.
594
+ variant: [
595
+ 'outline',
596
+ 'ghost'
597
+ ],
598
+ pressed: true,
599
+ color: 'inverse',
600
+ class: 'bg-inverse-50/20 text-inverse-50 font-medium hover:bg-inverse-50/25 ' + 'focus-visible:ring-offset-transparent'
601
+ },
602
+ // ───── pressed × color, on solid ─────
603
+ //
604
+ // Already filled, so "on" deepens instead of tinting: it reuses each
605
+ // row's own `active:` tier, the shade that already means "being pressed
606
+ // right now".
607
+ {
608
+ variant: 'solid',
609
+ pressed: true,
610
+ color: 'primary',
611
+ class: 'bg-primary-700 hover:bg-primary-800'
612
+ },
613
+ {
614
+ variant: 'solid',
615
+ pressed: true,
616
+ color: 'secondary',
617
+ class: 'bg-secondary-700 hover:bg-secondary-800'
618
+ },
619
+ {
620
+ variant: 'solid',
621
+ pressed: true,
622
+ color: 'success',
623
+ class: 'bg-success-700 hover:bg-success-800'
624
+ },
625
+ {
626
+ variant: 'solid',
627
+ pressed: true,
628
+ color: 'warning',
629
+ class: 'bg-warning-700 hover:bg-warning-800'
630
+ },
631
+ {
632
+ variant: 'solid',
633
+ pressed: true,
634
+ color: 'danger',
635
+ class: 'bg-danger-700 hover:bg-danger-800'
636
+ },
637
+ {
638
+ variant: 'solid',
639
+ pressed: true,
640
+ color: 'inverse',
641
+ class: 'bg-inverse-300 hover:bg-inverse-400'
494
642
  }
495
643
  ],
496
644
  defaultVariants: {
@@ -563,7 +711,7 @@ Spinner.displayName = 'Spinner';
563
711
  // omit a field, TV picks its `defaultVariants` value (step 1).
564
712
  const themeDefaults = useComponentDefaults('Button');
565
713
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
566
- const { access, visibleWhen, disabled, variant, color, size, fullWidth, loading, asChild, sx, children } = merged, rest = _object_without_properties_loose(merged, [
714
+ const { access, visibleWhen, disabled, variant, color, size, fullWidth, loading, pressed, asChild, sx, children } = merged, rest = _object_without_properties_loose(merged, [
567
715
  "access",
568
716
  "visibleWhen",
569
717
  "disabled",
@@ -572,6 +720,7 @@ Spinner.displayName = 'Spinner';
572
720
  "size",
573
721
  "fullWidth",
574
722
  "loading",
723
+ "pressed",
575
724
  "asChild",
576
725
  "sx",
577
726
  "children"
@@ -596,7 +745,8 @@ Spinner.displayName = 'Spinner';
596
745
  color,
597
746
  size,
598
747
  fullWidth,
599
- loading
748
+ loading,
749
+ pressed
600
750
  }), sx);
601
751
  /*
602
752
  * `aria-busy` announces the loading state to assistive tech.
@@ -614,6 +764,7 @@ Spinner.displayName = 'Spinner';
614
764
  "data-disabled": effectiveDisabled || undefined,
615
765
  "aria-disabled": effectiveDisabled || undefined,
616
766
  "aria-busy": ariaBusy,
767
+ "aria-pressed": pressed,
617
768
  children: children
618
769
  });
619
770
  }
@@ -622,6 +773,7 @@ Spinner.displayName = 'Spinner';
622
773
  type: (_rest_type = rest.type) != null ? _rest_type : 'button',
623
774
  disabled: effectiveDisabled,
624
775
  "aria-busy": ariaBusy,
776
+ "aria-pressed": pressed,
625
777
  className: classes
626
778
  }, rest, {
627
779
  children: [
@@ -859,7 +1011,6 @@ const tooltipVariants = tv({
859
1011
  if (onChange !== undefined) return;
860
1012
  hasWarnedRef.current = true;
861
1013
  const nameSegment = typeof name === 'string' && name.length > 0 ? ` name="${name}"` : '';
862
- // eslint-disable-next-line no-console -- dev-only guard, guarded by NODE_ENV above.
863
1014
  console.warn(`[@dashforge/tw] <${componentName}${nameSegment}> is not inside a ` + `DashFormProvider and no value/onChange were provided. Did you ` + `forget the provider, or intend controlled mode? The widget will ` + `render but never emit changes upward. See the "Controlled vs ` + `bridge mode" docs.`);
864
1015
  // The check runs once on mount; the ref guards re-runs even if React
865
1016
  // Strict Mode double-invokes the effect in development.
@@ -870,23 +1021,33 @@ const tooltipVariants = tv({
870
1021
  /**
871
1022
  * Compute `error` + `helperText` for a bridge-managed field.
872
1023
  *
873
- * **Renderer-agnostic** — copy of the MUI-side
874
- * `textField.validation.ts`. Promoted to a `_shared/` directory inside
875
- * `@dashforge/tw` so all form components (TextField + Checkbox +
876
- * Switch) reuse the same precedence logic without depending on a
1024
+ * **Renderer-agnostic.** Mirrors the MUI-side
1025
+ * `ui/src/components/TextField/textField.validation.ts`. Promoted to a
1026
+ * `_shared/` directory inside `@dashforge/tw` so all 14 bridge-managed
1027
+ * form components reuse the same precedence without depending on a
877
1028
  * specific component folder.
878
1029
  *
879
- * Precedence rules (matches MUI side byte-for-byte):
880
- *
881
- * 1. **Explicit props win.** If the user passed `error` / `helperText`
882
- * explicitly, those values are used as-is.
883
- * 2. **Auto values are gated by interaction.** The bridge's error is
1030
+ * ⚠️ This file is a COPY, not an import. The two sides have to be kept
1031
+ * in step by hand, and once already were not: BUG 17 (2026-09-15)
1032
+ * inverted the `helperText` precedence on the MUI side only, and this
1033
+ * copy kept the broken order for ten days until BUG 32 caught it. The
1034
+ * verification that missed it was a grep for imports of the MUI file,
1035
+ * which by construction cannot reach a copy. **When you change the
1036
+ * precedence here, change it there too — and vice versa.**
1037
+ *
1038
+ * Precedence rules:
1039
+ *
1040
+ * 1. **`error`: the explicit prop wins.** An author who passes `error`
1041
+ * is forcing the visual state, so it overrides the auto-gate.
1042
+ * 2. **`helperText`: the validation message wins while it is showing;
1043
+ * the explicit prop is the fallback for the no-error state.** A
1044
+ * constant hint ("Unique, uppercase") must not swallow the reason a
1045
+ * required field was rejected — that leaves the user looking at a
1046
+ * red field with no explanation. See README-BUG.md § BUG 17 / § BUG 32.
1047
+ * 3. **Auto values are gated by interaction.** The bridge's error is
884
1048
  * surfaced only when the field is `touched` OR the form has been
885
1049
  * submitted at least once (`submitCount > 0`). This prevents the
886
1050
  * "error spam while typing" anti-pattern.
887
- * 3. **`error === false` explicitly clears the bridge's error message
888
- * text.** Useful for forcing a "valid" visual state regardless of
889
- * bridge state.
890
1051
  *
891
1052
  * @param name field name registered with the bridge
892
1053
  * @param bridge active `DashFormBridge` (guaranteed non-null at
@@ -899,8 +1060,16 @@ const tooltipVariants = tv({
899
1060
  const autoTouched = (_bridge_isTouched = bridge.isTouched(name)) != null ? _bridge_isTouched : false;
900
1061
  const submitCount = (_bridge_submitCount = bridge.submitCount) != null ? _bridge_submitCount : 0;
901
1062
  const allowAutoError = autoTouched || submitCount > 0;
1063
+ // `error` still lets an explicit prop force the visual state.
902
1064
  const error = explicitError != null ? explicitError : Boolean(autoErr) && allowAutoError;
903
- const helperText = explicitHelperText != null ? explicitHelperText : allowAutoError ? autoErr == null ? void 0 : autoErr.message : undefined;
1065
+ // BUG 32: the validation message wins over the explicit hint while it
1066
+ // is showing. The previous order (`explicitHelperText ?? autoMessage`)
1067
+ // short-circuited on the hint, so any field carrying one painted the
1068
+ // danger state and `aria-invalid="true"` while still rendering the
1069
+ // hint text — a red field with no stated reason. Identical inversion
1070
+ // to the one BUG 17 landed on the MUI side.
1071
+ const autoMessage = allowAutoError ? autoErr == null ? void 0 : autoErr.message : undefined;
1072
+ const helperText = autoMessage != null ? autoMessage : explicitHelperText;
904
1073
  return {
905
1074
  error,
906
1075
  helperText
@@ -1242,11 +1411,25 @@ const tooltipVariants = tv({
1242
1411
  * - `error` : bool — swaps borders + helper-text color to danger tones
1243
1412
  */ const checkboxVariants = tv({
1244
1413
  slots: {
1245
- root: 'inline-flex items-start gap-2',
1414
+ /*
1415
+ * `relative` is load-bearing, not cosmetic. Radix renders a hidden
1416
+ * native input for form participation with inline
1417
+ * `position: absolute; opacity: 0; transform: translateX(-100%)`.
1418
+ * Without a positioned ancestor it anchors to the BODY, so it escapes
1419
+ * any `overflow: hidden` ancestor and extends the document's
1420
+ * scrollable area down to wherever the field happens to sit.
1421
+ *
1422
+ * Measured on `learn/dash` with a checkbox ~1870px down the page:
1423
+ * `document.documentElement.scrollHeight` 1891 against a 768px
1424
+ * viewport, and the window scrolled 800px inside an AppShell that had
1425
+ * just been given `h-dvh overflow-hidden`. Adding this one declaration
1426
+ * dropped scrollHeight to 768 and the window scroll to 0.
1427
+ * See README-BUG § BUG 36.
1428
+ */ root: 'relative inline-flex items-start gap-2',
1246
1429
  control: [
1247
1430
  'inline-flex items-center justify-center shrink-0',
1248
1431
  // bg-neutral-50 inverts — dark-mode aware via the dashforge preset.
1249
- 'rounded border bg-neutral-50',
1432
+ 'rounded-sm border bg-neutral-50',
1250
1433
  'border-neutral-300',
1251
1434
  'transition-colors',
1252
1435
  // Radix.Checkbox.Root renders a `<button>`, whose UA cursor is
@@ -1595,7 +1778,21 @@ const tooltipVariants = tv({
1595
1778
  * - `helperText` / `errorText` — informational lines below
1596
1779
  */ const switchVariants = tv({
1597
1780
  slots: {
1598
- root: 'inline-flex items-start gap-3',
1781
+ /*
1782
+ * `relative` is load-bearing, not cosmetic. Radix renders a hidden
1783
+ * native input for form participation with inline
1784
+ * `position: absolute; opacity: 0; transform: translateX(-100%)`.
1785
+ * Without a positioned ancestor it anchors to the BODY, so it escapes
1786
+ * any `overflow: hidden` ancestor and extends the document's
1787
+ * scrollable area down to wherever the field happens to sit.
1788
+ *
1789
+ * Measured on `learn/dash` with a checkbox ~1870px down the page:
1790
+ * `document.documentElement.scrollHeight` 1891 against a 768px
1791
+ * viewport, and the window scrolled 800px inside an AppShell that had
1792
+ * just been given `h-dvh overflow-hidden`. Adding this one declaration
1793
+ * dropped scrollHeight to 768 and the window scroll to 0.
1794
+ * See README-BUG § BUG 36.
1795
+ */ root: 'relative inline-flex items-start gap-3',
1599
1796
  control: [
1600
1797
  'relative inline-flex shrink-0 cursor-pointer items-center',
1601
1798
  'rounded-full border-2 border-transparent',
@@ -1864,7 +2061,21 @@ const tooltipVariants = tv({
1864
2061
  * - `error` : bool — swaps borders + helperText color to danger tones
1865
2062
  */ const radioGroupVariants = tv({
1866
2063
  slots: {
1867
- root: 'flex flex-col gap-1.5',
2064
+ /*
2065
+ * `relative` is load-bearing, not cosmetic. Radix renders a hidden
2066
+ * native input for form participation with inline
2067
+ * `position: absolute; opacity: 0; transform: translateX(-100%)`.
2068
+ * Without a positioned ancestor it anchors to the BODY, so it escapes
2069
+ * any `overflow: hidden` ancestor and extends the document's
2070
+ * scrollable area down to wherever the field happens to sit.
2071
+ *
2072
+ * Measured on `learn/dash` with a checkbox ~1870px down the page:
2073
+ * `document.documentElement.scrollHeight` 1891 against a 768px
2074
+ * viewport, and the window scrolled 800px inside an AppShell that had
2075
+ * just been given `h-dvh overflow-hidden`. Adding this one declaration
2076
+ * dropped scrollHeight to 768 and the window scroll to 0.
2077
+ * See README-BUG § BUG 36.
2078
+ */ root: 'relative flex flex-col gap-1.5',
1868
2079
  label: 'block text-sm font-medium text-neutral-900',
1869
2080
  requiredMark: 'text-danger-500 ml-0.5',
1870
2081
  optionList: 'flex gap-3',
@@ -4276,7 +4487,7 @@ function Autocomplete(props) {
4276
4487
  slots: {
4277
4488
  root: 'inline-flex w-fit flex-col gap-2 rounded-lg border border-neutral-200 bg-neutral-50 p-3 select-none',
4278
4489
  header: 'flex items-center justify-between gap-1',
4279
- navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[1rem] text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:pointer-events-none disabled:opacity-40',
4490
+ navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-base text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 disabled:pointer-events-none disabled:opacity-40',
4280
4491
  monthLabel: 'flex-1 text-center text-sm font-semibold text-neutral-900',
4281
4492
  grid: 'flex flex-col gap-1 focus:outline-none',
4282
4493
  weekdayRow: 'grid grid-cols-7 gap-1',
@@ -4301,7 +4512,26 @@ function Autocomplete(props) {
4301
4512
  base: 'flex h-9 w-9 items-center justify-center rounded-md text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500',
4302
4513
  variants: {
4303
4514
  siblingMonth: {
4304
- true: 'text-neutral-400'
4515
+ /*
4516
+ * `neutral-600`, not `neutral-400`, and not `neutral-500` either.
4517
+ *
4518
+ * A sibling-month cell is ACTIVE: it is selectable, so its label is
4519
+ * subject to WCAG 1.4.3, and at 14px / weight 400 the threshold is
4520
+ * 4.5:1. Measured on the running app, `neutral-400` gave **2.42:1**
4521
+ * in light and **2.53:1** in dark. Both fail. (The disabled cells sit
4522
+ * at 1.23:1 and are fine: WCAG exempts the text of inactive
4523
+ * components.)
4524
+ *
4525
+ * `neutral-500` is the pivot of the scale — it stays rgb(115,115,115)
4526
+ * in both themes — so it clears light at 4.54:1 but **fails dark at
4527
+ * 4.18:1**. `neutral-600` is the first tier that passes both:
4528
+ * 7.49:1 light, 7.85:1 dark, while still reading clearly muted
4529
+ * against the current month's 17:1.
4530
+ *
4531
+ * See README-BUG § BUG 31. Do not soften this without re-measuring
4532
+ * BOTH themes: the neutral scale inverts, so one number proves
4533
+ * nothing.
4534
+ */ true: 'text-neutral-600'
4305
4535
  },
4306
4536
  today: {
4307
4537
  true: 'font-bold ring-1 ring-inset ring-primary-400'
@@ -4377,7 +4607,7 @@ function ChevronRightIcon$2() {
4377
4607
  const themeDefaults = useComponentDefaults('Calendar');
4378
4608
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
4379
4609
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
4380
- const { value, defaultValue, onChange, month, year, defaultMonth, defaultYear, onMonthChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, today, disabled = false, autoFocus = false, sx, slotProps, testId, 'aria-label': ariaLabel = 'Calendar' } = merged;
4610
+ const { value, defaultValue, onChange, month, year, defaultMonth, defaultYear, onMonthChange, minDate, maxDate, disabledDates, isDateDisabled, weekStartDay, locale, today, disabled = false, showSiblingDays = true, autoFocus = false, sx, slotProps, testId, 'aria-label': ariaLabel = 'Calendar' } = merged;
4381
4611
  const isValueControlled = value !== undefined;
4382
4612
  const [internalValue, setInternalValue] = useState(defaultValue != null ? defaultValue : null);
4383
4613
  const selectedValue = isValueControlled ? value : internalValue;
@@ -4450,6 +4680,14 @@ function ChevronRightIcon$2() {
4450
4680
  calendar
4451
4681
  ]);
4452
4682
  const resolvedLocale = locale != null ? locale : 'en-US';
4683
+ /**
4684
+ * `slotProps.day` may be a flat object or a function of the cell state.
4685
+ * Resolving it here keeps the call site in the grid readable and means
4686
+ * the theme-level and instance-level slots go through the same path.
4687
+ */ const resolveDaySlot = (slot, state)=>{
4688
+ var _this;
4689
+ return (_this = typeof slot === 'function' ? slot(state) : slot) == null ? void 0 : _this.className;
4690
+ };
4453
4691
  const v = calendarVariants({
4454
4692
  disabled
4455
4693
  });
@@ -4506,8 +4744,26 @@ function ChevronRightIcon$2() {
4506
4744
  role: "row",
4507
4745
  className: v.weekRow(),
4508
4746
  children: week.days.map((day)=>{
4509
- var _themeSlotProps_day, _slotProps_day;
4510
4747
  const isActiveCell = day.iso === calendar.focusedDate;
4748
+ // `showSiblingDays={false}` keeps the 7-column geometry with
4749
+ // an empty cell rather than dropping the element, so the grid
4750
+ // does not reflow and the row stays a row.
4751
+ if (day.isSiblingMonth && !showSiblingDays) {
4752
+ return jsx("span", {
4753
+ role: "gridcell",
4754
+ "aria-hidden": "true",
4755
+ className: "flex",
4756
+ children: jsx("span", {
4757
+ className: "h-9 w-9"
4758
+ })
4759
+ }, day.iso);
4760
+ }
4761
+ const dayState = {
4762
+ siblingMonth: day.isSiblingMonth,
4763
+ today: day.isToday,
4764
+ selected: day.isSelected,
4765
+ disabled: day.isDisabled
4766
+ };
4511
4767
  return jsx("span", {
4512
4768
  role: "gridcell",
4513
4769
  className: "flex",
@@ -4527,12 +4783,7 @@ function ChevronRightIcon$2() {
4527
4783
  onClick: ()=>{
4528
4784
  calendar.selectDate(day.iso);
4529
4785
  },
4530
- className: cn(calendarDayVariants({
4531
- siblingMonth: day.isSiblingMonth,
4532
- today: day.isToday,
4533
- selected: day.isSelected,
4534
- disabled: day.isDisabled
4535
- }), themeSlotProps == null ? void 0 : (_themeSlotProps_day = themeSlotProps.day) == null ? void 0 : _themeSlotProps_day.className, slotProps == null ? void 0 : (_slotProps_day = slotProps.day) == null ? void 0 : _slotProps_day.className),
4786
+ className: cn(calendarDayVariants(dayState), resolveDaySlot(themeSlotProps == null ? void 0 : themeSlotProps.day, dayState), resolveDaySlot(slotProps == null ? void 0 : slotProps.day, dayState)),
4536
4787
  children: day.day
4537
4788
  })
4538
4789
  }, day.iso);
@@ -4628,7 +4879,7 @@ const popoverVariants = tv({
4628
4879
  trigger: 'flex items-center gap-2 h-10 min-w-[12rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
4629
4880
  value: 'flex-1 truncate text-left',
4630
4881
  placeholder: 'flex-1 truncate text-left text-neutral-400',
4631
- icon: 'shrink-0 text-[1rem] text-neutral-500',
4882
+ icon: 'shrink-0 text-base text-neutral-500',
4632
4883
  helperText: 'mt-1 text-sm text-neutral-600',
4633
4884
  errorText: 'mt-1 text-sm text-danger-600'
4634
4885
  },
@@ -4935,9 +5186,9 @@ function CalendarIcon$2() {
4935
5186
  trigger: 'flex items-center gap-2 h-10 min-w-[10rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
4936
5187
  value: 'flex-1 truncate text-left',
4937
5188
  placeholder: 'flex-1 truncate text-left text-neutral-400',
4938
- icon: 'shrink-0 text-[1rem] text-neutral-500',
5189
+ icon: 'shrink-0 text-base text-neutral-500',
4939
5190
  list: 'flex max-h-64 min-w-[8rem] flex-col gap-0.5 overflow-y-auto p-1',
4940
- option: 'flex w-full cursor-pointer items-center rounded px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600 aria-disabled:cursor-default aria-disabled:text-neutral-300 aria-disabled:opacity-60 aria-disabled:hover:bg-transparent',
5191
+ option: 'flex w-full cursor-pointer items-center rounded-md px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600 aria-disabled:cursor-default aria-disabled:text-neutral-300 aria-disabled:opacity-60 aria-disabled:hover:bg-transparent',
4941
5192
  helperText: 'mt-1 text-sm text-neutral-600',
4942
5193
  errorText: 'mt-1 text-sm text-danger-600'
4943
5194
  },
@@ -5231,10 +5482,10 @@ function ClockIcon() {
5231
5482
  trigger: 'flex items-center gap-2 h-10 min-w-[13rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
5232
5483
  value: 'flex-1 truncate text-left',
5233
5484
  placeholder: 'flex-1 truncate text-left text-neutral-400',
5234
- icon: 'shrink-0 text-[1rem] text-neutral-500',
5485
+ icon: 'shrink-0 text-base text-neutral-500',
5235
5486
  panel: 'flex items-stretch',
5236
5487
  list: 'flex max-h-[19rem] min-w-[6.5rem] flex-col gap-0.5 overflow-y-auto border-l border-neutral-200 p-1',
5237
- option: 'flex w-full cursor-pointer items-center rounded px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600',
5488
+ option: 'flex w-full cursor-pointer items-center rounded-md px-2.5 py-1.5 text-sm text-neutral-900 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500 aria-selected:bg-primary-600 aria-selected:text-white aria-selected:hover:bg-primary-600',
5238
5489
  helperText: 'mt-1 text-sm text-neutral-600',
5239
5490
  errorText: 'mt-1 text-sm text-danger-600'
5240
5491
  },
@@ -5581,11 +5832,11 @@ const DATETIME_PATTERN = /^(\d{4}-\d{2}-\d{2})T(\d{2}):(\d{2})/;
5581
5832
  trigger: 'flex items-center gap-2 h-10 min-w-[15rem] rounded-md border border-neutral-300 bg-neutral-50 px-3 text-base text-neutral-900 cursor-pointer transition-colors hover:border-neutral-400 focus-visible:outline-none focus-visible:border-primary-500 focus-visible:ring-2 focus-visible:ring-primary-500/30 disabled:cursor-not-allowed disabled:opacity-60 disabled:bg-neutral-200',
5582
5833
  value: 'flex-1 truncate text-left',
5583
5834
  placeholder: 'flex-1 truncate text-left text-neutral-400',
5584
- icon: 'shrink-0 text-[1rem] text-neutral-500',
5835
+ icon: 'shrink-0 text-base text-neutral-500',
5585
5836
  panel: 'flex gap-3 p-3 select-none',
5586
5837
  month: 'flex flex-col gap-2',
5587
5838
  header: 'flex items-center justify-between gap-1',
5588
- navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[1rem] text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
5839
+ navButton: 'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-base text-neutral-600 transition-colors hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
5589
5840
  navSpacer: 'h-7 w-7 shrink-0',
5590
5841
  monthLabel: 'flex-1 text-center text-sm font-semibold text-neutral-900',
5591
5842
  grid: 'flex flex-col gap-1',
@@ -6364,7 +6615,7 @@ function formatRange(range, locale) {
6364
6615
  itemLabel: 'truncate flex-1 min-w-0 text-left',
6365
6616
  itemBadge: [
6366
6617
  'shrink-0 inline-flex items-center justify-center',
6367
- 'px-1.5 h-5 rounded-full text-[10px] font-medium',
6618
+ 'px-1.5 h-5 rounded-full text-2xs font-medium',
6368
6619
  'bg-neutral-200 text-neutral-800'
6369
6620
  ],
6370
6621
  group: 'flex flex-col gap-0.5',
@@ -6666,7 +6917,16 @@ function formatRange(range, locale) {
6666
6917
  'flex items-center gap-3 w-full px-4 shrink-0',
6667
6918
  'bg-neutral-50 border-b border-neutral-200'
6668
6919
  ],
6669
- start: 'flex items-center gap-2 min-w-0 shrink-0',
6920
+ /*
6921
+ * `shrink-0` removed (README-BUG § BUG 42): it sat next to `min-w-0`,
6922
+ * which only has an effect on an item that is allowed to shrink, so the
6923
+ * two contradicted each other and `shrink-0` won. The slot kept its
6924
+ * content width whatever happened, which meant a long brand pushed
6925
+ * `center` and `end` out of the bar instead of ellipsing.
6926
+ *
6927
+ * Only the already-broken case changes: where there is room, a
6928
+ * shrinkable item and a non-shrinkable one lay out identically.
6929
+ */ start: 'flex items-center gap-2 min-w-0',
6670
6930
  center: 'flex items-center gap-2 min-w-0 flex-1',
6671
6931
  end: 'flex items-center gap-2 shrink-0'
6672
6932
  },
@@ -6693,6 +6953,22 @@ function formatRange(range, locale) {
6693
6953
  sticky: true
6694
6954
  }
6695
6955
  });
6956
+ /**
6957
+ * Recipe for `<TopBarBrand>`.
6958
+ *
6959
+ * `min-w-0` plus `truncate` on both lines is the load-bearing part: a bar
6960
+ * is a fixed-height row, and a long subtitle (a file path, which is the
6961
+ * case this exists for) would otherwise push the `center` and `end` slots
6962
+ * out of the bar rather than ellipsing.
6963
+ */ const topBarBrandVariants = tv({
6964
+ slots: {
6965
+ root: 'flex items-center gap-2 min-w-0',
6966
+ logo: 'shrink-0 flex items-center',
6967
+ text: 'flex flex-col justify-center min-w-0 leading-tight',
6968
+ title: 'truncate text-sm font-semibold text-neutral-900',
6969
+ subtitle: 'truncate text-xs text-neutral-500'
6970
+ }
6971
+ });
6696
6972
 
6697
6973
  /**
6698
6974
  * Dashforge TW TopBar — sticky header with 3 named slots.
@@ -6752,22 +7028,70 @@ function formatRange(range, locale) {
6752
7028
  ]
6753
7029
  });
6754
7030
  }
7031
+ /**
7032
+ * `<TopBarBrand>` — the mark-plus-text block for a `<TopBar>`'s `start`
7033
+ * slot.
7034
+ *
7035
+ * ```tsx
7036
+ * <TopBar
7037
+ * start={<TopBarBrand logo={<Logo />} title="Dashforge" subtitle="src/App.tsx" />}
7038
+ * end={<UserMenu />}
7039
+ * />
7040
+ * ```
7041
+ *
7042
+ * See {@link TopBarBrandProps} for why it is a sibling export rather than
7043
+ * `TopBar.Brand`, and why it stays layout-only.
7044
+ */ const TopBarBrand = /*#__PURE__*/ forwardRef(function TopBarBrand({ logo, title, subtitle, sx, slotProps }, ref) {
7045
+ var _slotProps_root, _slotProps_logo, _slotProps_text, _slotProps_title, _slotProps_subtitle;
7046
+ const v = topBarBrandVariants();
7047
+ return jsxs("div", {
7048
+ ref: ref,
7049
+ className: cn(v.root(), slotProps == null ? void 0 : (_slotProps_root = slotProps.root) == null ? void 0 : _slotProps_root.className, sx),
7050
+ children: [
7051
+ logo != null && jsx("span", {
7052
+ className: cn(v.logo(), slotProps == null ? void 0 : (_slotProps_logo = slotProps.logo) == null ? void 0 : _slotProps_logo.className),
7053
+ children: logo
7054
+ }),
7055
+ jsxs("span", {
7056
+ className: cn(v.text(), slotProps == null ? void 0 : (_slotProps_text = slotProps.text) == null ? void 0 : _slotProps_text.className),
7057
+ children: [
7058
+ jsx("span", {
7059
+ className: cn(v.title(), slotProps == null ? void 0 : (_slotProps_title = slotProps.title) == null ? void 0 : _slotProps_title.className),
7060
+ children: title
7061
+ }),
7062
+ subtitle != null && jsx("span", {
7063
+ className: cn(v.subtitle(), slotProps == null ? void 0 : (_slotProps_subtitle = slotProps.subtitle) == null ? void 0 : _slotProps_subtitle.className),
7064
+ children: subtitle
7065
+ })
7066
+ ]
7067
+ })
7068
+ ]
7069
+ });
7070
+ });
7071
+ TopBarBrand.displayName = 'TopBarBrand';
6755
7072
 
6756
7073
  /**
6757
7074
  * Tailwind-variants recipe for `<AppShell>`.
6758
7075
  *
6759
7076
  * Slots:
6760
- * - `root` — outer flex column (full viewport)
7077
+ * - `root` — outer flex column
6761
7078
  * - `header` — top region
6762
7079
  * - `body` — flex row holding nav + main
6763
7080
  * - `nav` — desktop nav rail (md+)
6764
7081
  * - `navMobile` — mobile drawer (slide-in)
6765
- * - `main` — scrollable content area
7082
+ * - `main` — content area
6766
7083
  * - `footer` — bottom region
6767
7084
  * - `backdrop` — semi-opaque overlay behind the mobile drawer
7085
+ *
7086
+ * Which element scrolls is decided by the `layout` axis, NOT by the
7087
+ * slots. Before BUG 27 it was decided by neither: `root` carried
7088
+ * `min-h-screen` while `main` carried `overflow-y-auto`, which describe
7089
+ * two different layouts, and `min-h-screen` won. The root grew with the
7090
+ * content, the window scrolled, and `main` never overflowed, so its
7091
+ * `overflow-y-auto` was dead on every page long enough to need it.
6768
7092
  */ const appShellVariants = tv({
6769
7093
  slots: {
6770
- root: 'flex flex-col min-h-screen bg-neutral-100',
7094
+ root: 'flex flex-col bg-neutral-100',
6771
7095
  header: 'shrink-0',
6772
7096
  body: 'flex flex-1 min-h-0',
6773
7097
  nav: 'hidden md:flex shrink-0',
@@ -6781,7 +7105,7 @@ function formatRange(range, locale) {
6781
7105
  'transition-transform duration-200 ease-out motion-reduce:transition-none motion-reduce:duration-0',
6782
7106
  '-translate-x-full'
6783
7107
  ],
6784
- main: 'flex-1 min-w-0 overflow-y-auto',
7108
+ main: 'flex-1 min-w-0',
6785
7109
  footer: 'shrink-0',
6786
7110
  backdrop: [
6787
7111
  'md:hidden fixed inset-0 z-30 bg-black/40',
@@ -6791,6 +7115,36 @@ function formatRange(range, locale) {
6791
7115
  ]
6792
7116
  },
6793
7117
  variants: {
7118
+ /*
7119
+ * Both shells are legitimate, so this is an axis rather than a
7120
+ * decision baked into the slots. See README-BUG § BUG 27.
7121
+ *
7122
+ * `viewport` — the shell fills the window and `main` scrolls inside
7123
+ * it, so header, nav and footer stay put. This is the layout the
7124
+ * component's own header diagram draws and the one
7125
+ * `main: overflow-y-auto` was written for, so it is the default.
7126
+ *
7127
+ * `page` — the window scrolls and the whole shell moves with it,
7128
+ * which is what a marketing-style shell wants. `main` gets no
7129
+ * overflow here: it could never be the scroller in this mode, and
7130
+ * advertising one is how the two ended up contradicting.
7131
+ */ layout: {
7132
+ viewport: {
7133
+ /*
7134
+ * `h-dvh`, never `h-screen`. `100vh` is the height the screen
7135
+ * has only while a phone's address bar is hidden, so `h-screen`
7136
+ * yields a shell taller than the window and the page scrolls
7137
+ * again, on exactly the devices where that is worst.
7138
+ */ root: 'h-dvh overflow-hidden',
7139
+ // Fixed must not mean clipped: on a short screen the last nav
7140
+ // items have to stay reachable.
7141
+ nav: 'overflow-y-auto',
7142
+ main: 'overflow-y-auto'
7143
+ },
7144
+ page: {
7145
+ root: 'min-h-screen'
7146
+ }
7147
+ },
6794
7148
  navOpen: {
6795
7149
  true: {
6796
7150
  navMobile: 'translate-x-0',
@@ -6799,6 +7153,7 @@ function formatRange(range, locale) {
6799
7153
  }
6800
7154
  },
6801
7155
  defaultVariants: {
7156
+ layout: 'viewport',
6802
7157
  navOpen: false
6803
7158
  }
6804
7159
  });
@@ -6870,10 +7225,14 @@ function formatRange(range, locale) {
6870
7225
  */ function AppShell(props) {
6871
7226
  var _themeSlotProps_root, _slotProps_root, _themeSlotProps_header, _slotProps_header, _themeSlotProps_nav, _slotProps_nav, _themeSlotProps_navMobile, _slotProps_navMobile, _themeSlotProps_backdrop, _slotProps_backdrop, _themeSlotProps_main, _slotProps_main, _themeSlotProps_footer, _slotProps_footer;
6872
7227
  const themeDefaults = useComponentDefaults('AppShell');
6873
- const { header, nav, footer, children, navOpen = false, onNavOpenChange, sx, slotProps } = props;
7228
+ // Instance props win over the theme, same precedence as every other
7229
+ // component that supports `theme.components.<Name>.defaults`.
7230
+ const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
7231
+ const { header, nav, footer, children, layout = 'viewport', navOpen = false, onNavOpenChange, sx, slotProps } = merged;
6874
7232
  // Theme-level slotProps (Option C Track B).
6875
7233
  const themeSlotProps = themeDefaults == null ? void 0 : themeDefaults.slotProps;
6876
7234
  const v = appShellVariants({
7235
+ layout,
6877
7236
  navOpen
6878
7237
  });
6879
7238
  // Lock body scroll while the mobile drawer is open.
@@ -7875,7 +8234,7 @@ const SnackbarContext = /*#__PURE__*/ createContext(null);
7875
8234
  root: 'px-4 py-3'
7876
8235
  },
7877
8236
  compact: {
7878
- root: 'px-3 py-2 text-[13px]'
8237
+ root: 'px-3 py-2 text-xs'
7879
8238
  }
7880
8239
  }
7881
8240
  },
@@ -8521,6 +8880,21 @@ Progress.displayName = 'Progress';
8521
8880
  *
8522
8881
  * Motion is gated by `prefers-reduced-motion`: slide animations collapse
8523
8882
  * to instantaneous appear/disappear for users who opt out.
8883
+ */ /*
8884
+ * Layering: the Drawer sits on the catalog's `z-50` overlay tier, like
8885
+ * Dialog, Menu, Popover, Tooltip, Snackbar and the two comboboxes. It used
8886
+ * to carry `z-[1400]` / `z-[1410]`, MUI's ladder, which is a different
8887
+ * scale entirely — and authored alone it looked fine.
8888
+ *
8889
+ * What it broke, which only shows when two overlays meet: a Dialog raised
8890
+ * from inside a Drawer rendered BEHIND it (50 against 1410), so the user
8891
+ * got a drawer with an invisible modal holding their focus, and a Snackbar
8892
+ * confirming an action taken in the drawer was invisible too.
8893
+ *
8894
+ * Overlay and content are both `z-50` on purpose, matching Dialog: within
8895
+ * one component DOM order settles it (content renders after the overlay),
8896
+ * and between components the one mounted later wins, which is what
8897
+ * "opened on top" means.
8524
8898
  */ const drawerVariants = tv({
8525
8899
  slots: {
8526
8900
  overlay: [
@@ -8528,7 +8902,7 @@ Progress.displayName = 'Progress';
8528
8902
  // z-1300; Chakra AppShell uses ~1000). z-[1400] sits above them
8529
8903
  // without touching the reserved "topmost" (~9999) tier for host
8530
8904
  // error overlays / dev tools. Matches Chakra Drawer's own default.
8531
- 'fixed inset-0 z-[1400] bg-neutral-950/60 backdrop-blur-sm',
8905
+ 'fixed inset-0 z-50 bg-neutral-950/60 backdrop-blur-sm',
8532
8906
  // Fade the backdrop in/out via a pure CSS keyframe (defined in
8533
8907
  // Drawer.tsx's module-level style injection).
8534
8908
  'data-[state=open]:animate-[df-drawer-fade-in_300ms_ease-out]',
@@ -8538,7 +8912,7 @@ Progress.displayName = 'Progress';
8538
8912
  content: [
8539
8913
  // Same z-tier as the overlay (see note above) plus 10 so the
8540
8914
  // drawer panel stacks above its own backdrop reliably.
8541
- 'fixed z-[1410] flex flex-col',
8915
+ 'fixed z-50 flex flex-col',
8542
8916
  // bg-white hardcoded (doesn't auto-invert) so `dark:` is needed.
8543
8917
  // Same pattern as Dialog for cross-primitive consistency.
8544
8918
  'bg-white dark:bg-neutral-100 shadow-xl',
@@ -8570,7 +8944,11 @@ Progress.displayName = 'Progress';
8570
8944
  // mode without a `dark:` variant.
8571
8945
  'rounded-md border border-neutral-200',
8572
8946
  'text-neutral-500 hover:text-neutral-900 hover:bg-neutral-100 hover:border-neutral-300',
8573
- 'focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-1',
8947
+ // `focus-visible:`, not `focus:` — see BUG 28. Radix focuses the
8948
+ // panel on open and this is its first focusable child, so a plain
8949
+ // `focus:ring` framed the × on every mouse-opened drawer. The
8950
+ // `resizeHandle` slot below already had it right.
8951
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1',
8574
8952
  'transition-colors motion-reduce:transition-none'
8575
8953
  ],
8576
8954
  body: 'flex-1 min-h-0 overflow-y-auto p-4',
@@ -8834,7 +9212,6 @@ injectDrawerKeyframes();
8834
9212
  if (hasWarnedRef.current) return;
8835
9213
  if (process.env.NODE_ENV === 'production') return;
8836
9214
  hasWarnedRef.current = true;
8837
- // eslint-disable-next-line no-console -- dev-only, guarded above.
8838
9215
  console.warn('[@dashforge/tw] <Drawer resize> was used without a `resizeKey`. ' + 'The resized dimension will not persist across sessions. Pass a ' + 'string identifier to `resizeKey` (e.g. `resizeKey="inspector"`) ' + 'to enable localStorage persistence.');
8839
9216
  }, [
8840
9217
  resize,
@@ -9150,7 +9527,7 @@ if (process.env.NODE_ENV !== 'production') {
9150
9527
  'group-data-[state=invalid]/step:text-danger-700'
9151
9528
  ],
9152
9529
  helperText: 'text-xs text-neutral-600 truncate',
9153
- optionalTag: 'text-[10px] italic text-neutral-500',
9530
+ optionalTag: 'text-2xs italic text-neutral-500',
9154
9531
  connector: 'bg-neutral-200',
9155
9532
  content: 'w-full',
9156
9533
  footer: 'flex items-center justify-between mt-4'
@@ -9209,7 +9586,7 @@ if (process.env.NODE_ENV !== 'production') {
9209
9586
  sm: {
9210
9587
  indicator: 'w-6 h-6 text-xs',
9211
9588
  label: 'text-xs',
9212
- helperText: 'text-[10px]'
9589
+ helperText: 'text-2xs'
9213
9590
  },
9214
9591
  md: {
9215
9592
  indicator: 'w-7 h-7 text-sm',
@@ -9341,7 +9718,6 @@ if (process.env.NODE_ENV !== 'production') {
9341
9718
  }, `stepper-passthrough-${i}`));
9342
9719
  });
9343
9720
  if (sawStrayDom && process.env.NODE_ENV !== 'production') {
9344
- // eslint-disable-next-line no-console -- dev-only, guarded by NODE_ENV.
9345
9721
  console.warn('[@dashforge/tw] <Stepper> received bare DOM children (text or ' + 'intrinsic elements like <div>). Only <Step> configs and ' + 'component children (e.g. a footer reading useStep()) are ' + 'recognized; stray DOM was ignored.');
9346
9722
  }
9347
9723
  return {
@@ -9380,7 +9756,6 @@ if (process.env.NODE_ENV !== 'production') {
9380
9756
  return initialStep;
9381
9757
  }
9382
9758
  if (initialStep && process.env.NODE_ENV !== 'production') {
9383
- // eslint-disable-next-line no-console -- dev-only warning.
9384
9759
  console.warn(`[@dashforge/tw] <Stepper initialStep="${initialStep}"> did not ` + `match any visible step. Falling back to the first visible step.`);
9385
9760
  }
9386
9761
  return (_visibleStepNames_ = visibleStepNames[0]) != null ? _visibleStepNames_ : '';
@@ -9471,7 +9846,6 @@ if (process.env.NODE_ENV !== 'production') {
9471
9846
  return false;
9472
9847
  }
9473
9848
  } else if (process.env.NODE_ENV !== 'production') {
9474
- // eslint-disable-next-line no-console -- dev-only guard.
9475
9849
  console.warn(`[@dashforge/tw] <Step name="${step.name}" fields={[...]}> was ` + `used outside a <DashForm> (or with a bridge that does not ` + `implement trigger). The fields validation is skipped.`);
9476
9850
  }
9477
9851
  }
@@ -9882,7 +10256,7 @@ Stepper.displayName = 'Stepper';
9882
10256
  ],
9883
10257
  valueLabel: [
9884
10258
  'absolute -translate-x-1/2 -translate-y-full',
9885
- 'px-1.5 py-0.5 rounded text-xs font-medium text-neutral-50',
10259
+ 'px-1.5 py-0.5 rounded-md text-xs font-medium text-neutral-50',
9886
10260
  'pointer-events-none whitespace-nowrap',
9887
10261
  'shadow-sm',
9888
10262
  // background color applied via variant below
@@ -9931,7 +10305,7 @@ Stepper.displayName = 'Stepper';
9931
10305
  track: 'h-1',
9932
10306
  thumb: 'w-3.5 h-3.5',
9933
10307
  mark: 'w-0.5 h-0.5',
9934
- valueLabel: 'text-[10px] px-1'
10308
+ valueLabel: 'text-2xs px-1'
9935
10309
  },
9936
10310
  md: {
9937
10311
  controlWrapper: 'py-2 h-8',
@@ -10280,6 +10654,7 @@ const DEFAULT_STEP = 1;
10280
10654
  return v <= displayValueArr[0];
10281
10655
  };
10282
10656
  return jsxs("div", {
10657
+ ref: ref,
10283
10658
  className: rootClasses,
10284
10659
  "data-testid": testId,
10285
10660
  children: [
@@ -10600,7 +10975,8 @@ Slider.displayName = 'Slider';
10600
10975
  *
10601
10976
  * `V` inference — TypeScript narrows the union from the `options`
10602
10977
  * array so `onChange` gets the precise literal type.
10603
- */ function SelectInner(props, ref) {
10978
+ */ function SelectInner(props, // The trigger is a `<div role="combobox">`, not a `<button>` — see BUG 23.
10979
+ ref) {
10604
10980
  var _ref;
10605
10981
  var _themeDefaults_defaults, _themeSlotProps_root, _slotProps_root, _themeSlotProps_label, _slotProps_label, _themeSlotProps_requiredMark, _slotProps_requiredMark, _themeSlotProps_trigger, _slotProps_trigger, _themeSlotProps_triggerText, _slotProps_triggerText, _themeSlotProps_triggerPlaceholder, _slotProps_triggerPlaceholder, _themeSlotProps_chevron, _slotProps_chevron, _themeSlotProps_popover, _slotProps_popover, _themeSlotProps_listBox, _slotProps_listBox, _themeSlotProps_emptyState, _slotProps_emptyState, _themeSlotProps_helperText, _slotProps_helperText, _themeSlotProps_errorText, _slotProps_errorText, _themeSlotProps_chipsList, _slotProps_chipsList, _themeSlotProps_chip, _slotProps_chip, _themeSlotProps_chipRemove, _slotProps_chipRemove, _triggerRef_current;
10606
10982
  const themeDefaults = useComponentDefaults('Select');
@@ -10617,6 +10993,7 @@ Slider.displayName = 'Slider';
10617
10993
  const controlId = useId();
10618
10994
  const listboxId = `${controlId}-listbox`;
10619
10995
  const helperId = `${controlId}-help`;
10996
+ const labelId = `${controlId}-label`;
10620
10997
  const isFormMode = Boolean(bridge == null ? void 0 : bridge.register);
10621
10998
  useStandaloneFieldWarning('Select', name, isFormMode, userValue, onChange);
10622
10999
  // Mirror of useStandaloneFieldWarning: warn when running under a
@@ -10981,8 +11358,18 @@ Slider.displayName = 'Slider';
10981
11358
  "data-testid": testId,
10982
11359
  children: [
10983
11360
  label != null && jsx("label", {
11361
+ id: labelId,
10984
11362
  htmlFor: controlId,
10985
11363
  className: labelClasses,
11364
+ // `htmlFor` no longer associates anything: the trigger is a div,
11365
+ // and `<label for>` only binds to a labelable element. It is kept
11366
+ // because it is harmless and consumers may key CSS off it, but
11367
+ // the real name comes from `aria-labelledby` on the trigger and
11368
+ // the focus-on-click has to be done by hand. BUG 23.
11369
+ onClick: ()=>{
11370
+ var _triggerRef_current;
11371
+ return (_triggerRef_current = triggerRef.current) == null ? void 0 : _triggerRef_current.focus();
11372
+ },
10986
11373
  children: renderLabelWithTooltip(label, tooltipConfig, required && jsx("span", {
10987
11374
  className: requiredMarkClasses,
10988
11375
  children: "*"
@@ -10994,24 +11381,27 @@ Slider.displayName = 'Slider';
10994
11381
  children: [
10995
11382
  jsx(RadixPopover.Trigger, {
10996
11383
  asChild: true,
10997
- children: jsxs("button", {
11384
+ children: jsxs("div", _extends({
10998
11385
  ref: (node)=>{
10999
11386
  triggerRef.current = node;
11000
11387
  if (typeof ref === 'function') ref(node);
11001
11388
  else if (ref) ref.current = node;
11002
11389
  },
11003
- id: controlId,
11004
- type: "button",
11390
+ id: controlId
11391
+ }, {
11392
+ type: undefined
11393
+ }, {
11005
11394
  role: "combobox",
11395
+ tabIndex: effectiveDisabled ? -1 : 0,
11006
11396
  "aria-haspopup": "listbox",
11007
11397
  "aria-expanded": isOpen,
11008
11398
  "aria-controls": listboxId,
11399
+ "aria-labelledby": label != null ? labelId : undefined,
11009
11400
  "aria-required": required || undefined,
11010
11401
  "aria-invalid": hasError || undefined,
11011
11402
  "aria-describedby": resolvedHelperText != null ? helperId : undefined,
11012
11403
  "aria-disabled": effectiveDisabled || undefined,
11013
- disabled: effectiveDisabled,
11014
- name: name,
11404
+ "data-name": name,
11015
11405
  className: triggerClasses,
11016
11406
  onKeyDown: handleTriggerKeyDown,
11017
11407
  children: [
@@ -11020,7 +11410,7 @@ Slider.displayName = 'Slider';
11020
11410
  className: cn(chevronClasses)
11021
11411
  })
11022
11412
  ]
11023
- })
11413
+ }))
11024
11414
  }),
11025
11415
  jsx(RadixPopover.Portal, {
11026
11416
  children: jsx(RadixPopover.Content, {
@@ -11147,7 +11537,7 @@ Select.displayName = 'Select';
11147
11537
  'inline-flex items-center gap-1',
11148
11538
  // Anchor semantics — cursor + focus-visible ring using primary token.
11149
11539
  'cursor-pointer',
11150
- 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 focus-visible:rounded',
11540
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 focus-visible:rounded-sm',
11151
11541
  // Motion-reduce safe transition on color / decoration.
11152
11542
  'transition-colors',
11153
11543
  'motion-reduce:transition-none'
@@ -11246,7 +11636,6 @@ Select.displayName = 'Select';
11246
11636
  // the Slot pattern paints classes onto a foreign child, and there is
11247
11637
  // no seam for us to inject start/end icon wrappers.
11248
11638
  if (process.env.NODE_ENV !== 'production' && (startIcon != null || endIcon != null)) {
11249
- // eslint-disable-next-line no-console -- dev-only nudge.
11250
11639
  console.warn('[@dashforge/tw] <Link asChild> ignores startIcon / endIcon — the ' + 'child element renders its own content. Move the icons inside the ' + 'child instead.');
11251
11640
  }
11252
11641
  return jsx(Slot, _extends({
@@ -11688,7 +12077,7 @@ MenuItem.displayName = 'MenuItem';
11688
12077
  // dimensions below.
11689
12078
  'inline-flex items-center justify-center',
11690
12079
  'rounded-full',
11691
- 'text-[10px] font-medium leading-none',
12080
+ 'text-2xs font-medium leading-none',
11692
12081
  'pointer-events-none select-none',
11693
12082
  // Smooth show/hide for `invisible` toggle (consumer-side
11694
12083
  // animation hook). Gated on motion-reduce per WCAG 2.3.3.
@@ -11938,6 +12327,78 @@ MenuItem.displayName = 'MenuItem';
11938
12327
  });
11939
12328
  Badge.displayName = 'Badge';
11940
12329
 
12330
+ /**
12331
+ * Tailwind-variants recipe for `<Kbd>`.
12332
+ *
12333
+ * One axis, `size`, because a keycap has no intent: it is chrome around a
12334
+ * literal key, not a status or an action. Colour is fixed to the neutral
12335
+ * ramp, which auto-inverts through the preset's CSS vars, so the cap reads
12336
+ * as a raised surface in light and a recessed one in dark with no `dark:`
12337
+ * variant to keep in sync.
12338
+ *
12339
+ * The border is what makes the cap read as a cap, and it is at `-500`
12340
+ * rather than the `-300` a hairline invites. Measured on `learn/dash`
12341
+ * against both surfaces a keycap realistically sits on:
12342
+ *
12343
+ * surface neutral-100 ... 1.09 vs page, 1.04 vs card
12344
+ * surface neutral-200 ... 1.26 / 1.21
12345
+ * border neutral-300 ... 1.48 / 1.42
12346
+ * border neutral-400 ... 2.52 / 2.42
12347
+ * border neutral-500 ... 4.74 / 4.54 <- the first that clears 3:1
12348
+ *
12349
+ * The first draft assumed the `bg-neutral-100` surface would do the
12350
+ * separating and left the border at `-300`. It does not: a neutral surface
12351
+ * one step off the page is ~1.1:1 by construction, and in dark mode it
12352
+ * lands on exactly the same colour as a card, so the cap disappeared into
12353
+ * the panel and only the hairline was left. That is the same shape that
12354
+ * failed on the outline Chip (README-BUG § BUG 40), and it reached the
12355
+ * same tier for the same reason.
12356
+ *
12357
+ * The surface stays as a faint lift, not as the separator.
12358
+ *
12359
+ * `rounded-md` rather than a bare `rounded`, which is hard-coded at
12360
+ * 0.25rem and immune to the radius tokens (README-BUG § BUG 26).
12361
+ */ const kbdVariants = tv({
12362
+ base: [
12363
+ 'inline-flex items-center justify-center',
12364
+ 'font-mono font-medium leading-none whitespace-nowrap select-none',
12365
+ 'bg-neutral-100 text-neutral-700',
12366
+ 'border border-neutral-500 rounded-md shadow-sm'
12367
+ ],
12368
+ variants: {
12369
+ size: {
12370
+ sm: 'h-5 min-w-5 px-1 text-2xs',
12371
+ md: 'h-6 min-w-6 px-1.5 text-xs',
12372
+ lg: 'h-7 min-w-7 px-2 text-sm'
12373
+ }
12374
+ },
12375
+ defaultVariants: {
12376
+ size: 'md'
12377
+ }
12378
+ });
12379
+
12380
+ /**
12381
+ * `<Kbd>` — a single keycap.
12382
+ *
12383
+ * ```tsx
12384
+ * Press <Kbd>Esc</Kbd> to close
12385
+ * ```
12386
+ *
12387
+ * See {@link KbdProps} for the full API surface, including why chords are
12388
+ * composed rather than passed as an array.
12389
+ */ const Kbd = /*#__PURE__*/ forwardRef(function Kbd(rawProps, ref) {
12390
+ const themeDefaults = useComponentDefaults('Kbd');
12391
+ const { size, sx, className, children } = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, rawProps);
12392
+ return jsx("kbd", {
12393
+ ref: ref,
12394
+ className: cn(kbdVariants({
12395
+ size
12396
+ }), className, sx),
12397
+ children: children
12398
+ });
12399
+ });
12400
+ Kbd.displayName = 'Kbd';
12401
+
11941
12402
  /**
11942
12403
  * Tailwind-variants recipe for `<Avatar>`.
11943
12404
  *
@@ -11972,7 +12433,7 @@ Badge.displayName = 'Badge';
11972
12433
  variants: {
11973
12434
  size: {
11974
12435
  xs: {
11975
- root: 'w-5 h-5 text-[10px]',
12436
+ root: 'w-5 h-5 text-2xs',
11976
12437
  initials: ''
11977
12438
  },
11978
12439
  sm: {
@@ -12412,7 +12873,7 @@ AvatarGroup.displayName = 'AvatarGroup';
12412
12873
  variants: {
12413
12874
  variant: {
12414
12875
  text: {
12415
- root: 'rounded h-[1em] w-full'
12876
+ root: 'rounded-sm h-[1em] w-full'
12416
12877
  },
12417
12878
  rectangle: {
12418
12879
  root: 'rounded-md w-full h-[100px]'
@@ -13118,6 +13579,18 @@ Video.displayName = 'Video';
13118
13579
  4: 'shadow-lg',
13119
13580
  5: 'shadow-xl'
13120
13581
  },
13582
+ /*
13583
+ * Border style. Only visible where a border-width is already set:
13584
+ * `variant="outlined"` sets one, and a consumer can bring their own
13585
+ * through `sx`. Declared as its own axis rather than folded into
13586
+ * `variant` because the two are orthogonal — a dashed placeholder
13587
+ * can be outlined, soft or solid. kensaadi/dashforge#63 gap I.
13588
+ */ borderStyle: {
13589
+ solid: 'border-solid',
13590
+ dashed: 'border-dashed',
13591
+ dotted: 'border-dotted',
13592
+ double: 'border-double'
13593
+ },
13121
13594
  rounded: {
13122
13595
  none: 'rounded-none',
13123
13596
  sm: 'rounded-sm',
@@ -13394,11 +13867,12 @@ Video.displayName = 'Video';
13394
13867
  // theme < instance; `sx` layers last via `cn()` + tailwind-merge.
13395
13868
  const themeDefaults = useComponentDefaults('Box');
13396
13869
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
13397
- const { variant, color, elevation, rounded, p, px, py, m, mx, my, fullWidth, fullHeight, as, asChild = false, sx, visibleWhen, access, children } = merged, rest = _object_without_properties_loose(merged, [
13870
+ const { variant, color, elevation, rounded, borderStyle, p, px, py, m, mx, my, fullWidth, fullHeight, as, asChild = false, sx, visibleWhen, access, children } = merged, rest = _object_without_properties_loose(merged, [
13398
13871
  "variant",
13399
13872
  "color",
13400
13873
  "elevation",
13401
13874
  "rounded",
13875
+ "borderStyle",
13402
13876
  "p",
13403
13877
  "px",
13404
13878
  "py",
@@ -13436,6 +13910,7 @@ Video.displayName = 'Video';
13436
13910
  color,
13437
13911
  elevation,
13438
13912
  rounded,
13913
+ borderStyle,
13439
13914
  p,
13440
13915
  px,
13441
13916
  py,
@@ -13726,7 +14201,18 @@ CardActionArea.displayName = 'CardActionArea';
13726
14201
  * (one step up the elevation ladder of its current variant).
13727
14202
  * Compound rules below apply the actual shift per (variant, color)
13728
14203
  * cell.
13729
- */ selected: {
14204
+ */ /*
14205
+ * Pointer affordance for a chip used as a drag source, e.g. a palette
14206
+ * of items dragged onto a canvas. Native HTML5 drag-and-drop, so the
14207
+ * library takes on no dnd dependency and the consumer keeps ownership
14208
+ * of the transfer payload. kensaadi/dashforge#63 gap D.
14209
+ *
14210
+ * `active:cursor-grabbing` rather than a `:hover` swap: the closed
14211
+ * hand belongs to the moment the pointer is down, not to hovering.
14212
+ */ draggable: {
14213
+ true: 'cursor-grab active:cursor-grabbing'
14214
+ },
14215
+ selected: {
13730
14216
  true: 'ring-2 ring-offset-1'
13731
14217
  },
13732
14218
  /** Disabled state for clickable chips. */ disabled: {
@@ -13820,7 +14306,7 @@ CardActionArea.displayName = 'CardActionArea';
13820
14306
  {
13821
14307
  variant: 'outline',
13822
14308
  color: 'neutral',
13823
- class: 'border border-neutral-300 text-neutral-700'
14309
+ class: 'border border-neutral-500 text-neutral-700'
13824
14310
  },
13825
14311
  {
13826
14312
  variant: 'outline',
@@ -14154,7 +14640,7 @@ CardActionArea.displayName = 'CardActionArea';
14154
14640
  */ const Chip = /*#__PURE__*/ forwardRef(function Chip(props, ref) {
14155
14641
  const themeDefaults = useComponentDefaults('Chip');
14156
14642
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
14157
- const { label, icon, avatar, variant, color, size, clickable: clickableProp, onClick, onDelete, deleteIcon, deleteLabel = 'Remove', selected, disabled: disabledProp, visibleWhen, access, sx, className } = merged;
14643
+ const { label, icon, avatar, variant, color, size, clickable: clickableProp, onClick, onDelete, deleteIcon, deleteLabel = 'Remove', selected, disabled: disabledProp, draggable, onDragStart, onDragEnd, visibleWhen, access, sx, className } = merged;
14158
14644
  // Bridge — both hooks called unconditionally (rules-of-hooks).
14159
14645
  // Inside a `<DashForm>`, `useEngineVisibility` subscribes to engine
14160
14646
  // state and re-evaluates the predicate on changes; outside a form,
@@ -14173,7 +14659,8 @@ CardActionArea.displayName = 'CardActionArea';
14173
14659
  size,
14174
14660
  clickable: isClickable || undefined,
14175
14661
  selected: selected || undefined,
14176
- disabled: isDisabled || undefined
14662
+ disabled: isDisabled || undefined,
14663
+ draggable: draggable || undefined
14177
14664
  }), sx, className);
14178
14665
  // ─── Inner content — leading slot + label + delete ────────────
14179
14666
  // Avatar wins over icon when both provided (MUI parity).
@@ -14183,6 +14670,46 @@ CardActionArea.displayName = 'CardActionArea';
14183
14670
  // `inline-flex items-center` provides vertical alignment with
14184
14671
  // any leading / trailing slots.
14185
14672
  const leading = avatar != null ? avatar : icon;
14673
+ const deleteClasses = cn('ml-0.5 -mr-1 inline-flex items-center justify-center', 'h-4 w-4 rounded-full opacity-70 hover:opacity-100', 'transition-opacity');
14674
+ /*
14675
+ * The delete affordance has TWO shapes, and which one is legal depends
14676
+ * on what the root turned out to be. See README-BUG § BUG 35.
14677
+ *
14678
+ * Static root (`<span>`): a real `<button>`. A span is not a widget, so
14679
+ * a focusable button inside it is valid HTML and is the only keyboard
14680
+ * path to delete. Unchanged.
14681
+ *
14682
+ * Clickable root (`role="button"`): NOT a button, and not focusable.
14683
+ * Both `<button>` and `role="button"` forbid interactive descendants —
14684
+ * HTML by its content model, ARIA by the presentational-children rule,
14685
+ * which strips the semantics of everything inside a `button` role. So a
14686
+ * nested control is either invalid markup or invisible to AT, depending
14687
+ * on which spelling you pick. The keyboard path moves to Backspace /
14688
+ * Delete on the chip itself, which is what MUI does and what this
14689
+ * component's own `onKeyDown` comment already anticipated.
14690
+ */ const deleteNode = onDelete == null ? null : isClickable ? jsx("span", {
14691
+ "data-chip-delete": "",
14692
+ "aria-hidden": "true",
14693
+ onClick: (e)=>{
14694
+ // Stop propagation so the chip's own onClick doesn't fire when
14695
+ // the user clicks the × — MUI matches this behaviour.
14696
+ e.stopPropagation();
14697
+ if (!isDisabled) onDelete(e);
14698
+ },
14699
+ className: cn(deleteClasses, !isDisabled && 'cursor-pointer'),
14700
+ children: deleteIcon != null ? deleteIcon : jsx(DefaultDeleteIcon, {})
14701
+ }) : jsx("button", {
14702
+ "data-chip-delete": "",
14703
+ type: "button",
14704
+ "aria-label": deleteLabel,
14705
+ onClick: (e)=>{
14706
+ e.stopPropagation();
14707
+ if (!isDisabled) onDelete(e);
14708
+ },
14709
+ disabled: isDisabled,
14710
+ className: cn(deleteClasses, 'outline-none focus-visible:ring-2 focus-visible:ring-current'),
14711
+ children: deleteIcon != null ? deleteIcon : jsx(DefaultDeleteIcon, {})
14712
+ });
14186
14713
  const innerContent = jsxs(Fragment, {
14187
14714
  children: [
14188
14715
  leading != null && jsx("span", {
@@ -14190,45 +14717,70 @@ CardActionArea.displayName = 'CardActionArea';
14190
14717
  children: leading
14191
14718
  }),
14192
14719
  label,
14193
- onDelete != null && jsx("button", {
14194
- type: "button",
14195
- "aria-label": deleteLabel,
14196
- onClick: (e)=>{
14197
- // Stop propagation so the chip's own onClick doesn't fire
14198
- // when the user clicks the × — MUI matches this behaviour.
14199
- e.stopPropagation();
14200
- if (!isDisabled) onDelete(e);
14201
- },
14202
- disabled: isDisabled,
14203
- className: cn('ml-0.5 -mr-1 inline-flex items-center justify-center', 'h-4 w-4 rounded-full opacity-70 hover:opacity-100', 'outline-none focus-visible:ring-2 focus-visible:ring-current', 'transition-opacity'),
14204
- children: deleteIcon != null ? deleteIcon : jsx(DefaultDeleteIcon, {})
14205
- })
14720
+ deleteNode
14206
14721
  ]
14207
14722
  });
14208
14723
  // ─── Interactive root (clickable) ─────────────────────────────
14724
+ /*
14725
+ * A `<div role="button">`, not a `<button>`. With `onDelete` also set,
14726
+ * the native element nested a `<button>` inside a `<button>`, which is
14727
+ * invalid HTML: React reported a hydration error and the parser hoisted
14728
+ * the inner control out. BUG 35.
14729
+ *
14730
+ * Everything the native element gave away is re-supplied below, because
14731
+ * a div gives none of it: focusability, Enter / Space activation, and
14732
+ * inertness while disabled.
14733
+ */ // A disabled chip must not be a drag source either: `pointer-events-none`
14734
+ // already blocks the pointer, but the attribute would still advertise it.
14735
+ const dragProps = {
14736
+ draggable: draggable && !isDisabled ? true : undefined,
14737
+ onDragStart: draggable && !isDisabled ? onDragStart : undefined,
14738
+ onDragEnd: draggable && !isDisabled ? onDragEnd : undefined
14739
+ };
14209
14740
  if (isClickable) {
14210
- return jsx("button", {
14741
+ return jsx("div", _extends({
14211
14742
  ref: ref,
14212
- type: "button",
14213
- className: classes,
14214
- disabled: isDisabled,
14743
+ role: "button",
14744
+ tabIndex: isDisabled ? -1 : 0,
14745
+ className: classes
14746
+ }, dragProps, {
14747
+ "aria-disabled": isDisabled || undefined,
14215
14748
  "aria-pressed": selected ? true : undefined,
14216
14749
  onClick: (e)=>{
14217
14750
  if (isDisabled) return;
14218
14751
  onClick == null ? void 0 : onClick(e);
14219
14752
  },
14220
14753
  onKeyDown: (e)=>{
14754
+ if (isDisabled) return;
14755
+ // Ignore anything bubbling up from a child, so a focusable slot
14756
+ // (an interactive avatar, say) keeps its own keys. Mirrors
14757
+ // MUI's `event.currentTarget === event.target` guard.
14758
+ if (e.currentTarget !== e.target) return;
14759
+ // A div has no implicit activation. Space is prevented first so
14760
+ // the page does not scroll under the chip.
14761
+ if (e.key === 'Enter' || e.key === ' ') {
14762
+ e.preventDefault();
14763
+ onClick == null ? void 0 : onClick(e);
14764
+ return;
14765
+ }
14766
+ // The keyboard path to the delete affordance, which is not
14767
+ // focusable in this branch. Same keys as MUI.
14768
+ if (onDelete != null && (e.key === 'Backspace' || e.key === 'Delete')) {
14769
+ e.preventDefault();
14770
+ onDelete(e);
14771
+ }
14221
14772
  },
14222
14773
  children: innerContent
14223
- });
14774
+ }));
14224
14775
  }
14225
14776
  // ─── Static root (display only) ───────────────────────────────
14226
- return jsx("span", {
14777
+ return jsx("span", _extends({
14227
14778
  ref: ref,
14228
14779
  className: classes,
14229
- "aria-disabled": isDisabled || undefined,
14780
+ "aria-disabled": isDisabled || undefined
14781
+ }, dragProps, {
14230
14782
  children: innerContent
14231
- });
14783
+ }));
14232
14784
  });
14233
14785
 
14234
14786
  /**
@@ -14548,7 +15100,19 @@ Typography.displayName = 'Typography';
14548
15100
  * Runtime allow-list, for the dev-warn to check against a passed
14549
15101
  * `gap` value at render time. Same set as {@link StackGap}. Frozen
14550
15102
  * so callers can't mutate the source of truth.
14551
- */ const STACK_GAP_VALUES = Object.freeze([
15103
+ */ /**
15104
+ * The accepted `direction` values, for the dev-time guard in `Stack.tsx`.
15105
+ *
15106
+ * Frozen and exported rather than inlined so the warning cannot drift from
15107
+ * the variant recipe: adding an axis value without adding it here would
15108
+ * make the guard warn about something that works.
15109
+ */ const STACK_DIRECTION_VALUES = Object.freeze([
15110
+ 'row',
15111
+ 'col',
15112
+ 'row-reverse',
15113
+ 'col-reverse'
15114
+ ]);
15115
+ const STACK_GAP_VALUES = Object.freeze([
14552
15116
  0,
14553
15117
  0.5,
14554
15118
  1,
@@ -14568,6 +15132,25 @@ Typography.displayName = 'Typography';
14568
15132
  * useStandaloneFieldWarning "warn once" ergonomic. `Set<unknown>` so
14569
15133
  * `null` / arbitrary strings share the same bucket as `NaN`.
14570
15134
  */ const warnedGapValues = new Set();
15135
+ /** Same "warn once per value" bucket, for `direction`. */ const warnedDirectionValues = new Set();
15136
+ /** Whether the `spacing`-instead-of-`gap` warning has already fired. */ let warnedSpacingAlias = false;
15137
+ /**
15138
+ * Names people reach for instead of the ones this component uses, mapped to
15139
+ * what they meant. kensaadi/dashforge#63 gap K reported the surprise;
15140
+ * accepting both spellings was the alternative and was not taken, because a
15141
+ * second name for one axis is a fork every consumer, every theme default
15142
+ * and every doc example then has to pick a side of, forever.
15143
+ *
15144
+ * TypeScript already rejects these. The guard is for the consumers TS does
15145
+ * not reach: JS callers, `{...anyProps}` spreads, values read from config.
15146
+ * Left alone the value is simply dropped and the Stack renders its default
15147
+ * axis, which is the silent failure the report described as "surprising".
15148
+ */ const DIRECTION_ALIASES = {
15149
+ column: 'col',
15150
+ 'column-reverse': 'col-reverse',
15151
+ vertical: 'col',
15152
+ horizontal: 'row'
15153
+ };
14571
15154
  /**
14572
15155
  * Walk the children, inserting `divider` BETWEEN every consecutive pair.
14573
15156
  *
@@ -14583,7 +15166,28 @@ Typography.displayName = 'Typography';
14583
15166
  * • If `divider` is a valid element, we `cloneElement` once per
14584
15167
  * boundary (cheaper than re-rendering the JSX expression N-1 times).
14585
15168
  * For string/number dividers we wrap in a span automatically.
14586
- */ function interleaveDividers(children, divider) {
15169
+ */ /**
15170
+ * The divider orientation that separates items along each axis. A row is
15171
+ * separated by vertical rules and a column by horizontal ones, which is the
15172
+ * opposite of what the names suggest and the reason this was worth deriving
15173
+ * rather than leaving to the caller. kensaadi/dashforge#63 gap F.
15174
+ */ const DIVIDER_ORIENTATION_FOR = {
15175
+ row: 'vertical',
15176
+ 'row-reverse': 'vertical',
15177
+ col: 'horizontal',
15178
+ 'col-reverse': 'horizontal'
15179
+ };
15180
+ /**
15181
+ * True for our own `<Divider>`, read off `displayName` rather than by
15182
+ * importing the component: Stack has no business depending on Divider, and
15183
+ * the check has to fail safely for anything else. Injecting an
15184
+ * `orientation` prop into a foreign element is how `tooltip` once landed on
15185
+ * the DOM as an unknown attribute (README-BUG § BUG 9).
15186
+ */ function isDashforgeDivider(node) {
15187
+ var _node_type;
15188
+ return ((_node_type = node.type) == null ? void 0 : _node_type.displayName) === 'Divider';
15189
+ }
15190
+ function interleaveDividers(children, divider, direction) {
14587
15191
  const items = Children.toArray(children);
14588
15192
  if (items.length <= 1) return items;
14589
15193
  const result = [];
@@ -14592,9 +15196,20 @@ Typography.displayName = 'Typography';
14592
15196
  if (i < items.length - 1) {
14593
15197
  const key = `df-stack-divider-${i}`;
14594
15198
  if (/*#__PURE__*/ isValidElement(divider)) {
14595
- result.push(/*#__PURE__*/ cloneElement(divider, {
15199
+ var _DIVIDER_ORIENTATION_FOR_direction;
15200
+ // A `<Divider>` defaults to `horizontal`, so dropping one into a
15201
+ // row Stack gave a zero-height rule at `w-full`: it did not
15202
+ // separate anything, it demanded the whole width and squeezed the
15203
+ // items instead. The Stack knows its own axis, so it supplies the
15204
+ // orientation the caller did not choose. An explicit `orientation`
15205
+ // always wins, and a non-Divider node is passed through untouched.
15206
+ const props = divider.props;
15207
+ const shouldDerive = isDashforgeDivider(divider) && props.orientation === undefined;
15208
+ result.push(/*#__PURE__*/ cloneElement(divider, _extends({
14596
15209
  key
14597
- }));
15210
+ }, shouldDerive ? {
15211
+ orientation: (_DIVIDER_ORIENTATION_FOR_direction = DIVIDER_ORIENTATION_FOR[direction]) != null ? _DIVIDER_ORIENTATION_FOR_direction : 'horizontal'
15212
+ } : {})));
14598
15213
  } else {
14599
15214
  result.push(jsx(Fragment$1, {
14600
15215
  children: divider
@@ -14627,7 +15242,7 @@ Typography.displayName = 'Typography';
14627
15242
  */ const Stack = /*#__PURE__*/ forwardRef(function Stack(props, ref) {
14628
15243
  const themeDefaults = useComponentDefaults('Stack');
14629
15244
  const merged = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, props);
14630
- const { direction, align, justify, gap, wrap, fullWidth, fullHeight, divider, as, asChild = false, sx, children } = merged, rest = _object_without_properties_loose(merged, [
15245
+ const { direction, align, justify, gap, wrap, fullWidth, fullHeight, divider, as, asChild = false, sx, visibleWhen, access, children } = merged, rest = _object_without_properties_loose(merged, [
14631
15246
  "direction",
14632
15247
  "align",
14633
15248
  "justify",
@@ -14639,6 +15254,8 @@ Typography.displayName = 'Typography';
14639
15254
  "as",
14640
15255
  "asChild",
14641
15256
  "sx",
15257
+ "visibleWhen",
15258
+ "access",
14642
15259
  "children"
14643
15260
  ]);
14644
15261
  // Dev-only guard for #111 (G-27): `<Stack gap>` is typed to the
@@ -14648,9 +15265,18 @@ Typography.displayName = 'Typography';
14648
15265
  // consumers get a real signal instead of silent `rowGap: normal`.
14649
15266
  if (process.env.NODE_ENV !== 'production' && gap !== undefined && !STACK_GAP_VALUES.includes(gap) && !warnedGapValues.has(gap)) {
14650
15267
  warnedGapValues.add(gap);
14651
- // eslint-disable-next-line no-console -- dev-only guard, guarded by NODE_ENV above.
14652
15268
  console.warn(`[@dashforge/tw] <Stack gap={${JSON.stringify(gap)}}> is not on the ` + `token-scale set (${STACK_GAP_VALUES.join(', ')}). The variant ` + `mapping silently falls back to no gap. Use a numeric literal ` + `from the accepted set — token strings like "sm" / "md" / "lg" ` + `are not supported.`);
14653
15269
  }
15270
+ // Same shape of guard for `direction`, which fails the same silent way:
15271
+ // an unrecognised value is dropped by the variant recipe and the Stack
15272
+ // falls back to `defaultVariants.direction`, so a column Stack asked
15273
+ // for as "column" quietly lays out as a column anyway and a "vertical"
15274
+ // row does not. See kensaadi/dashforge#63 gap K.
15275
+ if (process.env.NODE_ENV !== 'production' && direction !== undefined && !STACK_DIRECTION_VALUES.includes(direction) && !warnedDirectionValues.has(direction)) {
15276
+ warnedDirectionValues.add(direction);
15277
+ const meant = DIRECTION_ALIASES[String(direction)];
15278
+ console.warn(`[@dashforge/tw] <Stack direction={${JSON.stringify(direction)}}> is ` + `not an accepted value (${STACK_DIRECTION_VALUES.join(', ')}). ` + `The variant mapping drops it and the Stack falls back to ` + `"${'col'}".` + (meant ? ` Did you mean "${meant}"?` : ''));
15279
+ }
14654
15280
  // Runtime safety-net for #112 (G-28): the `StackProps` interface
14655
15281
  // Omit-excludes `className`, forcing typed consumers to use `sx`. But
14656
15282
  // TS-loose consumers (unchecked casts, `{...anyProps}` spreads) can
@@ -14661,9 +15287,40 @@ Typography.displayName = 'Typography';
14661
15287
  // Extract it, feed it through `cn` so tailwind-merge preserves the
14662
15288
  // non-conflicting utility (e.g. consumer's `min-h-0` + variant
14663
15289
  // `flex flex-col gap-3`), and drop it from the DOM spread.
14664
- const { className: consumerClassName } = rest, safeRest = _object_without_properties_loose(rest, [
14665
- "className"
15290
+ // Bridge — hooks called unconditionally, ABOVE the early return
15291
+ // (rules-of-hooks; see README-BUG § BUG 33 for what happens when a
15292
+ // subscription is made conditional instead). `useEngineVisibility`
15293
+ // re-evaluates the predicate reactively inside a `<DashForm>` and
15294
+ // watches nothing when `visibleWhen` is omitted; `useAccessState`
15295
+ // consults the @dashforge/rbac policy engine. Both are no-ops when
15296
+ // their prop is absent, so a consumer passing neither renders
15297
+ // exactly as before.
15298
+ const bridge = useContext(DashFormContext);
15299
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
15300
+ const accessState = useAccessState(access);
15301
+ // Early return — predicate false OR RBAC denies visibility.
15302
+ if (!isVisible || !accessState.visible) return null;
15303
+ const ariaProps = {
15304
+ 'aria-disabled': accessState.disabled || undefined,
15305
+ 'aria-readonly': accessState.readonly || undefined,
15306
+ 'data-disabled': accessState.disabled || undefined,
15307
+ 'data-readonly': accessState.readonly || undefined
15308
+ };
15309
+ const { className: consumerClassName, // `spacing` is MUI's name for this axis and the one consumers reach
15310
+ // for first (kensaadi/dashforge#63 gap K). Pulled out of the spread
15311
+ // rather than passed through: React 19 writes an unknown lowercase
15312
+ // attribute to the DOM without a word, so `spacing={2}` would ship as
15313
+ // `spacing="2"` on the element while doing nothing to the layout.
15314
+ // That is BUG 9's failure mode, and the warning below is the signal
15315
+ // the consumer would otherwise never get.
15316
+ spacing: spacingAlias } = rest, safeRest = _object_without_properties_loose(rest, [
15317
+ "className",
15318
+ "spacing"
14666
15319
  ]);
15320
+ if (process.env.NODE_ENV !== 'production' && spacingAlias !== undefined && !warnedSpacingAlias) {
15321
+ warnedSpacingAlias = true;
15322
+ console.warn(`[@dashforge/tw] <Stack spacing={${JSON.stringify(spacingAlias)}}> ` + `has no effect: this component calls the axis \`gap\`. The value ` + `is dropped rather than forwarded, so it does not reach the DOM ` + `either. Use gap={${JSON.stringify(spacingAlias)}}.`);
15323
+ }
14667
15324
  const classes = cn(stackVariants({
14668
15325
  direction,
14669
15326
  align,
@@ -14680,22 +15337,24 @@ Typography.displayName = 'Typography';
14680
15337
  wrap,
14681
15338
  fullWidth,
14682
15339
  fullHeight
14683
- }), consumerClassName, sx);
15340
+ }), // RBAC-denied surfaces dim and carry the ARIA attributes assigned
15341
+ // below, so descendants and assistive tech can react.
15342
+ accessState.disabled && 'opacity-60', accessState.readonly && 'opacity-80', consumerClassName, sx);
14684
15343
  if (asChild) {
14685
15344
  // Slot expects a single child; divider has no place here.
14686
15345
  return jsx(Slot, _extends({
14687
15346
  ref: ref,
14688
15347
  className: classes
14689
- }, safeRest, {
15348
+ }, ariaProps, safeRest, {
14690
15349
  children: children
14691
15350
  }));
14692
15351
  }
14693
15352
  const Tag = as != null ? as : 'div';
14694
- const content = divider != null ? interleaveDividers(children, divider) : children;
15353
+ const content = divider != null ? interleaveDividers(children, divider, direction != null ? direction : 'col') : children;
14695
15354
  return jsx(Tag, _extends({
14696
15355
  ref: ref,
14697
15356
  className: classes
14698
- }, safeRest, {
15357
+ }, ariaProps, safeRest, {
14699
15358
  children: content
14700
15359
  }));
14701
15360
  });
@@ -14925,13 +15584,34 @@ Stack.displayName = 'Stack';
14925
15584
  // branches of the discriminated union.
14926
15585
  const themeDefaults = useComponentDefaults('Grid');
14927
15586
  const props = _extends({}, themeDefaults == null ? void 0 : themeDefaults.defaults, _props);
14928
- const { as, asChild = false, sx, children, className: consumerClassName } = props, rest = _object_without_properties_loose(props, [
15587
+ const { as, asChild = false, sx, visibleWhen, access, children, className: consumerClassName } = props, rest = _object_without_properties_loose(props, [
14929
15588
  "as",
14930
15589
  "asChild",
14931
15590
  "sx",
15591
+ "visibleWhen",
15592
+ "access",
14932
15593
  "children",
14933
15594
  "className"
14934
15595
  ]);
15596
+ // Bridge — hooks called unconditionally, ABOVE the early return and
15597
+ // above the union branching (rules-of-hooks; see README-BUG § BUG 33
15598
+ // for what a conditional subscription costs). Both are no-ops when
15599
+ // their prop is absent, so a consumer passing neither renders exactly
15600
+ // as before. Destructured out of `rest` so neither can reach the DOM.
15601
+ const bridge = useContext(DashFormContext);
15602
+ const isVisible = useEngineVisibility(bridge == null ? void 0 : bridge.engine, visibleWhen);
15603
+ const accessState = useAccessState(access);
15604
+ // Early return — predicate false OR RBAC denies visibility.
15605
+ if (!isVisible || !accessState.visible) return null;
15606
+ // RBAC-denied surfaces dim and carry the ARIA attributes below, so
15607
+ // descendants and assistive tech can react.
15608
+ const accessClasses = cn(accessState.disabled && 'opacity-60', accessState.readonly && 'opacity-80');
15609
+ const ariaProps = {
15610
+ 'aria-disabled': accessState.disabled || undefined,
15611
+ 'aria-readonly': accessState.readonly || undefined,
15612
+ 'data-disabled': accessState.disabled || undefined,
15613
+ 'data-readonly': accessState.readonly || undefined
15614
+ };
14935
15615
  // #112 (G-28): salvage a stray untyped `className` that snuck in via
14936
15616
  // spread props so the JSX spread doesn't clobber the variant chain
14937
15617
  // via last-wins prop override. Merged through `cn` in both branches
@@ -14964,7 +15644,7 @@ Stack.displayName = 'Stack';
14964
15644
  spacingX: containerPayload.spacingX,
14965
15645
  spacingY: containerPayload.spacingY,
14966
15646
  autoFlow: containerPayload.autoFlow
14967
- }), consumerClassName, sx);
15647
+ }), accessClasses, consumerClassName, sx);
14968
15648
  } else {
14969
15649
  var _itemPayload_xs;
14970
15650
  itemPayload = props;
@@ -14977,7 +15657,7 @@ Stack.displayName = 'Stack';
14977
15657
  md: itemPayload.md,
14978
15658
  lg: itemPayload.lg,
14979
15659
  xl: itemPayload.xl
14980
- }), consumerClassName, sx);
15660
+ }), accessClasses, consumerClassName, sx);
14981
15661
  }
14982
15662
  /*
14983
15663
  * Strip role-specific props from `rest` before spreading on the DOM
@@ -15004,7 +15684,7 @@ Stack.displayName = 'Stack';
15004
15684
  return jsx(Slot, _extends({
15005
15685
  ref: ref,
15006
15686
  className: classes
15007
- }, domRest, {
15687
+ }, ariaProps, domRest, {
15008
15688
  children: children
15009
15689
  }));
15010
15690
  }
@@ -15012,7 +15692,7 @@ Stack.displayName = 'Stack';
15012
15692
  return jsx(Tag, _extends({
15013
15693
  ref: ref,
15014
15694
  className: classes
15015
- }, domRest, {
15695
+ }, ariaProps, domRest, {
15016
15696
  children: children
15017
15697
  }));
15018
15698
  });
@@ -15218,13 +15898,29 @@ Container.displayName = 'Container';
15218
15898
  * border-* color token to the intent.
15219
15899
  *
15220
15900
  * The `segment` axis distinguishes whether this is a line-only render
15221
- * (full width) vs a labeled-mode segment (flex-1 grows to share space).
15901
+ * (spans its own main axis) vs a labeled-mode segment (flex-1 grows to
15902
+ * share space).
15903
+ *
15904
+ * `full` means "span the divider's OWN main axis", which is the width
15905
+ * for a horizontal rule and the HEIGHT for a vertical one. It is
15906
+ * therefore orientation-dependent and lives in `compoundVariants`, not
15907
+ * on the plain axis. See README-BUG § BUG 25: an unconditional
15908
+ * `w-full` here fought the `w-0` from the orientation axis, tailwind-
15909
+ * merge kept the later `w-full`, and every vertical line-only divider
15910
+ * came out as a full-width bar with a left border.
15222
15911
  */ const dividerLineVariants = tv({
15223
15912
  base: '',
15224
15913
  variants: {
15225
15914
  orientation: {
15226
15915
  horizontal: 'h-0 border-t',
15227
- vertical: 'w-0 border-l self-stretch'
15916
+ /*
15917
+ * `w-0` does double duty and must NOT be dropped:
15918
+ * - in a `flex-row` parent it is the main-axis size, so the
15919
+ * visible width is exactly the 1px `border-l`;
15920
+ * - in a `flex-col` parent (labeled vertical mode) it is the
15921
+ * CROSS size, and being definite it stops `self-stretch` from
15922
+ * stretching the rule to the full width.
15923
+ */ vertical: 'w-0 border-l self-stretch'
15228
15924
  },
15229
15925
  variant: {
15230
15926
  solid: 'border-solid',
@@ -15242,13 +15938,30 @@ Container.displayName = 'Container';
15242
15938
  info: 'border-info-300 dark:border-info-700'
15243
15939
  },
15244
15940
  /*
15245
- * `segment` controls whether the line spans full width (line-only
15246
- * mode) or grows to fill space (labeled-mode flex segment).
15941
+ * `segment` controls whether the line spans its own main axis
15942
+ * (line-only mode) or grows to fill space (labeled-mode flex
15943
+ * segment).
15944
+ *
15945
+ * `full` is empty here on purpose: what "full" resolves to depends
15946
+ * on the orientation, so it is emitted from `compoundVariants`.
15947
+ * `grow` is genuinely orientation-agnostic — `flex-1` grows along
15948
+ * whichever main axis the parent sets — so it stays on the axis.
15247
15949
  */ segment: {
15248
- full: 'w-full',
15950
+ full: '',
15249
15951
  grow: 'flex-1'
15250
15952
  }
15251
15953
  },
15954
+ compoundVariants: [
15955
+ /*
15956
+ * Horizontal: `w-full` is load-bearing. A block child does NOT fill
15957
+ * the width once it is a flex item, which is exactly the toolbar
15958
+ * case, so the rule would collapse without this.
15959
+ */ {
15960
+ orientation: 'horizontal',
15961
+ segment: 'full',
15962
+ class: 'w-full'
15963
+ }
15964
+ ],
15252
15965
  defaultVariants: {
15253
15966
  orientation: 'horizontal',
15254
15967
  variant: 'solid',
@@ -15556,7 +16269,11 @@ VisuallyHidden.displayName = 'VisuallyHidden';
15556
16269
  'absolute top-4 right-4 inline-flex items-center justify-center',
15557
16270
  'h-8 w-8 rounded-md text-neutral-500 hover:text-neutral-900',
15558
16271
  'hover:bg-neutral-100',
15559
- 'focus:outline-none focus:ring-2 focus:ring-primary-500',
16272
+ // `focus-visible:`, not `focus:` — Radix moves focus into the panel
16273
+ // on open and this button is the first focusable thing in it, so a
16274
+ // plain `focus:ring` painted a ring on every mouse-opened dialog.
16275
+ // BUG 28. Matches the catalog's dominant form (9 other slots).
16276
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
15560
16277
  'transition-colors'
15561
16278
  ],
15562
16279
  body: 'flex-1 overflow-y-auto',
@@ -15911,8 +16628,12 @@ const accordionVariants = tv({
15911
16628
  trigger: [
15912
16629
  'flex flex-1 items-center justify-between py-3 text-left',
15913
16630
  'text-sm font-medium text-neutral-900',
15914
- 'transition-all hover:underline',
15915
- 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:rounded',
16631
+ // `transition-all` covers transform-able properties, so it is
16632
+ // motion under WCAG 2.3.3 and needs the gate. Every other
16633
+ // `transition-transform` in the catalog has one; this was the
16634
+ // only movement left ungated.
16635
+ 'transition-all hover:underline motion-reduce:transition-none',
16636
+ 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:rounded-sm',
15916
16637
  '[&[data-state=open]>svg]:rotate-180',
15917
16638
  'disabled:opacity-50 disabled:pointer-events-none',
15918
16639
  'motion-reduce:transition-none'
@@ -17580,7 +18301,7 @@ function Row(props) {
17580
18301
  "aria-label": isExpanded ? labels.ariaCollapseRow : labels.ariaExpandRow,
17581
18302
  "aria-expanded": isExpanded,
17582
18303
  disabled: !isInteractive,
17583
- className: cn('inline-flex items-center justify-center h-6 w-6 rounded', // text-neutral-500 + hover bump; both auto-invert via CSS-var preset.
18304
+ className: cn('inline-flex items-center justify-center h-6 w-6 rounded-md', // text-neutral-500 + hover bump; both auto-invert via CSS-var preset.
17584
18305
  'text-neutral-500 hover:text-neutral-900', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500', 'transition-transform motion-reduce:transition-none', isExpanded && 'rotate-90'),
17585
18306
  children: jsx(ChevronRightIcon, {})
17586
18307
  })
@@ -18327,7 +19048,7 @@ function ColumnFilterTrigger(props) {
18327
19048
  "aria-label": labels.filterColumn,
18328
19049
  "aria-pressed": active,
18329
19050
  disabled: disabled,
18330
- className: cn('inline-flex items-center justify-center h-6 w-6 rounded', 'text-neutral-500 hover:text-neutral-900', active && 'text-primary-700', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500', 'transition-colors motion-reduce:transition-none', 'disabled:opacity-50 disabled:cursor-not-allowed'),
19051
+ className: cn('inline-flex items-center justify-center h-6 w-6 rounded-md', 'text-neutral-500 hover:text-neutral-900', active && 'text-primary-700', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500', 'transition-colors motion-reduce:transition-none', 'disabled:opacity-50 disabled:cursor-not-allowed'),
18331
19052
  children: jsx(FilterIcon, {})
18332
19053
  })
18333
19054
  });
@@ -18709,7 +19430,7 @@ function ColumnVisibilityTrigger(props) {
18709
19430
  const header = typeof col.header === 'function' ? col.header() : col.header;
18710
19431
  return jsx("li", {
18711
19432
  children: jsxs("label", {
18712
- className: "flex items-center gap-2 cursor-pointer py-1 px-1 rounded hover:bg-neutral-100",
19433
+ className: "flex items-center gap-2 cursor-pointer py-1 px-1 rounded-md hover:bg-neutral-100",
18713
19434
  children: [
18714
19435
  jsx("input", {
18715
19436
  type: "checkbox",
@@ -19803,4 +20524,4 @@ function SearchIcon(props) {
19803
20524
  * Package version (synced with `package.json` at publish time).
19804
20525
  */ const VERSION = '1.5.2';
19805
20526
 
19806
- export { AVATAR_SOFT_FALLBACK, AVATAR_TONE_FALLBACK, Accordion, Alert, AlertTitle, AppShell, AspectRatio, Autocomplete, Avatar, AvatarGroup, Badge, Box, Breadcrumbs, Button, Calendar, Card, CardActionArea, CardContent, Checkbox, Chip, ConfirmDialogProvider, Container, DRAWER_SIZE_PRESETS, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Drawer, Grid, ICON_BUTTON_BASE, ICON_BUTTON_SIZE_OVERRIDES, IconButton, Image, LeftNav, Link, Menu, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuSkeleton, MenuTrigger, NumberField, OTPField, Pagination, Popover, Progress, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, SPINNER_COLORS, SPINNER_STROKE_WIDTH, SPINNER_TRACK_OPACITY, Select, Skeleton, Slider, SnackbarProvider, Spinner, Stack, Step, Stepper, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, Typography, VERSION, Video, VisuallyHidden, accordionVariants, alertVariants, appShellVariants, autocompleteVariants, avatarVariants, badgeVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, chipVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, drawerVariants, getNestedValue, gridVariants, imageVariants, leftNavVariants, linkVariants, menuVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, progressVariants, radioGroupVariants, selectVariants, skeletonVariants, sliderVariants, snackbarVariants, spinnerVariants, stackVariants, stepperVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar, useStep, videoVariants };
20527
+ export { AVATAR_SOFT_FALLBACK, AVATAR_TONE_FALLBACK, Accordion, Alert, AlertTitle, AppShell, AspectRatio, Autocomplete, Avatar, AvatarGroup, Badge, Box, Breadcrumbs, Button, Calendar, Card, CardActionArea, CardContent, Checkbox, Chip, ConfirmDialogProvider, Container, DRAWER_SIZE_PRESETS, DataGrid, DatePicker, DateRangePicker, DateTimePicker, Dialog, Divider, Drawer, Grid, ICON_BUTTON_BASE, ICON_BUTTON_SIZE_OVERRIDES, IconButton, Image, Kbd, LeftNav, Link, Menu, MenuContent, MenuItem, MenuLabel, MenuSeparator, MenuSkeleton, MenuTrigger, NumberField, OTPField, Pagination, Popover, Progress, RadioGroup, RenderButton, RenderChip, RenderText, RenderTwoLine, RowActionsMenu, SPINNER_COLORS, SPINNER_STROKE_WIDTH, SPINNER_TRACK_OPACITY, Select, Skeleton, Slider, SnackbarProvider, Spinner, Stack, Step, Stepper, Switch, Table, Tabs, TextField, Textarea, TimePicker, Tooltip, TopBar, TopBarBrand, Typography, VERSION, Video, VisuallyHidden, accordionVariants, alertVariants, appShellVariants, autocompleteVariants, avatarVariants, badgeVariants, boxVariants, breadcrumbsVariants, buttonVariants, calendarDayVariants, calendarVariants, checkboxVariants, chipVariants, cn, confirmDialogVariants, containerVariants, dataGridVariants, datePickerVariants, dateRangeDayVariants, dateRangePickerVariants, dateTimePickerVariants, dialogVariants, dividerLineVariants, dividerVariants, drawerVariants, getNestedValue, gridVariants, imageVariants, kbdVariants, leftNavVariants, linkVariants, menuVariants, numberFieldVariants, otpFieldVariants, paginationVariants, popoverVariants, progressVariants, radioGroupVariants, selectVariants, skeletonVariants, sliderVariants, snackbarVariants, spinnerVariants, stackVariants, stepperVariants, switchVariants, tableVariants, tabsVariants, textFieldVariants, textareaVariants, timePickerVariants, tooltipVariants, topBarVariants, typographyVariants, useAccessState, useConfirm, useSnackbar, useStep, videoVariants };