@eifi1/ui-kit 0.48.2 → 0.50.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 (377) hide show
  1. package/README.md +34 -16
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +24 -12
  7. package/dist/admin/user-roster.js +21 -6
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.d.ts +9 -1
  16. package/dist/auth/verify-email.js +5 -3
  17. package/dist/auth/verify-email.js.map +1 -1
  18. package/dist/billing/plan-card.js +2 -2
  19. package/dist/billing/plan-card.js.map +1 -1
  20. package/dist/components/amount-input.d.ts +2 -1
  21. package/dist/components/amount-input.js +3 -2
  22. package/dist/components/amount-input.js.map +1 -1
  23. package/dist/components/autocomplete.js +1 -0
  24. package/dist/components/autocomplete.js.map +1 -1
  25. package/dist/components/breadcrumbs.d.ts +19 -1
  26. package/dist/components/breadcrumbs.js +11 -4
  27. package/dist/components/breadcrumbs.js.map +1 -1
  28. package/dist/components/button-look.d.ts +37 -7
  29. package/dist/components/button-look.js +33 -6
  30. package/dist/components/button-look.js.map +1 -1
  31. package/dist/components/button.d.ts +4 -1
  32. package/dist/components/button.js +2 -2
  33. package/dist/components/button.js.map +1 -1
  34. package/dist/components/calendar-heatmap.js +2 -1
  35. package/dist/components/calendar-heatmap.js.map +1 -1
  36. package/dist/components/chart-edge.d.ts +10 -0
  37. package/dist/components/chart-edge.js +57 -0
  38. package/dist/components/chart-edge.js.map +1 -0
  39. package/dist/components/chart.js +2 -1
  40. package/dist/components/chart.js.map +1 -1
  41. package/dist/components/chip.d.ts +8 -1
  42. package/dist/components/chip.js +4 -1
  43. package/dist/components/chip.js.map +1 -1
  44. package/dist/components/combobox-core.js +1 -0
  45. package/dist/components/combobox-core.js.map +1 -1
  46. package/dist/components/combobox-suggestions.js +1 -0
  47. package/dist/components/combobox-suggestions.js.map +1 -1
  48. package/dist/components/confirm-context.d.ts +5 -0
  49. package/dist/components/confirm-context.js +7 -0
  50. package/dist/components/confirm-context.js.map +1 -0
  51. package/dist/components/confirm-dialog.js +3 -3
  52. package/dist/components/confirm-dialog.js.map +1 -1
  53. package/dist/components/currency-select.d.ts +28 -2
  54. package/dist/components/currency-select.js +56 -10
  55. package/dist/components/currency-select.js.map +1 -1
  56. package/dist/components/danger-confirm.d.ts +2 -2
  57. package/dist/components/danger-confirm.js +1 -1
  58. package/dist/components/danger-confirm.js.map +1 -1
  59. package/dist/components/data-table-fit.js +21 -3
  60. package/dist/components/data-table-fit.js.map +1 -1
  61. package/dist/components/data-table-mobile.d.ts +58 -3
  62. package/dist/components/data-table-mobile.js +147 -16
  63. package/dist/components/data-table-mobile.js.map +1 -1
  64. package/dist/components/data-table-row-actions.d.ts +2 -1
  65. package/dist/components/data-table-row-actions.js +7 -0
  66. package/dist/components/data-table-row-actions.js.map +1 -1
  67. package/dist/components/data-table-row.d.ts +1 -1
  68. package/dist/components/data-table-row.js.map +1 -1
  69. package/dist/components/data-table.d.ts +34 -1
  70. package/dist/components/data-table.js +37 -10
  71. package/dist/components/data-table.js.map +1 -1
  72. package/dist/components/dropdown.js +1 -0
  73. package/dist/components/dropdown.js.map +1 -1
  74. package/dist/components/error-boundary.js +2 -2
  75. package/dist/components/error-boundary.js.map +1 -1
  76. package/dist/components/floating-panel.d.ts +36 -13
  77. package/dist/components/floating-panel.js +27 -7
  78. package/dist/components/floating-panel.js.map +1 -1
  79. package/dist/components/form-actions.d.ts +1 -1
  80. package/dist/components/form-actions.js +1 -1
  81. package/dist/components/form-actions.js.map +1 -1
  82. package/dist/components/full-bleed-dialog.js +1 -0
  83. package/dist/components/full-bleed-dialog.js.map +1 -1
  84. package/dist/components/icon-button.d.ts +11 -0
  85. package/dist/components/icon-button.js +7 -5
  86. package/dist/components/icon-button.js.map +1 -1
  87. package/dist/components/modal.js +1 -0
  88. package/dist/components/modal.js.map +1 -1
  89. package/dist/components/nav-pills.d.ts +8 -1
  90. package/dist/components/nav-pills.js +61 -38
  91. package/dist/components/nav-pills.js.map +1 -1
  92. package/dist/components/number-input.d.ts +2 -1
  93. package/dist/components/number-input.js +6 -2
  94. package/dist/components/number-input.js.map +1 -1
  95. package/dist/components/numpad-sheet.js +2 -0
  96. package/dist/components/numpad-sheet.js.map +1 -1
  97. package/dist/components/page-header-context.d.ts +8 -0
  98. package/dist/components/page-header-context.js +7 -0
  99. package/dist/components/page-header-context.js.map +1 -0
  100. package/dist/components/page-header.d.ts +14 -2
  101. package/dist/components/page-header.js +10 -1
  102. package/dist/components/page-header.js.map +1 -1
  103. package/dist/components/picker-sheet.js +2 -0
  104. package/dist/components/picker-sheet.js.map +1 -1
  105. package/dist/components/pie-chart.js +2 -1
  106. package/dist/components/pie-chart.js.map +1 -1
  107. package/dist/components/popover.js +1 -0
  108. package/dist/components/popover.js.map +1 -1
  109. package/dist/components/progress-bar.js +5 -4
  110. package/dist/components/progress-bar.js.map +1 -1
  111. package/dist/components/reauth-dialog.js +1 -1
  112. package/dist/components/reauth-dialog.js.map +1 -1
  113. package/dist/components/row-actions-labels.js +3 -1
  114. package/dist/components/row-actions-labels.js.map +1 -1
  115. package/dist/components/row-actions.d.ts +43 -9
  116. package/dist/components/row-actions.js +78 -15
  117. package/dist/components/row-actions.js.map +1 -1
  118. package/dist/components/section-nav.d.ts +37 -0
  119. package/dist/components/section-nav.js +33 -0
  120. package/dist/components/section-nav.js.map +1 -0
  121. package/dist/components/series-chart.js +8 -3
  122. package/dist/components/series-chart.js.map +1 -1
  123. package/dist/components/share-card.js +1 -1
  124. package/dist/components/share-card.js.map +1 -1
  125. package/dist/components/signature-pad.js +1 -1
  126. package/dist/components/signature-pad.js.map +1 -1
  127. package/dist/components/sparkline.js +2 -1
  128. package/dist/components/sparkline.js.map +1 -1
  129. package/dist/components/stat-tile.js +14 -7
  130. package/dist/components/stat-tile.js.map +1 -1
  131. package/dist/components/status-dot.d.ts +5 -1
  132. package/dist/components/status-dot.js +4 -1
  133. package/dist/components/status-dot.js.map +1 -1
  134. package/dist/components/tooltip.js +1 -1
  135. package/dist/components/tooltip.js.map +1 -1
  136. package/dist/components/translation-review-editor.js +2 -2
  137. package/dist/components/translation-review-editor.js.map +1 -1
  138. package/dist/components/translation-review.js +2 -2
  139. package/dist/components/translation-review.js.map +1 -1
  140. package/dist/components/treemap.d.ts +5 -2
  141. package/dist/components/treemap.js +26 -6
  142. package/dist/components/treemap.js.map +1 -1
  143. package/dist/components/use-table-state.js +37 -6
  144. package/dist/components/use-table-state.js.map +1 -1
  145. package/dist/data-table.js.map +1 -1
  146. package/dist/demo/demo-banner.js +1 -1
  147. package/dist/demo/demo-banner.js.map +1 -1
  148. package/dist/demo/demo-ended.js +1 -1
  149. package/dist/demo/demo-ended.js.map +1 -1
  150. package/dist/demo/demo-start.js +1 -1
  151. package/dist/demo/demo-start.js.map +1 -1
  152. package/dist/eslint/index.js +1 -1
  153. package/dist/feedback/feedback-inbox.js +1 -1
  154. package/dist/feedback/feedback-inbox.js.map +1 -1
  155. package/dist/hooks/use-dismiss.d.ts +9 -1
  156. package/dist/hooks/use-dismiss.js +25 -5
  157. package/dist/hooks/use-dismiss.js.map +1 -1
  158. package/dist/hooks/use-document-title.d.ts +14 -0
  159. package/dist/hooks/use-document-title.js +22 -0
  160. package/dist/hooks/use-document-title.js.map +1 -0
  161. package/dist/i18n/defaults.js +2 -0
  162. package/dist/i18n/defaults.js.map +1 -1
  163. package/dist/i18n/german.js +20 -3
  164. package/dist/i18n/german.js.map +1 -1
  165. package/dist/i18n/kit-labels.d.ts +34 -1
  166. package/dist/i18n/kit-labels.js +43 -2
  167. package/dist/i18n/kit-labels.js.map +1 -1
  168. package/dist/i18n/locales/en.js +22 -3
  169. package/dist/i18n/locales/en.js.map +3 -3
  170. package/dist/i18n/locales/es.js +20 -3
  171. package/dist/i18n/locales/es.js.map +1 -1
  172. package/dist/i18n/locales/fr.js +20 -3
  173. package/dist/i18n/locales/fr.js.map +1 -1
  174. package/dist/i18n/locales/hu.js +20 -3
  175. package/dist/i18n/locales/hu.js.map +1 -1
  176. package/dist/i18n/locales/it.js +20 -3
  177. package/dist/i18n/locales/it.js.map +1 -1
  178. package/dist/i18n/locales/zh.js +20 -3
  179. package/dist/i18n/locales/zh.js.map +1 -1
  180. package/dist/i18n/review.js +3 -0
  181. package/dist/i18n/review.js.map +1 -1
  182. package/dist/i18n/shared-labels.js +1 -0
  183. package/dist/i18n/shared-labels.js.map +1 -1
  184. package/dist/index.d.ts +8 -1
  185. package/dist/index.js +11 -0
  186. package/dist/index.js.map +1 -1
  187. package/dist/landing/landing-actions.js +2 -2
  188. package/dist/landing/landing-actions.js.map +1 -1
  189. package/dist/landing/public-header.js +2 -2
  190. package/dist/landing/public-header.js.map +1 -1
  191. package/dist/lib/clipboard-minus.d.ts +27 -0
  192. package/dist/lib/clipboard-minus.js +63 -0
  193. package/dist/lib/clipboard-minus.js.map +1 -0
  194. package/dist/lib/data-attributes.d.ts +11 -0
  195. package/dist/lib/data-attributes.js +10 -0
  196. package/dist/lib/data-attributes.js.map +1 -0
  197. package/dist/lib/document-title.d.ts +17 -0
  198. package/dist/lib/document-title.js +29 -0
  199. package/dist/lib/document-title.js.map +1 -0
  200. package/dist/lib/float-clearance.js +8 -2
  201. package/dist/lib/float-clearance.js.map +1 -1
  202. package/dist/lib/format.d.ts +36 -10
  203. package/dist/lib/format.js +57 -11
  204. package/dist/lib/format.js.map +1 -1
  205. package/dist/lib/minus.d.ts +32 -0
  206. package/dist/lib/minus.js +14 -0
  207. package/dist/lib/minus.js.map +1 -0
  208. package/dist/lib/status-tone.d.ts +30 -0
  209. package/dist/lib/status-tone.js +17 -0
  210. package/dist/lib/status-tone.js.map +1 -0
  211. package/dist/settings/settings-layout.js +3 -2
  212. package/dist/settings/settings-layout.js.map +1 -1
  213. package/dist/shell/account-menu-language.d.ts +1 -1
  214. package/dist/shell/account-menu-language.js +9 -1
  215. package/dist/shell/account-menu-language.js.map +1 -1
  216. package/dist/shell/app-shell-labels.js +2 -1
  217. package/dist/shell/app-shell-labels.js.map +1 -1
  218. package/dist/shell/app-shell.d.ts +58 -16
  219. package/dist/shell/app-shell.js +151 -43
  220. package/dist/shell/app-shell.js.map +1 -1
  221. package/dist/shell/language-code.d.ts +3 -0
  222. package/dist/shell/language-code.js +8 -0
  223. package/dist/shell/language-code.js.map +1 -0
  224. package/dist/shell/topbar-controls.d.ts +14 -6
  225. package/dist/shell/topbar-controls.js +14 -17
  226. package/dist/shell/topbar-controls.js.map +1 -1
  227. package/dist/testing/css-variables.generated.d.ts +1 -1
  228. package/dist/testing/css-variables.generated.js +1 -1
  229. package/dist/testing/css-variables.generated.js.map +1 -1
  230. package/dist/testing/react.d.ts +25 -0
  231. package/dist/testing/react.js +19 -0
  232. package/dist/testing/react.js.map +1 -0
  233. package/dist/theme/chart-palette.d.ts +82 -15
  234. package/dist/theme/chart-palette.js +51 -24
  235. package/dist/theme/chart-palette.js.map +1 -1
  236. package/dist/theme/palette-presets.d.ts +9 -0
  237. package/dist/theme/palette-presets.js +77 -62
  238. package/dist/theme/palette-presets.js.map +1 -1
  239. package/dist/tour/tour-dock.d.ts +42 -0
  240. package/dist/tour/tour-dock.js +80 -0
  241. package/dist/tour/tour-dock.js.map +1 -0
  242. package/dist/tour/tour-gates.d.ts +71 -0
  243. package/dist/tour/tour-gates.js +114 -0
  244. package/dist/tour/tour-gates.js.map +1 -0
  245. package/dist/tour/tour-guide-button.d.ts +27 -0
  246. package/dist/tour/tour-guide-button.js +45 -0
  247. package/dist/tour/tour-guide-button.js.map +1 -0
  248. package/dist/tour/tour-guide-labels.d.ts +8 -0
  249. package/dist/tour/tour-guide-labels.js +9 -0
  250. package/dist/tour/tour-guide-labels.js.map +1 -0
  251. package/dist/tour/tour-labels.js +9 -1
  252. package/dist/tour/tour-labels.js.map +1 -1
  253. package/dist/tour/tour.d.ts +133 -9
  254. package/dist/tour/tour.js +594 -70
  255. package/dist/tour/tour.js.map +1 -1
  256. package/dist/tour/use-tour-url-sync.d.ts +5 -2
  257. package/dist/tour/use-tour-url-sync.js +7 -1
  258. package/dist/tour/use-tour-url-sync.js.map +1 -1
  259. package/dist/tour.d.ts +2 -0
  260. package/dist/tour.js +3 -0
  261. package/dist/tour.js.map +1 -1
  262. package/dist/wizard/stepper-nav.d.ts +1 -1
  263. package/dist/wizard/stepper-nav.js +18 -5
  264. package/dist/wizard/stepper-nav.js.map +1 -1
  265. package/dist/wizard/types.d.ts +11 -0
  266. package/dist/wizard/types.js.map +1 -1
  267. package/dist/wizard/use-wizard.js +11 -5
  268. package/dist/wizard/use-wizard.js.map +1 -1
  269. package/package.json +9 -1
  270. package/src/admin/admin-action-confirm.tsx +1 -1
  271. package/src/admin/invitations-panel.tsx +7 -3
  272. package/src/admin/user-roster.tsx +41 -14
  273. package/src/auth/accept-invitation.tsx +3 -3
  274. package/src/auth/not-found-page.tsx +1 -1
  275. package/src/auth/reset-password-form.tsx +3 -3
  276. package/src/auth/verify-email.tsx +13 -3
  277. package/src/billing/plan-card.tsx +2 -2
  278. package/src/components/amount-input.tsx +5 -4
  279. package/src/components/autocomplete.tsx +2 -0
  280. package/src/components/breadcrumbs.tsx +32 -4
  281. package/src/components/button-look.ts +86 -15
  282. package/src/components/button.tsx +6 -3
  283. package/src/components/calendar-heatmap.tsx +2 -1
  284. package/src/components/chart-edge.ts +73 -0
  285. package/src/components/chart.tsx +3 -1
  286. package/src/components/chip.tsx +10 -1
  287. package/src/components/combobox-core.tsx +2 -0
  288. package/src/components/combobox-suggestions.tsx +2 -0
  289. package/src/components/confirm-context.ts +7 -0
  290. package/src/components/confirm-dialog.tsx +3 -3
  291. package/src/components/currency-select.tsx +95 -6
  292. package/src/components/danger-confirm.tsx +3 -3
  293. package/src/components/data-table-fit.ts +41 -6
  294. package/src/components/data-table-mobile.tsx +259 -17
  295. package/src/components/data-table-row-actions.tsx +9 -1
  296. package/src/components/data-table-row.tsx +3 -0
  297. package/src/components/data-table.tsx +91 -19
  298. package/src/components/dropdown.tsx +2 -0
  299. package/src/components/error-boundary.tsx +2 -2
  300. package/src/components/floating-panel.tsx +67 -16
  301. package/src/components/form-actions.tsx +2 -2
  302. package/src/components/full-bleed-dialog.tsx +3 -0
  303. package/src/components/icon-button.tsx +21 -8
  304. package/src/components/modal.tsx +3 -0
  305. package/src/components/nav-pills.tsx +29 -2
  306. package/src/components/number-input.tsx +8 -3
  307. package/src/components/numpad-sheet.tsx +4 -0
  308. package/src/components/page-header-context.ts +8 -0
  309. package/src/components/page-header.tsx +34 -2
  310. package/src/components/picker-sheet.tsx +4 -0
  311. package/src/components/pie-chart.tsx +2 -1
  312. package/src/components/popover.tsx +2 -0
  313. package/src/components/progress-bar.tsx +6 -3
  314. package/src/components/reauth-dialog.tsx +1 -1
  315. package/src/components/row-actions-labels.ts +2 -0
  316. package/src/components/row-actions.tsx +154 -33
  317. package/src/components/section-nav.tsx +65 -0
  318. package/src/components/series-chart.tsx +12 -3
  319. package/src/components/share-card.tsx +1 -1
  320. package/src/components/signature-pad.tsx +1 -1
  321. package/src/components/sparkline.tsx +2 -1
  322. package/src/components/stat-tile.tsx +19 -7
  323. package/src/components/status-dot.tsx +7 -1
  324. package/src/components/tooltip.tsx +3 -1
  325. package/src/components/translation-review-editor.tsx +2 -2
  326. package/src/components/translation-review.tsx +2 -2
  327. package/src/components/treemap.tsx +34 -8
  328. package/src/components/use-table-state.ts +63 -8
  329. package/src/data-table.ts +3 -3
  330. package/src/demo/demo-banner.tsx +1 -1
  331. package/src/demo/demo-ended.tsx +1 -1
  332. package/src/demo/demo-start.tsx +1 -1
  333. package/src/feedback/feedback-inbox.tsx +1 -1
  334. package/src/hooks/use-dismiss.ts +43 -4
  335. package/src/hooks/use-document-title.ts +31 -0
  336. package/src/i18n/defaults.ts +2 -0
  337. package/src/i18n/german.ts +17 -0
  338. package/src/i18n/kit-labels.tsx +79 -1
  339. package/src/i18n/locales/es.ts +17 -0
  340. package/src/i18n/locales/fr.ts +17 -0
  341. package/src/i18n/locales/hu.ts +17 -0
  342. package/src/i18n/locales/it.ts +17 -0
  343. package/src/i18n/locales/zh.ts +17 -0
  344. package/src/i18n/review.ts +3 -0
  345. package/src/i18n/shared-labels.ts +1 -0
  346. package/src/index.ts +13 -0
  347. package/src/landing/landing-actions.tsx +2 -2
  348. package/src/landing/public-header.tsx +2 -2
  349. package/src/lib/clipboard-minus.ts +94 -0
  350. package/src/lib/data-attributes.ts +14 -0
  351. package/src/lib/document-title.ts +41 -0
  352. package/src/lib/float-clearance.ts +19 -3
  353. package/src/lib/format.ts +117 -30
  354. package/src/lib/minus.ts +50 -0
  355. package/src/lib/status-tone.ts +43 -0
  356. package/src/settings/settings-layout.tsx +3 -2
  357. package/src/shell/account-menu-language.tsx +14 -2
  358. package/src/shell/app-shell-labels.ts +1 -0
  359. package/src/shell/app-shell.tsx +291 -68
  360. package/src/shell/language-code.ts +5 -0
  361. package/src/shell/topbar-controls.tsx +35 -15
  362. package/src/testing/css-variables.generated.ts +6 -6
  363. package/src/testing/react.ts +59 -0
  364. package/src/theme/chart-palette.ts +102 -20
  365. package/src/theme/palette-presets.ts +121 -84
  366. package/src/tour/tour-dock.ts +132 -0
  367. package/src/tour/tour-gates.ts +195 -0
  368. package/src/tour/tour-guide-button.tsx +64 -0
  369. package/src/tour/tour-guide-labels.ts +15 -0
  370. package/src/tour/tour-labels.ts +8 -0
  371. package/src/tour/tour.tsx +949 -86
  372. package/src/tour/use-tour-url-sync.ts +14 -4
  373. package/src/tour.ts +3 -0
  374. package/src/wizard/stepper-nav.tsx +27 -7
  375. package/src/wizard/types.ts +12 -0
  376. package/src/wizard/use-wizard.ts +16 -9
  377. package/tokens.css +28 -18
@@ -15,11 +15,12 @@ import { X } from "lucide-react";
15
15
 
16
16
  import { cn } from "../lib/cn";
17
17
  import { FOCUS_RING } from "./focus-ring";
18
+ import { warnDeprecatedVariant } from "./button-look";
18
19
  import { dirOf, type Direction } from "../lib/direction";
19
20
  import { registerFloating } from "../lib/floating-stack";
20
21
  import { useFloatClearance } from "../lib/float-clearance";
21
22
  import { isComposing } from "../lib/ime";
22
- import { takeEscape } from "../hooks/use-dismiss";
23
+ import { takeEscape, useOverlayOpen } from "../hooks/use-dismiss";
23
24
  import { useKitLabels, useKitLink } from "../i18n/kit-labels";
24
25
  import { assignRef } from "./field-parts";
25
26
  import { pickLinkRenderer } from "./text-link";
@@ -137,13 +138,18 @@ export interface FloatingActionButtonProps extends Omit<ButtonHTMLAttributes<HTM
137
138
  */
138
139
  live?: boolean;
139
140
  /**
140
- * `primary` (default): the solid brand disc of a page's main action. `surface`: the
141
- * page's surface with a hairline border — for a FAB that is a status rather than a
142
- * call to action, as kastlan's offline pill is (a brand pill would shout "press me"
143
- * at a user who is merely offline). Ignored while `pressed` is set; a toggle has its
144
- * own two looks.
141
+ * `accent` (default): the solid brand disc of a page's main action, as Button's
142
+ * `accent`. `surface`: the page's surface with a hairline border — for a FAB that is a
143
+ * status rather than a call to action, as kastlan's offline pill is (a brand pill would
144
+ * shout "press me" at a user who is merely offline). Ignored while `pressed` is set; a
145
+ * toggle has its own two looks. `primary` is a deprecated alias of `accent` (0.49; a
146
+ * development build warns once), removed in 0.51.
145
147
  */
146
- variant?: "primary" | "surface";
148
+ variant?:
149
+ | "accent"
150
+ | "surface"
151
+ /** @deprecated 0.49 — use `accent` (same look); removed in 0.51. */
152
+ | "primary";
147
153
  /**
148
154
  * Hover and focus label in the kit {@link Tooltip}, instead of the native `title` —
149
155
  * keksdose has ONE tooltip (dev#523) and wrapped its assistant launcher by hand,
@@ -158,9 +164,23 @@ export interface FloatingActionButtonProps extends Omit<ButtonHTMLAttributes<HTM
158
164
  /** Where the {@link tooltip} opens. Default `top`; the bubble is portalled and kept on
159
165
  * screen, so a corner button's label is not pushed off the edge. */
160
166
  tooltipSide?: TooltipSide;
167
+ /**
168
+ * 0.49 KIT-11: step aside (as `hidden` does: off screen, the room kept) while a dialog,
169
+ * a sheet, a popover, a hover menu or a list is open, instead of floating over it or
170
+ * beside it. Default `true`; `false` keeps it on screen. A tooltip does not count.
171
+ */
172
+ hideWhenOverlay?: boolean;
161
173
  ref?: Ref<HTMLButtonElement>;
162
174
  }
163
175
 
176
+ /** FloatingActionButton's and FloatingAction's `primary` was always the solid brand
177
+ * fill: it reads as `accent` (warning once in development), never as Button's
178
+ * `default`. */
179
+ function deprecatedPrimary(component: string): "accent" {
180
+ warnDeprecatedVariant(component, "primary", "accent");
181
+ return "accent";
182
+ }
183
+
164
184
  /**
165
185
  * A round, icon-only button pinned to a corner of the viewport — the trigger of a
166
186
  * {@link FloatingPanel}, and usable alone for a page's one primary action.
@@ -195,17 +215,22 @@ export function FloatingActionButton({
195
215
  pressed,
196
216
  extended = false,
197
217
  live = false,
198
- variant = "primary",
218
+ variant: givenVariant = "accent",
199
219
  tooltip,
200
220
  tooltipSide = "top",
221
+ hideWhenOverlay = true,
201
222
  className,
202
223
  style,
203
224
  type = "button",
204
225
  title,
226
+ hidden: hiddenProp,
205
227
  ref,
206
228
  ...rest
207
229
  }: FloatingActionButtonProps) {
230
+ const overlay = useOverlayOpen(hideWhenOverlay);
231
+ const hidden = hiddenProp || overlay || undefined;
208
232
  const [marker, dir] = usePortalDir();
233
+ const variant = givenVariant === "primary" ? deprecatedPrimary("FloatingActionButton variant") : givenVariant;
209
234
  const toggle = pressed !== undefined;
210
235
  const tip = tooltip === true ? label : tooltip === false || tooltip === "" ? undefined : tooltip;
211
236
  const wrapped = tip !== undefined && tip !== null;
@@ -229,6 +254,7 @@ export function FloatingActionButton({
229
254
  const button = (
230
255
  <button
231
256
  {...rest}
257
+ hidden={wrapped ? undefined : hidden}
232
258
  ref={buttonRef}
233
259
  type={type}
234
260
  dir={dir}
@@ -243,7 +269,7 @@ export function FloatingActionButton({
243
269
  extended ? "h-12 gap-2 px-4 text-sm font-medium [&_svg]:size-5" : "size-12 [&_svg]:size-6",
244
270
  "shadow-lg transition-colors disabled:cursor-default",
245
271
  !toggle &&
246
- variant === "primary" &&
272
+ variant === "accent" &&
247
273
  "bg-brand text-brand-contrast hover:bg-brand-hover",
248
274
  !toggle &&
249
275
  variant === "surface" &&
@@ -280,7 +306,7 @@ export function FloatingActionButton({
280
306
  <div
281
307
  ref={setFixed}
282
308
  dir={dir}
283
- hidden={rest.hidden}
309
+ hidden={hidden}
284
310
  style={cornerStyle(corner, offset)}
285
311
  className="fixed z-40 inline-flex"
286
312
  >
@@ -350,6 +376,13 @@ export interface FloatingPanelProps {
350
376
  * back and shorten the page under the reader's thumb.
351
377
  */
352
378
  hidden?: boolean;
379
+ /**
380
+ * 0.49 KIT-11: the FAB steps aside while a dialog, a sheet, a popover, a hover menu or
381
+ * a list is open elsewhere (see {@link FloatingActionButtonProps.hideWhenOverlay}).
382
+ * Default `true`. An OPEN panel stays: what opens from inside it (a select, a date
383
+ * picker) is its own content.
384
+ */
385
+ hideWhenOverlay?: boolean;
353
386
  children: ReactNode;
354
387
  }
355
388
 
@@ -400,7 +433,8 @@ export function FloatingPanel({
400
433
  initialFocus,
401
434
  className,
402
435
  bodyClassName,
403
- hidden = false,
436
+ hidden: hiddenProp = false,
437
+ hideWhenOverlay = true,
404
438
  children,
405
439
  }: FloatingPanelProps) {
406
440
  const labels = useKitLabels(
@@ -410,6 +444,8 @@ export function FloatingPanel({
410
444
  );
411
445
  const [ownOpen, setOwnOpen] = useState(defaultOpen);
412
446
  const open = openProp ?? ownOpen;
447
+ const overlay = useOverlayOpen(hideWhenOverlay && !open);
448
+ const hidden = hiddenProp || overlay;
413
449
  const setOpen = (next: boolean) => {
414
450
  if (openProp === undefined) setOwnOpen(next);
415
451
  onOpenChange?.(next);
@@ -477,6 +513,7 @@ export function FloatingPanel({
477
513
  nativeTitle={fabNativeTitle}
478
514
  tooltip={fabTooltip}
479
515
  hidden={hidden || undefined}
516
+ hideWhenOverlay={false}
480
517
  aria-expanded={open}
481
518
  aria-controls={open && !hidden ? panelId : undefined}
482
519
  onClick={() => {
@@ -564,6 +601,9 @@ export interface FloatingActionGroupProps extends Omit<HTMLAttributes<HTMLDivEle
564
601
  offset?: string;
565
602
  /** {@link FloatingAction}s, in reading order. */
566
603
  children: ReactNode;
604
+ /** 0.49 KIT-11: step aside while a dialog, a sheet, a popover, a hover menu or a list
605
+ * is open — see {@link FloatingActionButtonProps.hideWhenOverlay}. Default `true`. */
606
+ hideWhenOverlay?: boolean;
567
607
  ref?: Ref<HTMLDivElement>;
568
608
  }
569
609
 
@@ -587,12 +627,15 @@ export interface FloatingActionGroupProps extends Omit<HTMLAttributes<HTMLDivEle
587
627
  export function FloatingActionGroup({
588
628
  corner = "bottom-end",
589
629
  offset = "1rem",
630
+ hideWhenOverlay = true,
631
+ hidden,
590
632
  className,
591
633
  style,
592
634
  children,
593
635
  ...rest
594
636
  }: FloatingActionGroupProps) {
595
637
  const [marker, dir] = usePortalDir();
638
+ const overlay = useOverlayOpen(hideWhenOverlay);
596
639
  // As the FAB's (keksdose dev#612): its 3rem members plus the frame's 1px borders.
597
640
  useFloatClearance(offset, "2px");
598
641
  return (
@@ -602,6 +645,7 @@ export function FloatingActionGroup({
602
645
  createPortal(
603
646
  <div
604
647
  {...rest}
648
+ hidden={hidden || overlay || undefined}
605
649
  role="group"
606
650
  dir={dir}
607
651
  data-corner={corner}
@@ -655,10 +699,16 @@ export interface FloatingActionProps extends Omit<ButtonHTMLAttributes<HTMLButto
655
699
  */
656
700
  pressedStyle?: "tint" | "plain";
657
701
  /**
658
- * `primary`: the brand-filled member, for the group's main action (the /transactions
659
- * "+"). Default: the quiet one.
702
+ * `accent`: the brand-filled member, for the group's main action (the /transactions
703
+ * "+"), as Button's `accent`. `default` (default): the quiet one. `primary` is a
704
+ * deprecated alias of `accent` (0.49; a development build warns once), removed no
705
+ * earlier than 0.51.
660
706
  */
661
- variant?: "default" | "primary";
707
+ variant?:
708
+ | "default"
709
+ | "accent"
710
+ /** @deprecated 0.49 — use `accent` (same look); removed in 0.51. */
711
+ | "primary";
662
712
  /**
663
713
  * A count in a dot at the member's top end corner — the /transactions invoices link's
664
714
  * pending count. Nothing is drawn for `0` or less; above 99 it reads `99+`. The count
@@ -704,7 +754,7 @@ export function FloatingAction({
704
754
  icon,
705
755
  pressed,
706
756
  pressedStyle = "tint",
707
- variant = "default",
757
+ variant: givenVariant = "default",
708
758
  badge,
709
759
  badgeLabel,
710
760
  badgeTone = "warning",
@@ -723,12 +773,13 @@ export function FloatingAction({
723
773
  const counted = badge !== undefined && badge > 0;
724
774
  const name = counted ? `${label}, ${badgeLabel ?? labels.badge(badge)}` : label;
725
775
  const isLink = href !== undefined;
776
+ const variant = givenVariant === "primary" ? deprecatedPrimary("FloatingAction variant") : givenVariant;
726
777
  const look = cn(
727
778
  "relative flex size-12 items-center justify-center transition-colors",
728
779
  "outline-none focus-visible:ring-inset",
729
780
  FOCUS_RING,
730
781
  "disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:size-5",
731
- variant === "primary"
782
+ variant === "accent"
732
783
  ? "bg-brand text-brand-contrast hover:bg-brand-hover"
733
784
  : pressed && !isLink && pressedStyle === "tint"
734
785
  ? "bg-brand-soft text-brand hover:bg-brand-soft-hover hover:text-brand-muted"
@@ -204,7 +204,7 @@ export interface FormActionsProps extends Omit<ComponentPropsWithoutRef<"div">,
204
204
  * allows (the account's own settings, docs/billing-harmonization.md §12.36).
205
205
  */
206
206
  commit?: CommitScope;
207
- /** The save button's variant. Default `brand`; `danger` for a save that destroys
207
+ /** The save button's variant. Default `accent`; `danger` for a save that destroys
208
208
  * (kastlan's `destructive` flag on its own FormActions). */
209
209
  submitVariant?: ButtonVariant;
210
210
  /**
@@ -371,7 +371,7 @@ export function FormActions({
371
371
  submitDisabled = false,
372
372
  submitDisabledReason,
373
373
  commit = false,
374
- submitVariant = "brand",
374
+ submitVariant = "accent",
375
375
  cancelVariant = "secondary",
376
376
  size = "md",
377
377
  destructive,
@@ -208,6 +208,9 @@ export function FullBleedDialog({
208
208
  // `tabIndex` below by passing one of them too — this is the element the focus
209
209
  // trap holds, and each of those failures is invisible on screen.
210
210
  {...rest}
211
+ // A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6):
212
+ // a backdrop, so the tour measures the panel inside it to tell a full-screen sheet.
213
+ data-kit-layer="backdrop"
211
214
  className={cn(
212
215
  "fixed inset-0 z-50 flex items-stretch justify-center bg-black/40 outline-none",
213
216
  closing ? "animate-overlay-out" : "animate-overlay",
@@ -28,8 +28,9 @@ import {
28
28
  HIT_AREA_LARGE,
29
29
  TOUCH_TARGET_LARGE,
30
30
  buttonVariantClasses,
31
+ resolveButtonVariant,
31
32
  } from "./button-look";
32
- import type { ButtonVariant } from "./button-look";
33
+ import type { ButtonVariant, CanonicalButtonVariant } from "./button-look";
33
34
 
34
35
  // Canonical square icon-only button. <Button> carries TEXT geometry — px-3 py-2
35
36
  // gap-2 — so rendering a bare glyph through it gives a small icon in a wide,
@@ -199,12 +200,12 @@ function iconButtonToneClass(tone: IconButtonTone, quiet: boolean | undefined):
199
200
  // and would out-rank a caller's plain `hover:` override), each variant pins its
200
201
  // RESTING look under `disabled:hover:`, which out-ranks any `hover:` by specificity
201
202
  // and only ever matches a disabled button.
202
- const ICON_BUTTON_DISABLED_REST: Record<IconButtonVariant, string> = {
203
- primary: "disabled:hover:bg-surface-2",
203
+ const ICON_BUTTON_DISABLED_REST: Record<CanonicalButtonVariant | "overlay" | "shutter", string> = {
204
+ default: "disabled:hover:bg-surface-2",
204
205
  secondary: "disabled:hover:bg-transparent",
205
206
  ghost: "disabled:hover:bg-transparent",
206
207
  danger: "disabled:hover:bg-danger",
207
- brand: "disabled:hover:bg-brand",
208
+ accent: "disabled:hover:bg-brand",
208
209
  link: "disabled:hover:bg-transparent disabled:hover:no-underline",
209
210
  overlay: "disabled:hover:bg-[color-mix(in_srgb,var(--bg-inverse)_60%,transparent)]",
210
211
  shutter: "disabled:hover:bg-media-scrim",
@@ -633,6 +634,7 @@ function iconButtonLook({
633
634
  /** The label is drawn beside the glyph (see `labelVisible`). */
634
635
  withText?: boolean;
635
636
  }) {
637
+ const look = variant === "overlay" || variant === "shutter" ? variant : resolveButtonVariant(variant, "IconButton variant");
636
638
  return cn(
637
639
  ICON_BUTTON_BASE,
638
640
  withText
@@ -644,13 +646,13 @@ function iconButtonLook({
644
646
  withText ? TOUCH_TARGET_LARGE : ICON_BUTTON_TOUCH_LARGE[size],
645
647
  glyphSize !== undefined && ICON_BUTTON_GLYPH_SIZES[glyphSize],
646
648
  // After the size, so the overlay's `rounded-full` beats the small sizes' `rounded`.
647
- variant === "overlay"
649
+ look === "overlay"
648
650
  ? ICON_BUTTON_OVERLAY
649
- : variant === "shutter"
651
+ : look === "shutter"
650
652
  ? ICON_BUTTON_SHUTTER
651
- : buttonVariantClasses[variant],
653
+ : buttonVariantClasses[look],
652
654
  shape === "round" && "rounded-full",
653
- ICON_BUTTON_DISABLED_REST[variant],
655
+ ICON_BUTTON_DISABLED_REST[look],
654
656
  iconButtonToneClass(tone, quiet),
655
657
  toneColor !== undefined && typeof toneColor !== "string" && ICON_BUTTON_TONE_PAIR,
656
658
  );
@@ -676,6 +678,17 @@ function withIconButtonBadge(control: ReactElement, badge: ReactNode, stretch: b
676
678
 
677
679
  // Declared link-first, as Button is, so `<IconButton href>` picks the link props and
678
680
  // `ComponentProps<typeof IconButton>` (the LAST signature) is still the button's.
681
+ /**
682
+ * An icon-only button whose `label` is its name and its tooltip.
683
+ *
684
+ * Destructive actions (0.49 KIT-12): a delete, revoke or erase is NOT an icon repeated in
685
+ * every row of a list — one stray tap away, and a bin per row draws the eye to the one
686
+ * thing nobody should do by accident. Put it in the row's menu, `RowActions` (where a
687
+ * `tone: "danger"` action goes by default and opens a `ConfirmDialog` first), or in the
688
+ * selection bar ("2 selected" → Delete), and confirm it through `useConfirm`. A single
689
+ * standalone delete — a card's own, a bulk bar's — may be an IconButton
690
+ * `tone="danger"`; it still asks first.
691
+ */
679
692
  export function IconButton(props: IconButtonLinkProps): ReactElement;
680
693
  export function IconButton(props: IconButtonProps): ReactElement;
681
694
  export function IconButton(props: IconButtonProps | IconButtonLinkProps): ReactElement {
@@ -394,6 +394,9 @@ function ModalPanel({
394
394
  // that page from a screen reader for 220ms. Not `inert` — that would let a tap
395
395
  // fall through the fading backdrop to the row underneath.
396
396
  aria-hidden={exiting || undefined}
397
+ // A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6):
398
+ // a backdrop, so the tour measures the panel inside it to tell a full-screen sheet.
399
+ data-kit-layer="backdrop"
397
400
  className={cn(
398
401
  // Live #320. The panel below rises only where it is bottom-anchored: from md up
399
402
  // it is centred, and a centred box sliding up from off-screen reads as a
@@ -1,5 +1,7 @@
1
+ import { useLayoutEffect, useRef } from "react";
1
2
  import type { ComponentPropsWithoutRef, ComponentType, MouseEvent, ReactElement, ReactNode, Ref } from "react";
2
3
  import { cn } from "../lib/cn";
4
+ import { scrollIntoStrip, useStripFade } from "../lib/strip-fade";
3
5
  import { useKitLink } from "../i18n/kit-labels";
4
6
  import { FOCUS_RING } from "./focus-ring";
5
7
  import { pickLinkRenderer, RenderedKitLink } from "./text-link";
@@ -50,6 +52,13 @@ export interface NavPillsProps<T extends string> extends Omit<ComponentPropsWith
50
52
  landmark?: boolean;
51
53
  /** Classes for every pill. */
52
54
  itemClassName?: string;
55
+ /**
56
+ * 0.49: `wrap` (default) puts the pills on as many rows as they need. `scroll` keeps
57
+ * them on ONE row that scrolls sideways — faded at a cut edge, the current pill
58
+ * scrolled into view (the row only, never the page) — for a strip that must stay one
59
+ * line high, as {@link SectionNav} under a page title on a phone.
60
+ */
61
+ layout?: "wrap" | "scroll";
53
62
  /** The `<nav>` (or the `<div role="group">`) — hence `HTMLElement` (0.34, API-2,
54
63
  * refactor audit 2026-10). */
55
64
  ref?: Ref<HTMLElement>;
@@ -89,12 +98,22 @@ export function NavPills<T extends string>({
89
98
  size = "md",
90
99
  landmark = true,
91
100
  itemClassName,
101
+ layout = "wrap",
92
102
  className,
93
103
  ref,
94
104
  ...rest
95
105
  }: NavPillsProps<T>) {
96
106
  const kitLink = useKitLink();
97
107
  const Root = landmark ? "nav" : "div";
108
+ const scroll = layout === "scroll";
109
+ const listRef = useRef<HTMLUListElement>(null);
110
+ const fade = useStripFade(listRef);
111
+ // The current pill into view — the row only, never the page.
112
+ useLayoutEffect(() => {
113
+ const list = listRef.current;
114
+ const currentPill = list?.querySelector<HTMLElement>("[aria-current]");
115
+ if (scroll && list && currentPill) scrollIntoStrip(list, currentPill);
116
+ }, [scroll, current]);
98
117
  return (
99
118
  <Root
100
119
  {...rest}
@@ -102,7 +121,15 @@ export function NavPills<T extends string>({
102
121
  role={landmark ? undefined : "group"}
103
122
  className={cn("min-w-0", className)}
104
123
  >
105
- <ul className="m-0 flex list-none flex-wrap gap-1 p-0">
124
+ <ul
125
+ ref={listRef}
126
+ className={cn(
127
+ "m-0 flex list-none gap-1 p-0",
128
+ scroll ? "flex-nowrap overflow-x-auto overscroll-x-contain [scrollbar-width:none]" : "flex-wrap",
129
+ )}
130
+ data-overflow={scroll ? fade.overflow : undefined}
131
+ style={scroll && fade.mask ? { maskImage: fade.mask, WebkitMaskImage: fade.mask } : undefined}
132
+ >
106
133
  {items.map((item) => {
107
134
  const isCurrent = item.value === current;
108
135
  const Icon = item.icon;
@@ -152,7 +179,7 @@ export function NavPills<T extends string>({
152
179
  );
153
180
  }
154
181
  return (
155
- <li key={item.value} className="flex">
182
+ <li key={item.value} className={cn("flex", scroll && "shrink-0")}>
156
183
  {pill}
157
184
  </li>
158
185
  );
@@ -16,10 +16,11 @@ import { useEndWidth, useNumericField } from "./numeric-field";
16
16
  import type { CommitScope } from "./write-lock";
17
17
  import { cn } from "../lib/cn";
18
18
  import { commitExpression, formatResult } from "../lib/calc";
19
+ import { dataAttributes, type DataAttributes } from "../lib/data-attributes";
19
20
 
20
21
  /** {@link NumberInput}'s props — exported since 0.34 (refactor audit 2026-10, API-9): a
21
22
  * wrapper typed itself off `ComponentProps<typeof NumberInput>` until then. */
22
- export interface NumberInputProps extends FieldHelpProps {
23
+ export interface NumberInputProps extends FieldHelpProps, DataAttributes {
23
24
  /** The text, DOT-decimal ("1234.5", or a calculation being typed: "12+5"), as
24
25
  * `onChange` reports it — so a caller's `Number(value)` reads it. The field SHOWS it
25
26
  * in the locale's mark (see `locale`). A comma here is read as the decimal mark too,
@@ -549,12 +550,16 @@ export function NumberInput(props: NumberInputProps) {
549
550
  ) : (
550
551
  field
551
552
  );
552
- if (numeric.hint.captionText === undefined && numeric.errorEl === null && !reserve) return box;
553
+ const data = dataAttributes(props);
554
+ if (numeric.hint.captionText === undefined && numeric.errorEl === null && !reserve) {
555
+ // Bare, unless the caller tags it: then the same plain root as below carries the tags.
556
+ return Object.keys(data).length > 0 ? <div {...data}>{box}</div> : box;
557
+ }
553
558
  // Outside the field's own `relative` box, as Select's caption is: the calculator
554
559
  // and the unit are `inset-y-0` in it and would stretch down over a second line.
555
560
  // `className` stays on the field, so adding a caption cannot change what it styles.
556
561
  return (
557
- <div>
562
+ <div {...data}>
558
563
  {box}
559
564
  <FieldCaption parts={numeric.hint} />
560
565
  {numeric.errorEl}
@@ -209,6 +209,10 @@ export function NumberPadSheet({
209
209
  <div
210
210
  role="group"
211
211
  aria-label={text.panel}
212
+ // A kit layer, and the bottom layer a docked tour card stays clear of (0.50,
213
+ // docs/tours-harmonization.md §11.6).
214
+ data-kit-layer=""
215
+ data-tour-obstruction=""
212
216
  // Keep the host input focused when tapping the pad: preventDefault on
213
217
  // pointerdown blocks the focus/blur, while the buttons' click still fires.
214
218
  onPointerDown={(e) => e.preventDefault()}
@@ -0,0 +1,8 @@
1
+ import { createContext } from "react";
2
+
3
+ /**
4
+ * Set by `PageHeader` around its `breadcrumbs` (0.49 KIT-9), so a `Breadcrumbs` there
5
+ * knows the page's title is already on screen: it hides its current entry by default
6
+ * (`hideCurrent`) and draws nothing when only the home entry would remain. Internal.
7
+ */
8
+ export const PageHeaderTrailContext = createContext<{ titled: boolean } | null>(null);
@@ -2,6 +2,8 @@ import { useLayoutEffect, useRef, useState } from "react";
2
2
  import type { ComponentPropsWithRef, ElementType, ReactNode } from "react";
3
3
  import { cn } from "../lib/cn";
4
4
  import { SECTION_LABEL_CLASS } from "./text";
5
+ import { useDocumentTitle } from "../hooks/use-document-title";
6
+ import { PageHeaderTrailContext } from "./page-header-context";
5
7
 
6
8
  /**
7
9
  * `md` (default) is kastlan's `text-2xl` page title from `sm` up, a step smaller on a
@@ -71,8 +73,20 @@ export interface PageHeaderProps extends Omit<ComponentPropsWithRef<"div">, "tit
71
73
  /** Buttons for the page — "New", "Export". At the title's end from `sm` up; under it,
72
74
  * wrapping, on a phone — unless {@link mobileLayout} is `inline`. */
73
75
  actions?: ReactNode;
74
- /** A `Breadcrumbs` trail, drawn above everything else. */
76
+ /** A `Breadcrumbs` trail, drawn above everything else. Inside a header with a title
77
+ * it hides its current entry (the title names it) and is drawn only when an entry
78
+ * other than home is left — `Breadcrumbs.hideCurrent` (0.49 KIT-9). */
75
79
  breadcrumbs?: ReactNode;
80
+ /**
81
+ * The window's title (`document.title`) while the header is mounted (0.49 KIT-9),
82
+ * through `useDocumentTitle` and the provider's format ("{page} · {section} · {app}",
83
+ * `appName` as the app). A string is the page; an array is `[page, section]` (or
84
+ * `[page, section, app]`); `false` leaves the window's title alone.
85
+ *
86
+ * Default: the `title` when it is a string, and only for the page's own header
87
+ * (`as="h1"`) — a tab's `h2` header does not rename the window.
88
+ */
89
+ documentTitle?: string | ReadonlyArray<string | null | undefined> | false;
76
90
  /** A small uppercase line above the title — the section or the kind of record
77
91
  * ("Building", "Settings"). The section-label type. */
78
92
  eyebrow?: ReactNode;
@@ -147,6 +161,9 @@ export interface PageHeaderProps extends Omit<ComponentPropsWithRef<"div">, "tit
147
161
  truncateTitle?: boolean;
148
162
  }
149
163
 
164
+ const TITLED = { titled: true };
165
+ const UNTITLED = { titled: false };
166
+
150
167
  /**
151
168
  * A page's head: optional breadcrumbs, an eyebrow, the title, a description, and the
152
169
  * page's actions.
@@ -170,10 +187,23 @@ export function PageHeader({
170
187
  secondaryActions,
171
188
  actionsAlign,
172
189
  truncateTitle = false,
190
+ documentTitle,
173
191
  className,
174
192
  ...rest
175
193
  }: PageHeaderProps) {
176
194
  const Heading = as as ElementType;
195
+ useDocumentTitle(
196
+ documentTitle === false
197
+ ? []
198
+ : documentTitle === undefined
199
+ ? as === "h1" && typeof title === "string"
200
+ ? [title]
201
+ : []
202
+ : typeof documentTitle === "string"
203
+ ? [documentTitle]
204
+ : documentTitle,
205
+ );
206
+ const titled = title != null && title !== false && title !== "";
177
207
  const hasSecondary = secondaryActions != null;
178
208
  const wrapsSecondary = hasSecondary && mobileLayout === "inline";
179
209
  const align = actionsAlign != null ? ROW_ALIGN[mobileLayout][actionsAlign] : undefined;
@@ -216,7 +246,9 @@ export function PageHeader({
216
246
  }, [measuresWrap]);
217
247
  return (
218
248
  <div {...rest} className={cn("flex min-w-0 flex-col gap-2", className)}>
219
- {breadcrumbs}
249
+ {breadcrumbs != null && (
250
+ <PageHeaderTrailContext.Provider value={titled ? TITLED : UNTITLED}>{breadcrumbs}</PageHeaderTrailContext.Provider>
251
+ )}
220
252
  {/* With a second group, `inline` wraps so that group's full-width basis puts it
221
253
  on a row of its own on a phone (`stacked` is a column there already, and a
222
254
  basis would size a column item's HEIGHT); from `sm` up the row stops wrapping
@@ -211,6 +211,10 @@ export function PickerSheet({
211
211
  // caller has something to add.
212
212
  {...rest}
213
213
  ref={setSheet}
214
+ // A kit layer and a bottom layer a docked tour card stays clear of (0.50,
215
+ // docs/tours-harmonization.md §11.6).
216
+ data-kit-layer=""
217
+ data-tour-obstruction=""
214
218
  role="dialog"
215
219
  aria-modal="true"
216
220
  // Without it the container cannot take focus, and `useFocusTrap` has nothing to
@@ -20,6 +20,7 @@ import { DEFAULT_PIE_CHART_LABELS, type PieChartLabels } from "./pie-chart-label
20
20
  import { StaticLegend, ToggleLegend, toggleHidden, type LegendEntry } from "./toggle-legend";
21
21
  import { useTextSize } from "../theme/text-size";
22
22
  import { chartHeightProps, type ChartHeight } from "./chart-height";
23
+ import { formatWithMinus } from "../lib/minus";
23
24
 
24
25
  /** One slice. */
25
26
  export interface PieChartSlice {
@@ -186,7 +187,7 @@ export function PieChart({
186
187
  // Memoised, like everything the sectors are drawn from: they must not be rebuilt on
187
188
  // this component's own re-renders (see `pie` below).
188
189
  const formatValue = useCallback(
189
- (v: number) => (formatValueProp ? formatValueProp(v) : numberFormat.format(v)),
190
+ (v: number) => (formatValueProp ? formatValueProp(v) : formatWithMinus(numberFormat, v)),
190
191
  [formatValueProp, numberFormat],
191
192
  );
192
193
  // One decimal under 10 %, where rounding to whole percents starts to lie — a slice
@@ -206,6 +206,8 @@ export function Popover({
206
206
  // to remove one of them by accident.
207
207
  {...rest}
208
208
  ref={setPanel}
209
+ // A kit layer, for the tour's layer order (0.50, docs/tours-harmonization.md §11.6).
210
+ data-kit-layer=""
209
211
  role="dialog"
210
212
  id={panelId}
211
213
  // The caller's own `aria-label` wins over {@link PopoverLabels.panel}, which
@@ -5,6 +5,7 @@ import { DEFAULT_COMMON_LABELS, useKitLabels, useKitLocale, type LabelOverride }
5
5
  import { toneTextClass } from "./signed-amount";
6
6
  import type { TextTone } from "./signed-amount";
7
7
  import { DEFAULT_PROGRESS_BAR_LABELS } from "./progress-bar-labels";
8
+ import { formatWithMinus } from "../lib/minus";
8
9
 
9
10
  /** The words a progress bar adds of its own. */
10
11
  export interface ProgressBarLabels {
@@ -57,6 +58,7 @@ const TRACK_HEIGHT: Record<ProgressBarSize, string> = { sm: "h-1", slim: "h-1.5"
57
58
 
58
59
  // A segment with no tone of its own takes the next categorical chart colour, so a
59
60
  // stacked bar of unnamed parts is still a set of tellable parts rather than one fill.
61
+ // Eight, cycled like `paletteFor` (0.49): `--chart-9` only repeats slot 1.
60
62
  const SERIES_FILL = [
61
63
  "bg-[var(--chart-1)]",
62
64
  "bg-[var(--chart-2)]",
@@ -66,7 +68,6 @@ const SERIES_FILL = [
66
68
  "bg-[var(--chart-6)]",
67
69
  "bg-[var(--chart-7)]",
68
70
  "bg-[var(--chart-8)]",
69
- "bg-[var(--chart-9)]",
70
71
  ];
71
72
 
72
73
  /** Each segment's fill classes: its own classes, else its tone, else (with no `color`)
@@ -282,7 +283,7 @@ export function ProgressBar({
282
283
  ? undefined
283
284
  : formatValue
284
285
  ? formatValue(value, Infinity, min)
285
- : new Intl.NumberFormat(kitLocale).format(value);
286
+ : formatWithMinus(new Intl.NumberFormat(kitLocale), value);
286
287
  return (
287
288
  <Tag {...rest} ref={tagRef} className={cn("min-w-0", inlineClass, className)} data-state="unlimited">
288
289
  <Tag className="flex items-baseline gap-2 text-sm">
@@ -325,7 +326,9 @@ export function ProgressBar({
325
326
  const formatPart = (v: number) => {
326
327
  if (!Number.isFinite(v)) return format(min);
327
328
  if (formatValue) return formatValue(v, max, min);
328
- return new Intl.NumberFormat(kitLocale, { style: "percent", maximumFractionDigits: 0 }).format(
329
+ // A part can be negative (a refund): U+2212, as everywhere (0.49, KIT-1).
330
+ return formatWithMinus(
331
+ new Intl.NumberFormat(kitLocale, { style: "percent", maximumFractionDigits: 0 }),
329
332
  span <= 0 ? 0 : (v - min) / span,
330
333
  );
331
334
  };
@@ -257,7 +257,7 @@ function ReauthActions({
257
257
  <Button
258
258
  type="submit"
259
259
  form={formId}
260
- variant={danger ? "danger" : "primary"}
260
+ variant={danger ? "danger" : "default"}
261
261
  disabled={!canSubmit || busy}
262
262
  aria-busy={busy || undefined}
263
263
  >
@@ -7,4 +7,6 @@ import type { RowActionsLabels } from "./row-actions";
7
7
  export const DEFAULT_ROW_ACTIONS_LABELS: RowActionsLabels = {
8
8
  actions: "Actions",
9
9
  actionsFor: (name) => `Actions for ${name}`,
10
+ confirmTitle: (action, name) => (name ? `${action} “${name}”?` : `${action}?`),
11
+ confirmBody: "This cannot be undone.",
10
12
  };