@eifi1/ui-kit 0.31.1 → 0.32.1

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 (565) hide show
  1. package/README.md +51 -28
  2. package/dist/account/delete-account-setting.d.ts +8 -3
  3. package/dist/admin/admin-action-confirm.d.ts +8 -3
  4. package/dist/admin/admin-action-log.d.ts +1 -1
  5. package/dist/admin/admin-action-log.js +4 -2
  6. package/dist/admin/admin-action-log.js.map +1 -1
  7. package/dist/admin/invitations-panel.d.ts +8 -3
  8. package/dist/admin/invitations-panel.js +29 -26
  9. package/dist/admin/invitations-panel.js.map +1 -1
  10. package/dist/admin/reviewer-scope-editor.d.ts +8 -3
  11. package/dist/admin/role-select.d.ts +10 -5
  12. package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
  13. package/dist/admin/user-roster.d.ts +10 -5
  14. package/dist/admin/user-roster.js +8 -47
  15. package/dist/admin/user-roster.js.map +1 -1
  16. package/dist/auth/auth-errors.d.ts +43 -8
  17. package/dist/auth/auth-errors.js +24 -1
  18. package/dist/auth/auth-errors.js.map +1 -1
  19. package/dist/auth/register-form.d.ts +8 -3
  20. package/dist/billing/billing-banner.d.ts +184 -0
  21. package/dist/billing/billing-banner.js +98 -0
  22. package/dist/billing/billing-banner.js.map +1 -0
  23. package/dist/billing/billing-labels.d.ts +100 -0
  24. package/dist/billing/billing-labels.js +76 -0
  25. package/dist/billing/billing-labels.js.map +1 -0
  26. package/dist/billing/billing-lock.d.ts +158 -0
  27. package/dist/billing/billing-lock.js +17 -0
  28. package/dist/billing/billing-lock.js.map +1 -0
  29. package/dist/billing/billing-standing.d.ts +149 -0
  30. package/dist/billing/billing-standing.js +28 -0
  31. package/dist/billing/billing-standing.js.map +1 -0
  32. package/dist/billing/plan-card.d.ts +248 -0
  33. package/dist/billing/plan-card.js +248 -0
  34. package/dist/billing/plan-card.js.map +1 -0
  35. package/dist/billing/plan-limit-notice.d.ts +156 -0
  36. package/dist/billing/plan-limit-notice.js +76 -0
  37. package/dist/billing/plan-limit-notice.js.map +1 -0
  38. package/dist/billing/plan-limit.d.ts +34 -0
  39. package/dist/billing/plan-limit.js +20 -0
  40. package/dist/billing/plan-limit.js.map +1 -0
  41. package/dist/billing/plan-price.d.ts +61 -0
  42. package/dist/billing/plan-price.js +45 -0
  43. package/dist/billing/plan-price.js.map +1 -0
  44. package/dist/billing/subscription-actions.d.ts +133 -0
  45. package/dist/billing/subscription-actions.js +28 -0
  46. package/dist/billing/subscription-actions.js.map +1 -0
  47. package/dist/billing/subscription-status-chip.d.ts +149 -0
  48. package/dist/billing/subscription-status-chip.js +48 -0
  49. package/dist/billing/subscription-status-chip.js.map +1 -0
  50. package/dist/chart.d.ts +9 -3
  51. package/dist/chart.js.map +1 -1
  52. package/dist/components/account-chips.d.ts +16 -4
  53. package/dist/components/account-chips.js +21 -3
  54. package/dist/components/account-chips.js.map +1 -1
  55. package/dist/components/alert-banner.d.ts +6 -1
  56. package/dist/components/alert-banner.js +5 -4
  57. package/dist/components/alert-banner.js.map +1 -1
  58. package/dist/components/amount-input.d.ts +8 -3
  59. package/dist/components/amount-input.js +5 -5
  60. package/dist/components/amount-input.js.map +1 -1
  61. package/dist/components/appearance-labels.d.ts +38 -0
  62. package/dist/components/appearance-labels.js +19 -0
  63. package/dist/components/appearance-labels.js.map +1 -0
  64. package/dist/components/autocomplete.js +2 -2
  65. package/dist/components/autocomplete.js.map +1 -1
  66. package/dist/components/breadcrumbs.js +3 -2
  67. package/dist/components/breadcrumbs.js.map +1 -1
  68. package/dist/components/bulk-action-bar.d.ts +2 -2
  69. package/dist/components/bulk-action-bar.js +7 -12
  70. package/dist/components/bulk-action-bar.js.map +1 -1
  71. package/dist/components/button-group.d.ts +8 -3
  72. package/dist/components/button-group.js +7 -1
  73. package/dist/components/button-group.js.map +1 -1
  74. package/dist/components/calculator.d.ts +8 -3
  75. package/dist/components/calculator.js +4 -3
  76. package/dist/components/calculator.js.map +1 -1
  77. package/dist/components/calendar-heatmap.js +5 -5
  78. package/dist/components/calendar-heatmap.js.map +1 -1
  79. package/dist/components/chart-height.d.ts +23 -0
  80. package/dist/components/chart-height.js +15 -0
  81. package/dist/components/chart-height.js.map +1 -0
  82. package/dist/components/chart-zoom.js +6 -1
  83. package/dist/components/chart-zoom.js.map +1 -1
  84. package/dist/components/checkbox-group.js +2 -2
  85. package/dist/components/checkbox-group.js.map +1 -1
  86. package/dist/components/checkbox.d.ts +10 -0
  87. package/dist/components/checkbox.js +25 -10
  88. package/dist/components/checkbox.js.map +1 -1
  89. package/dist/components/chip.d.ts +19 -3
  90. package/dist/components/chip.js +54 -23
  91. package/dist/components/chip.js.map +1 -1
  92. package/dist/components/choice-card.d.ts +1 -1
  93. package/dist/components/choice-card.js +9 -8
  94. package/dist/components/choice-card.js.map +1 -1
  95. package/dist/components/column-mapper.js +1 -1
  96. package/dist/components/column-mapper.js.map +1 -1
  97. package/dist/components/combobox-core.js +8 -7
  98. package/dist/components/combobox-core.js.map +1 -1
  99. package/dist/components/combobox.js +8 -8
  100. package/dist/components/combobox.js.map +1 -1
  101. package/dist/components/confirm-dialog.d.ts +8 -3
  102. package/dist/components/copy-button.d.ts +10 -5
  103. package/dist/components/country-select.js +1 -1
  104. package/dist/components/country-select.js.map +1 -1
  105. package/dist/components/currency-select.js +3 -3
  106. package/dist/components/currency-select.js.map +1 -1
  107. package/dist/components/danger-confirm.d.ts +8 -3
  108. package/dist/components/data-table-cells.d.ts +1 -1
  109. package/dist/components/data-table-filter-popover.d.ts +1 -1
  110. package/dist/components/data-table-filter-popover.js +4 -1
  111. package/dist/components/data-table-filter-popover.js.map +1 -1
  112. package/dist/components/data-table-filters.d.ts +1 -1
  113. package/dist/components/data-table-labels.d.ts +1 -1
  114. package/dist/components/data-table-pagination.d.ts +3 -2
  115. package/dist/components/data-table-pagination.js +15 -6
  116. package/dist/components/data-table-pagination.js.map +1 -1
  117. package/dist/components/data-table.d.ts +1 -1
  118. package/dist/components/data-table.js +123 -52
  119. package/dist/components/data-table.js.map +1 -1
  120. package/dist/components/date-picker.d.ts +1 -1
  121. package/dist/components/date-picker.js +18 -6
  122. package/dist/components/date-picker.js.map +1 -1
  123. package/dist/components/description-list.js +2 -2
  124. package/dist/components/description-list.js.map +1 -1
  125. package/dist/components/dialog-frame.js +2 -1
  126. package/dist/components/dialog-frame.js.map +1 -1
  127. package/dist/components/disclosure.js +3 -2
  128. package/dist/components/disclosure.js.map +1 -1
  129. package/dist/components/dropdown.d.ts +1 -1
  130. package/dist/components/dropdown.js +7 -1
  131. package/dist/components/dropdown.js.map +1 -1
  132. package/dist/components/facing-pair.d.ts +19 -6
  133. package/dist/components/facing-pair.js +3 -2
  134. package/dist/components/facing-pair.js.map +1 -1
  135. package/dist/components/field-parts.d.ts +102 -6
  136. package/dist/components/field-parts.js +81 -6
  137. package/dist/components/field-parts.js.map +1 -1
  138. package/dist/components/field-sync.d.ts +4 -1
  139. package/dist/components/field-sync.js +12 -2
  140. package/dist/components/field-sync.js.map +1 -1
  141. package/dist/components/field.js +2 -2
  142. package/dist/components/field.js.map +1 -1
  143. package/dist/components/file-button.d.ts +8 -3
  144. package/dist/components/file-dropzone.d.ts +8 -3
  145. package/dist/components/floating-panel.js +7 -4
  146. package/dist/components/floating-panel.js.map +1 -1
  147. package/dist/components/focus-ring.d.ts +12 -0
  148. package/dist/components/focus-ring.js +8 -0
  149. package/dist/components/focus-ring.js.map +1 -0
  150. package/dist/components/form-actions.d.ts +8 -3
  151. package/dist/components/form-actions.js +33 -19
  152. package/dist/components/form-actions.js.map +1 -1
  153. package/dist/components/full-bleed-dialog.d.ts +11 -1
  154. package/dist/components/full-bleed-dialog.js +3 -1
  155. package/dist/components/full-bleed-dialog.js.map +1 -1
  156. package/dist/components/iban-input.d.ts +8 -3
  157. package/dist/components/image-grid.js +1 -1
  158. package/dist/components/image-grid.js.map +1 -1
  159. package/dist/components/inline-edit-field.js +10 -4
  160. package/dist/components/inline-edit-field.js.map +1 -1
  161. package/dist/components/language-select.d.ts +8 -3
  162. package/dist/components/legal-page.d.ts +26 -5
  163. package/dist/components/legal-page.js +3 -1
  164. package/dist/components/legal-page.js.map +1 -1
  165. package/dist/components/legal.d.ts +8 -3
  166. package/dist/components/legal.js +2 -1
  167. package/dist/components/legal.js.map +1 -1
  168. package/dist/components/lightbox.js +24 -2
  169. package/dist/components/lightbox.js.map +1 -1
  170. package/dist/components/line-items.js +2 -1
  171. package/dist/components/line-items.js.map +1 -1
  172. package/dist/components/list.d.ts +5 -0
  173. package/dist/components/list.js +13 -9
  174. package/dist/components/list.js.map +1 -1
  175. package/dist/components/measured-grid.d.ts +3 -1
  176. package/dist/components/measured-grid.js +38 -20
  177. package/dist/components/measured-grid.js.map +1 -1
  178. package/dist/components/menu-item.js +2 -1
  179. package/dist/components/menu-item.js.map +1 -1
  180. package/dist/components/mini-calendar.js +8 -4
  181. package/dist/components/mini-calendar.js.map +1 -1
  182. package/dist/components/money-field.d.ts +8 -3
  183. package/dist/components/month-picker.js +50 -39
  184. package/dist/components/month-picker.js.map +1 -1
  185. package/dist/components/nav-pills.js +1 -1
  186. package/dist/components/nav-pills.js.map +1 -1
  187. package/dist/components/number-field.d.ts +8 -3
  188. package/dist/components/number-input.d.ts +11 -4
  189. package/dist/components/number-input.js +8 -6
  190. package/dist/components/number-input.js.map +1 -1
  191. package/dist/components/numpad-sheet.d.ts +8 -3
  192. package/dist/components/numpad-sheet.js +2 -1
  193. package/dist/components/numpad-sheet.js.map +1 -1
  194. package/dist/components/one-time-code-input.js +2 -2
  195. package/dist/components/one-time-code-input.js.map +1 -1
  196. package/dist/components/page-contents.js +2 -2
  197. package/dist/components/page-contents.js.map +1 -1
  198. package/dist/components/phone-input.d.ts +8 -3
  199. package/dist/components/phone-input.js +4 -4
  200. package/dist/components/phone-input.js.map +1 -1
  201. package/dist/components/pie-chart.d.ts +8 -2
  202. package/dist/components/pie-chart.js +11 -7
  203. package/dist/components/pie-chart.js.map +1 -1
  204. package/dist/components/popover.d.ts +9 -1
  205. package/dist/components/popover.js +8 -4
  206. package/dist/components/popover.js.map +1 -1
  207. package/dist/components/row-actions.d.ts +100 -0
  208. package/dist/components/row-actions.js +231 -0
  209. package/dist/components/row-actions.js.map +1 -0
  210. package/dist/components/scroll-area.js +1 -1
  211. package/dist/components/scroll-area.js.map +1 -1
  212. package/dist/components/search-field.js +3 -2
  213. package/dist/components/search-field.js.map +1 -1
  214. package/dist/components/series-chart-budget.d.ts +30 -7
  215. package/dist/components/series-chart-budget.js +12 -4
  216. package/dist/components/series-chart-budget.js.map +1 -1
  217. package/dist/components/series-chart-ticks.d.ts +7 -1
  218. package/dist/components/series-chart-ticks.js +6 -0
  219. package/dist/components/series-chart-ticks.js.map +1 -1
  220. package/dist/components/series-chart.d.ts +58 -28
  221. package/dist/components/series-chart.js +42 -32
  222. package/dist/components/series-chart.js.map +1 -1
  223. package/dist/components/settings-fields.d.ts +57 -8
  224. package/dist/components/settings-fields.js +39 -1
  225. package/dist/components/settings-fields.js.map +1 -1
  226. package/dist/components/share-card.d.ts +11 -6
  227. package/dist/components/share-card.js +8 -2
  228. package/dist/components/share-card.js.map +1 -1
  229. package/dist/components/sign-chip.d.ts +4 -0
  230. package/dist/components/sign-chip.js +17 -4
  231. package/dist/components/sign-chip.js.map +1 -1
  232. package/dist/components/signature-pad.d.ts +3 -1
  233. package/dist/components/signature-pad.js +8 -5
  234. package/dist/components/signature-pad.js.map +1 -1
  235. package/dist/components/slider.js +7 -6
  236. package/dist/components/slider.js.map +1 -1
  237. package/dist/components/sparkline.d.ts +5 -1
  238. package/dist/components/sparkline.js +4 -2
  239. package/dist/components/sparkline.js.map +1 -1
  240. package/dist/components/stat-tile.d.ts +17 -1
  241. package/dist/components/stat-tile.js +48 -20
  242. package/dist/components/stat-tile.js.map +1 -1
  243. package/dist/components/status-dot.d.ts +4 -0
  244. package/dist/components/status-dot.js +9 -2
  245. package/dist/components/status-dot.js.map +1 -1
  246. package/dist/components/swipeable-row.js +10 -2
  247. package/dist/components/swipeable-row.js.map +1 -1
  248. package/dist/components/switch.d.ts +21 -7
  249. package/dist/components/switch.js +24 -11
  250. package/dist/components/switch.js.map +1 -1
  251. package/dist/components/table.js +2 -2
  252. package/dist/components/table.js.map +1 -1
  253. package/dist/components/text-link.d.ts +8 -3
  254. package/dist/components/text-link.js +1 -1
  255. package/dist/components/text-link.js.map +1 -1
  256. package/dist/components/text.d.ts +7 -2
  257. package/dist/components/text.js +5 -5
  258. package/dist/components/text.js.map +1 -1
  259. package/dist/components/tile-radio.js +2 -1
  260. package/dist/components/tile-radio.js.map +1 -1
  261. package/dist/components/time-input.d.ts +8 -3
  262. package/dist/components/toast.d.ts +18 -2
  263. package/dist/components/toast.js +8 -5
  264. package/dist/components/toast.js.map +1 -1
  265. package/dist/components/toggle-group.d.ts +25 -3
  266. package/dist/components/toggle-group.js +40 -11
  267. package/dist/components/toggle-group.js.map +1 -1
  268. package/dist/components/toggle-legend.d.ts +10 -1
  269. package/dist/components/toggle-legend.js +21 -7
  270. package/dist/components/toggle-legend.js.map +1 -1
  271. package/dist/components/translation-review-editor.js +1 -1
  272. package/dist/components/translation-review-editor.js.map +1 -1
  273. package/dist/components/translation-review-parts.d.ts +4 -0
  274. package/dist/components/translation-review.d.ts +4 -0
  275. package/dist/components/translation-review.js +3 -3
  276. package/dist/components/translation-review.js.map +1 -1
  277. package/dist/components/tree-view.js +3 -2
  278. package/dist/components/tree-view.js.map +1 -1
  279. package/dist/components/treemap.d.ts +5 -2
  280. package/dist/components/treemap.js +33 -16
  281. package/dist/components/treemap.js.map +1 -1
  282. package/dist/components/ui.d.ts +8 -3
  283. package/dist/components/ui.js +207 -119
  284. package/dist/components/ui.js.map +1 -1
  285. package/dist/components/use-table-state.d.ts +1 -1
  286. package/dist/components/user-avatar.d.ts +5 -1
  287. package/dist/components/user-avatar.js +1 -1
  288. package/dist/components/user-avatar.js.map +1 -1
  289. package/dist/components/wizard-stepper.js +20 -14
  290. package/dist/components/wizard-stepper.js.map +1 -1
  291. package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
  292. package/dist/data-table.d.ts +1 -1
  293. package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-FLyJf-kZ.d.ts} +642 -60
  294. package/dist/demo/demo-banner.js +1 -0
  295. package/dist/demo/demo-banner.js.map +1 -1
  296. package/dist/feedback/feedback-attachment.d.ts +8 -3
  297. package/dist/feedback/feedback-context.d.ts +8 -3
  298. package/dist/feedback/feedback-crash.d.ts +8 -3
  299. package/dist/feedback/feedback-dialog.d.ts +8 -3
  300. package/dist/feedback/feedback-dialog.js +3 -3
  301. package/dist/feedback/feedback-dialog.js.map +1 -1
  302. package/dist/feedback/feedback-inbox.d.ts +8 -3
  303. package/dist/feedback/feedback-inbox.js +2 -2
  304. package/dist/feedback/feedback-inbox.js.map +1 -1
  305. package/dist/feedback/feedback-labels.d.ts +8 -3
  306. package/dist/feedback/feedback-menu.d.ts +9 -4
  307. package/dist/feedback/feedback-record.d.ts +8 -3
  308. package/dist/feedback/feedback-row-detail.d.ts +8 -3
  309. package/dist/feedback/feedback-status-undo.d.ts +8 -3
  310. package/dist/feedback/feedback-submit.d.ts +8 -3
  311. package/dist/feedback/feedback-swipe.d.ts +8 -3
  312. package/dist/feedback/feedback-table.d.ts +8 -3
  313. package/dist/feedback/feedback-thread.d.ts +8 -3
  314. package/dist/feedback/feedback-thread.js +3 -3
  315. package/dist/feedback/feedback-thread.js.map +1 -1
  316. package/dist/feedback.d.ts +8 -3
  317. package/dist/hooks/use-anchored-panel.d.ts +32 -3
  318. package/dist/hooks/use-anchored-panel.js +21 -3
  319. package/dist/hooks/use-anchored-panel.js.map +1 -1
  320. package/dist/hooks/use-breakpoint.d.ts +88 -0
  321. package/dist/hooks/use-breakpoint.js +50 -0
  322. package/dist/hooks/use-breakpoint.js.map +1 -0
  323. package/dist/hooks/use-file-drop.d.ts +8 -3
  324. package/dist/hooks/use-large-text.d.ts +29 -0
  325. package/dist/hooks/use-large-text.js +20 -0
  326. package/dist/hooks/use-large-text.js.map +1 -0
  327. package/dist/hooks/use-windowed-rows.d.ts +52 -8
  328. package/dist/hooks/use-windowed-rows.js +94 -5
  329. package/dist/hooks/use-windowed-rows.js.map +1 -1
  330. package/dist/i18n/defaults.d.ts +8 -3
  331. package/dist/i18n/defaults.js +8 -0
  332. package/dist/i18n/defaults.js.map +1 -1
  333. package/dist/i18n/german.d.ts +8 -3
  334. package/dist/i18n/german.js +94 -3
  335. package/dist/i18n/german.js.map +1 -1
  336. package/dist/i18n/kit-labels.d.ts +8 -3
  337. package/dist/i18n/kit-labels.js.map +1 -1
  338. package/dist/i18n/languages.d.ts +10 -5
  339. package/dist/i18n/locales/de-CH.d.ts +8 -3
  340. package/dist/i18n/locales/en.d.ts +8 -3
  341. package/dist/i18n/locales/es.d.ts +8 -3
  342. package/dist/i18n/locales/es.js +89 -3
  343. package/dist/i18n/locales/es.js.map +1 -1
  344. package/dist/i18n/locales/fr.d.ts +8 -3
  345. package/dist/i18n/locales/fr.js +91 -3
  346. package/dist/i18n/locales/fr.js.map +1 -1
  347. package/dist/i18n/locales/hu.d.ts +8 -3
  348. package/dist/i18n/locales/hu.js +91 -3
  349. package/dist/i18n/locales/hu.js.map +1 -1
  350. package/dist/i18n/locales/it.d.ts +8 -3
  351. package/dist/i18n/locales/it.js +92 -3
  352. package/dist/i18n/locales/it.js.map +1 -1
  353. package/dist/i18n/locales/zh.d.ts +8 -3
  354. package/dist/i18n/locales/zh.js +89 -3
  355. package/dist/i18n/locales/zh.js.map +1 -1
  356. package/dist/i18n/review.d.ts +8 -3
  357. package/dist/i18n/review.js +15 -1
  358. package/dist/i18n/review.js.map +1 -1
  359. package/dist/index.d.ts +27 -11
  360. package/dist/index.js +77 -4
  361. package/dist/index.js.map +1 -1
  362. package/dist/landing/landing-actions.d.ts +8 -3
  363. package/dist/landing/landing-sections.d.ts +8 -3
  364. package/dist/landing/landing-sections.js +7 -3
  365. package/dist/landing/landing-sections.js.map +1 -1
  366. package/dist/landing/public-header.d.ts +9 -4
  367. package/dist/landing/public-header.js +3 -3
  368. package/dist/landing/public-header.js.map +1 -1
  369. package/dist/landing/routing.d.ts +8 -3
  370. package/dist/lib/cn.js +3 -2
  371. package/dist/lib/cn.js.map +1 -1
  372. package/dist/rhf/fields.d.ts +8 -3
  373. package/dist/rhf/fields.js +1 -1
  374. package/dist/rhf/fields.js.map +1 -1
  375. package/dist/rhf/form.d.ts +8 -3
  376. package/dist/rhf/form.js +2 -2
  377. package/dist/rhf/form.js.map +1 -1
  378. package/dist/rhf.d.ts +8 -3
  379. package/dist/search/command-palette.d.ts +1 -1
  380. package/dist/search/command-palette.js +9 -8
  381. package/dist/search/command-palette.js.map +1 -1
  382. package/dist/settings/settings-layout.js +2 -1
  383. package/dist/settings/settings-layout.js.map +1 -1
  384. package/dist/settings/settings-section.d.ts +8 -3
  385. package/dist/settings/use-account-appearance.d.ts +92 -0
  386. package/dist/settings/use-account-appearance.js +63 -0
  387. package/dist/settings/use-account-appearance.js.map +1 -0
  388. package/dist/settings/use-settings-route.d.ts +1 -1
  389. package/dist/settings/use-settings-route.js +2 -3
  390. package/dist/settings/use-settings-route.js.map +1 -1
  391. package/dist/shell/app-shell.d.ts +12 -171
  392. package/dist/shell/app-shell.js +144 -28
  393. package/dist/shell/app-shell.js.map +1 -1
  394. package/dist/shell/auth-layout.js +1 -1
  395. package/dist/shell/auth-layout.js.map +1 -1
  396. package/dist/shell/option-switcher-menu.js +1 -1
  397. package/dist/shell/option-switcher-menu.js.map +1 -1
  398. package/dist/shell/top-bar-brand.d.ts +8 -3
  399. package/dist/shell/top-bar-brand.js +3 -1
  400. package/dist/shell/top-bar-brand.js.map +1 -1
  401. package/dist/shell/topbar-action-menu.d.ts +4 -0
  402. package/dist/shell/topbar-action-menu.js +3 -3
  403. package/dist/shell/topbar-action-menu.js.map +1 -1
  404. package/dist/shell/topbar-controls.js +4 -4
  405. package/dist/shell/topbar-controls.js.map +1 -1
  406. package/dist/shell.d.ts +9 -5
  407. package/dist/theme/contrast-tokens.d.ts +41 -0
  408. package/dist/theme/contrast-tokens.js +59 -0
  409. package/dist/theme/contrast-tokens.js.map +1 -0
  410. package/dist/theme/contrast.d.ts +110 -0
  411. package/dist/theme/contrast.js +128 -0
  412. package/dist/theme/contrast.js.map +1 -0
  413. package/dist/theme/palette-presets.d.ts +12 -1
  414. package/dist/theme/palette-presets.js +9 -4
  415. package/dist/theme/palette-presets.js.map +1 -1
  416. package/dist/theme/palette-store.js.map +1 -1
  417. package/dist/theme/text-size.d.ts +173 -0
  418. package/dist/theme/text-size.js +121 -0
  419. package/dist/theme/text-size.js.map +1 -0
  420. package/dist/wizard/stepper-nav.d.ts +8 -3
  421. package/dist/wizard/wizard-summary.js +1 -1
  422. package/dist/wizard/wizard-summary.js.map +1 -1
  423. package/dist/wizard.d.ts +8 -3
  424. package/package.json +1 -1
  425. package/src/admin/admin-action-log.tsx +6 -1
  426. package/src/admin/invitations-panel.tsx +36 -29
  427. package/src/admin/user-roster.tsx +17 -72
  428. package/src/auth/auth-errors.ts +68 -7
  429. package/src/billing/billing-banner.tsx +205 -0
  430. package/src/billing/billing-labels.ts +240 -0
  431. package/src/billing/billing-lock.ts +75 -0
  432. package/src/billing/billing-standing.ts +72 -0
  433. package/src/billing/plan-card.tsx +458 -0
  434. package/src/billing/plan-limit-notice.tsx +145 -0
  435. package/src/billing/plan-limit.ts +53 -0
  436. package/src/billing/plan-price.ts +101 -0
  437. package/src/billing/subscription-actions.tsx +76 -0
  438. package/src/billing/subscription-status-chip.tsx +95 -0
  439. package/src/chart.ts +2 -0
  440. package/src/components/account-chips.tsx +36 -10
  441. package/src/components/alert-banner.tsx +16 -7
  442. package/src/components/amount-input.tsx +5 -5
  443. package/src/components/appearance-labels.ts +47 -0
  444. package/src/components/autocomplete.tsx +2 -2
  445. package/src/components/breadcrumbs.tsx +3 -3
  446. package/src/components/bulk-action-bar.tsx +11 -14
  447. package/src/components/button-group.tsx +21 -0
  448. package/src/components/calculator.tsx +4 -3
  449. package/src/components/calendar-heatmap.tsx +5 -5
  450. package/src/components/chart-height.ts +30 -0
  451. package/src/components/chart-zoom.tsx +6 -1
  452. package/src/components/checkbox-group.tsx +2 -2
  453. package/src/components/checkbox.tsx +62 -20
  454. package/src/components/chip.tsx +139 -30
  455. package/src/components/choice-card.tsx +16 -11
  456. package/src/components/column-mapper.tsx +1 -1
  457. package/src/components/combobox-core.tsx +8 -7
  458. package/src/components/combobox.tsx +8 -8
  459. package/src/components/country-select.tsx +1 -1
  460. package/src/components/currency-select.tsx +3 -3
  461. package/src/components/data-table-filter-popover.tsx +7 -1
  462. package/src/components/data-table-pagination.tsx +60 -31
  463. package/src/components/data-table.tsx +164 -43
  464. package/src/components/date-picker.tsx +44 -10
  465. package/src/components/description-list.tsx +2 -2
  466. package/src/components/dialog-frame.tsx +2 -1
  467. package/src/components/disclosure.tsx +3 -2
  468. package/src/components/dropdown.tsx +10 -2
  469. package/src/components/facing-pair.ts +12 -5
  470. package/src/components/field-parts.tsx +190 -7
  471. package/src/components/field-sync.tsx +17 -3
  472. package/src/components/field.tsx +2 -2
  473. package/src/components/floating-panel.tsx +7 -4
  474. package/src/components/focus-ring.ts +12 -0
  475. package/src/components/form-actions.tsx +55 -18
  476. package/src/components/full-bleed-dialog.tsx +18 -4
  477. package/src/components/image-grid.tsx +1 -1
  478. package/src/components/inline-edit-field.tsx +26 -4
  479. package/src/components/legal-page.tsx +28 -2
  480. package/src/components/legal.tsx +19 -1
  481. package/src/components/lightbox.tsx +24 -2
  482. package/src/components/line-items.tsx +7 -1
  483. package/src/components/list.tsx +36 -10
  484. package/src/components/measured-grid.tsx +49 -24
  485. package/src/components/menu-item.tsx +2 -2
  486. package/src/components/mini-calendar.tsx +8 -4
  487. package/src/components/month-picker.tsx +15 -6
  488. package/src/components/nav-pills.tsx +1 -1
  489. package/src/components/number-input.tsx +14 -7
  490. package/src/components/numpad-sheet.tsx +2 -2
  491. package/src/components/one-time-code-input.tsx +2 -2
  492. package/src/components/page-contents.tsx +2 -2
  493. package/src/components/phone-input.tsx +9 -4
  494. package/src/components/pie-chart.tsx +19 -9
  495. package/src/components/popover.tsx +22 -5
  496. package/src/components/row-actions.tsx +450 -0
  497. package/src/components/scroll-area.tsx +1 -1
  498. package/src/components/search-field.tsx +4 -2
  499. package/src/components/series-chart-budget.ts +40 -8
  500. package/src/components/series-chart-ticks.ts +10 -0
  501. package/src/components/series-chart.tsx +123 -72
  502. package/src/components/settings-fields.tsx +89 -5
  503. package/src/components/share-card.tsx +31 -2
  504. package/src/components/sign-chip.tsx +28 -4
  505. package/src/components/signature-pad.tsx +13 -7
  506. package/src/components/slider.tsx +22 -9
  507. package/src/components/sparkline.tsx +11 -3
  508. package/src/components/stat-tile.tsx +70 -14
  509. package/src/components/status-dot.tsx +25 -2
  510. package/src/components/swipeable-row.tsx +10 -2
  511. package/src/components/switch.tsx +57 -23
  512. package/src/components/table.tsx +2 -2
  513. package/src/components/text-link.tsx +1 -1
  514. package/src/components/text.tsx +11 -6
  515. package/src/components/tile-radio.tsx +4 -1
  516. package/src/components/toast.tsx +27 -7
  517. package/src/components/toggle-group.tsx +115 -14
  518. package/src/components/toggle-legend.tsx +53 -8
  519. package/src/components/translation-review-editor.tsx +1 -1
  520. package/src/components/translation-review.tsx +11 -5
  521. package/src/components/tree-view.tsx +3 -2
  522. package/src/components/treemap.tsx +27 -10
  523. package/src/components/ui.tsx +353 -153
  524. package/src/components/user-avatar.tsx +1 -1
  525. package/src/components/wizard-stepper.tsx +5 -1
  526. package/src/demo/demo-banner.tsx +5 -0
  527. package/src/feedback/feedback-dialog.tsx +3 -3
  528. package/src/feedback/feedback-inbox.tsx +2 -2
  529. package/src/feedback/feedback-thread.tsx +3 -3
  530. package/src/hooks/use-anchored-panel.ts +67 -5
  531. package/src/hooks/use-breakpoint.ts +132 -0
  532. package/src/hooks/use-large-text.ts +41 -0
  533. package/src/hooks/use-windowed-rows.ts +168 -12
  534. package/src/i18n/defaults.ts +8 -0
  535. package/src/i18n/german.ts +107 -0
  536. package/src/i18n/kit-labels.tsx +12 -0
  537. package/src/i18n/locales/es.ts +98 -0
  538. package/src/i18n/locales/fr.ts +102 -0
  539. package/src/i18n/locales/hu.ts +100 -0
  540. package/src/i18n/locales/it.ts +103 -0
  541. package/src/i18n/locales/zh.ts +94 -0
  542. package/src/i18n/review.ts +14 -0
  543. package/src/index.ts +76 -6
  544. package/src/landing/landing-sections.tsx +7 -3
  545. package/src/landing/public-header.tsx +4 -4
  546. package/src/lib/cn.ts +13 -2
  547. package/src/rhf/fields.tsx +1 -1
  548. package/src/rhf/form.tsx +2 -2
  549. package/src/search/command-palette.tsx +10 -9
  550. package/src/settings/settings-layout.tsx +2 -1
  551. package/src/settings/use-account-appearance.ts +148 -0
  552. package/src/settings/use-settings-route.ts +3 -4
  553. package/src/shell/app-shell.tsx +278 -54
  554. package/src/shell/auth-layout.tsx +1 -1
  555. package/src/shell/option-switcher-menu.tsx +1 -1
  556. package/src/shell/top-bar-brand.tsx +3 -1
  557. package/src/shell/topbar-action-menu.tsx +3 -3
  558. package/src/shell/topbar-controls.tsx +4 -4
  559. package/src/theme/contrast-tokens.ts +112 -0
  560. package/src/theme/contrast.ts +225 -0
  561. package/src/theme/palette-presets.ts +24 -4
  562. package/src/theme/palette-store.ts +6 -1
  563. package/src/theme/text-size.ts +283 -0
  564. package/src/wizard/wizard-summary.tsx +4 -2
  565. package/tokens.css +158 -8
@@ -1,7 +1,10 @@
1
- import { createContext, useId } from "react";
1
+ import { createContext, isValidElement, useContext, useId } from "react";
2
2
  import type { ComponentPropsWithoutRef, ReactNode } from "react";
3
+ import { HelpCircle } from "lucide-react";
3
4
  import { cn } from "../lib/cn";
4
- import { Tooltip } from "./tooltip";
5
+ import { useInlineFacts } from "../hooks/use-large-text";
6
+ import { FOCUS_RING } from "./focus-ring";
7
+ import { Tooltip, type TooltipSide } from "./tooltip";
5
8
  import { useCommitReason } from "./write-lock";
6
9
  import { hasMessage, mergeDescribedBy } from "./choice-parts";
7
10
 
@@ -29,7 +32,7 @@ import { hasMessage, mergeDescribedBy } from "./choice-parts";
29
32
  // same baseline — which is what put dev#468's "?" three pixels above the word it
30
33
  // belongs to.
31
34
  export const STATIC_LABEL_TYPE =
32
- "text-[11px] leading-tight text-[var(--text-muted)] peer-disabled:opacity-50";
35
+ "text-caption leading-tight text-[var(--text-muted)] peer-disabled:opacity-50";
33
36
 
34
37
  // A field that always has a value (select / dropdown trigger) keeps the label
35
38
  // permanently in the floated position — small, in the top strip, value below.
@@ -58,6 +61,92 @@ export function FieldLabel({ children, className, ...rest }: FieldLabelProps) {
58
61
 
59
62
  /* ── hint ─────────────────────────────────────────────────────────────────── */
60
63
 
64
+ /**
65
+ * The "?" that explains a field, on the field's own label line (dev#468).
66
+ *
67
+ * Pass it to a labelled `Select` / `Input` as `hint`. It exists as a
68
+ * component rather than as a snippet each form repeats because the previous
69
+ * version was exactly that snippet — an absolutely-positioned button whose
70
+ * `top-1.5` was one guess at where an 11px label sits — and the reporter's
71
+ * follow-up was *"question mark is not centered. Is it part of the hoc?
72
+ * positioning problems seem quite frequently."* It was not part of the HOC. Now
73
+ * it is, and there is one place left where the answer can be wrong.
74
+ *
75
+ * A `<button>` rather than a bare icon: hover alone puts the explanation out of
76
+ * reach of a keyboard and of every touch device, and the tooltip shows on focus
77
+ * too. The text is also its accessible name, so a screen reader gets it without
78
+ * the bubble ever opening.
79
+ */
80
+ export interface FieldHintProps extends Omit<ComponentPropsWithoutRef<"button">, "children"> {
81
+ /** The explanation. It is both the tooltip's text and, by default, the button's
82
+ * accessible name, so a screen reader gets it without the bubble ever opening. */
83
+ label: string;
84
+ /** Default `"start"`: before the hint in the reading direction (the left in LTR, the
85
+ * right in RTL). `left` / `right` stay physical. */
86
+ side?: TooltipSide;
87
+ }
88
+
89
+ export function FieldHint({
90
+ label,
91
+ side = "start",
92
+ className,
93
+ "aria-label": ariaLabel,
94
+ ...rest
95
+ }: FieldHintProps) {
96
+ return (
97
+ // `tap="toggle"` (0.25): the "?" exists only to explain, so a tap on a phone shows
98
+ // the bubble and the next tap hides it — the touch rule (no bubble left behind by a
99
+ // tap) would otherwise make it show nothing at all.
100
+ <Tooltip label={label} side={side} portal tap="toggle">
101
+ <button
102
+ {...rest}
103
+ // `type` after the spread, not before. These render inside forms — that is the
104
+ // only place a field has a label line — and a hint that defaulted to `submit`
105
+ // because a caller spread a props object at it would save the form on a click
106
+ // meant to explain a field.
107
+ type="button"
108
+ // The explanation names the button unless the caller says otherwise; passing
109
+ // `aria-label` is how you shorten it for a screen reader without shortening
110
+ // what the bubble shows.
111
+ aria-label={ariaLabel ?? label}
112
+ // Nothing to activate: the tooltip opens on hover and on focus, and a
113
+ // click that did something as well would be a second, undiscoverable
114
+ // behaviour on the same target.
115
+ onClick={(e) => e.preventDefault()}
116
+ // The kit's focus frame (0.32, §5): the "?" is a tab stop, and had no ring at all.
117
+ className={cn(
118
+ "flex rounded-full text-[var(--text-placeholder)] transition-colors hover:text-[var(--text-secondary)]",
119
+ FOCUS_RING,
120
+ className,
121
+ )}
122
+ >
123
+ <HelpCircle className="size-3.5" />
124
+ </button>
125
+ </Tooltip>
126
+ );
127
+ }
128
+
129
+ /**
130
+ * A field's `hint` as the field should draw it here (0.32, docs/text-size-harmonization.md
131
+ * §4 "No fact only in a tooltip"): at Large and Extra large, and on a touch screen, a
132
+ * {@link FieldHint} becomes its own words — so the field shows them as its CAPTION under
133
+ * the box, where they are read without hovering or tapping a 14 px "?". Anywhere else,
134
+ * and for any other hint, the hint unchanged.
135
+ *
136
+ * The kit's fields call it through {@link useFieldHint} / {@link useFieldMessages}; a
137
+ * field that resolves its own `hint` (an app's, or one of the kit's own outside this
138
+ * file) calls it first and then treats the result as it treated a text hint.
139
+ */
140
+ export function useInlineHint(hint: ReactNode): ReactNode {
141
+ const inline = useInlineFacts();
142
+ if (inline && isValidElement(hint) && hint.type === FieldHint) {
143
+ const words = (hint.props as FieldHintProps).label;
144
+ if (words) return words;
145
+ }
146
+ return hint;
147
+ }
148
+
149
+
61
150
  /**
62
151
  * Whether a `hint` is a CAPTION (plain text: a non-empty string or a number) or
63
152
  * something for the label line (a {@link FieldHint} "?", or any other node).
@@ -73,7 +162,7 @@ export function isTextHint(hint: ReactNode): hint is string | number {
73
162
 
74
163
  /** The caption under a field — Select's and NumberInput's type, so every field's
75
164
  * standing advice reads alike. */
76
- export const FIELD_CAPTION_CLASS = "mt-1 text-[11px] leading-tight text-[var(--text-muted)]";
165
+ export const FIELD_CAPTION_CLASS = "mt-1 text-caption leading-tight text-[var(--text-muted)]";
77
166
 
78
167
  export interface FieldHintParts {
79
168
  /** The caller's `aria-describedby` with the caption's id appended — standing
@@ -92,7 +181,8 @@ export interface FieldHintParts {
92
181
  * Split a field's `hint` into the caption under it (attached with `aria-describedby`)
93
182
  * or the "?" on its label line. See {@link isTextHint}.
94
183
  */
95
- export function useFieldHint(hint: ReactNode, describedBy: string | undefined): FieldHintParts {
184
+ export function useFieldHint(hintProp: ReactNode, describedBy: string | undefined): FieldHintParts {
185
+ const hint = useInlineHint(hintProp);
96
186
  const captionId = useId();
97
187
  const text = isTextHint(hint);
98
188
  return {
@@ -197,13 +287,24 @@ export function useLockReason(commit: boolean | undefined, disabledReason: React
197
287
  */
198
288
  export function LockedReason({
199
289
  lock,
290
+ line = false,
200
291
  className,
201
292
  children,
202
293
  }: {
203
294
  lock: LockReason;
295
+ /** Draw the reason as a line under the control instead (0.32, §4) — see
296
+ * {@link DisabledReasonLine}; the caller decides with {@link useDisabledReasonLine}. */
297
+ line?: boolean;
204
298
  className?: string;
205
299
  children: ReactNode;
206
300
  }) {
301
+ if (line) {
302
+ return (
303
+ <DisabledReasonLine id={lock.reasonId} reason={lock.reason} className={className}>
304
+ {children}
305
+ </DisabledReasonLine>
306
+ );
307
+ }
207
308
  return (
208
309
  <Tooltip label={lock.reason} className={className}>
209
310
  <>
@@ -216,6 +317,87 @@ export function LockedReason({
216
317
  );
217
318
  }
218
319
 
320
+ /* ── no fact only in a tooltip (0.32, docs/text-size-harmonization.md §4, §10.8) ── */
321
+
322
+ /**
323
+ * Where a control's `disabledReason` is shown (§4 "No fact only in a tooltip"):
324
+ *
325
+ * - `"auto"` (default): as a LINE under the control at Large and Extra large and on a
326
+ * touch screen (`useInlineFacts`) — the reader asked for bigger type, or cannot hover
327
+ * — and in the kit {@link Tooltip} otherwise, where a pointer can hover it open and
328
+ * the form keeps its rhythm. Inside {@link CompactControls} (a top bar, a dense row's
329
+ * inline actions) it stays the tooltip at every size: there is no room for a line.
330
+ * - `"line"`: always the line — a form whose locked submit should say why at once.
331
+ * - `"tooltip"`: always the tooltip — a place the caller knows has no room, and where
332
+ * the reason is also said elsewhere on the screen (a banner over the form).
333
+ *
334
+ * Either way the reason is the control's `aria-describedby`, so a screen reader hears
335
+ * it on focus; the line IS that description (one copy), the tooltip keeps its hidden
336
+ * copy as before.
337
+ */
338
+ export type DisabledReasonDisplay = "auto" | "line" | "tooltip";
339
+
340
+ /**
341
+ * Marks a region where the controls must stay compact at every text size (§4, §10.8):
342
+ * an IconButton keeps its icon alone (its `label` stays the name and the tooltip) and a
343
+ * `disabledReason` stays in the tooltip rather than a line. AppShell puts its top bar
344
+ * in one ("the top bar keeps its essential icons"); `RowActions` its inline icons.
345
+ *
346
+ * For a toolbar that has no room, not for a whole page: the contract asks that the icon
347
+ * alone be rare. A dense row's actions belong in `RowActions`, which collapses them
348
+ * into a "⋯" menu at Large instead.
349
+ */
350
+ export const CompactControlsContext = createContext(false);
351
+
352
+ export function CompactControls({ children, compact = true }: { children: ReactNode; compact?: boolean }) {
353
+ return <CompactControlsContext.Provider value={compact}>{children}</CompactControlsContext.Provider>;
354
+ }
355
+
356
+ /** Whether a control's `disabledReason` is drawn as a line here — see
357
+ * {@link DisabledReasonDisplay}. For the kit's own controls (Button, IconButton, Chip,
358
+ * ToggleGroup) and an app's control that has a reason to give. */
359
+ export function useDisabledReasonLine(display: DisabledReasonDisplay = "auto"): boolean {
360
+ const compact = useContext(CompactControlsContext);
361
+ const inline = useInlineFacts();
362
+ if (display === "line") return true;
363
+ if (display === "tooltip") return false;
364
+ return inline && !compact;
365
+ }
366
+
367
+ /** The reason line's type: a field caption's ({@link FIELD_CAPTION_CLASS}), so a locked
368
+ * control's "why" reads like a field's standing advice. Capped, so a long reason under
369
+ * a small control wraps rather than widening its row without end. */
370
+ export const DISABLED_REASON_LINE_CLASS =
371
+ "mt-1 block max-w-[20rem] text-caption leading-tight text-[var(--text-muted)]";
372
+
373
+ /**
374
+ * A control and its `disabledReason` as a line under it — the `"line"` form of
375
+ * {@link DisabledReasonDisplay}. The wrapper is the flex item now (`className`: the
376
+ * control's own layout, e.g. `self-stretch`); `items-start` keeps the control its own
377
+ * width, and a column parent still stretches the wrapper (FormActions' phone stack).
378
+ * The line carries `id`, which the control's `aria-describedby` names.
379
+ */
380
+ export function DisabledReasonLine({
381
+ id,
382
+ reason,
383
+ className,
384
+ children,
385
+ }: {
386
+ id: string;
387
+ reason: ReactNode;
388
+ className?: string;
389
+ children: ReactNode;
390
+ }) {
391
+ return (
392
+ <span data-slot="disabled-reason" className={cn("inline-flex max-w-full flex-col items-start", className)}>
393
+ {children}
394
+ <span id={id} className={DISABLED_REASON_LINE_CLASS}>
395
+ {reason}
396
+ </span>
397
+ </span>
398
+ );
399
+ }
400
+
219
401
  /* ── groups ───────────────────────────────────────────────────────────────── */
220
402
 
221
403
  /**
@@ -234,7 +416,7 @@ export function hasContent(node: ReactNode): boolean {
234
416
  return node !== undefined && node !== null && node !== false && node !== "";
235
417
  }
236
418
 
237
- const FIELD_ERROR_CLASS = "mt-1 text-[11px] leading-tight text-[var(--danger)]";
419
+ const FIELD_ERROR_CLASS = "mt-1 text-caption leading-tight text-[var(--danger)]";
238
420
 
239
421
  export interface FieldMessages {
240
422
  /** `invalid`, a message, or an `aria-invalid` from outside (a form library). */
@@ -251,7 +433,7 @@ export interface FieldMessages {
251
433
 
252
434
  /** `hint` / `error` / `invalid` resolved the way `Select` resolves them. */
253
435
  export function useFieldMessages({
254
- hint,
436
+ hint: hintProp,
255
437
  error,
256
438
  invalid,
257
439
  describedBy,
@@ -263,6 +445,7 @@ export function useFieldMessages({
263
445
  describedBy?: string;
264
446
  ariaInvalid?: unknown;
265
447
  }): FieldMessages {
448
+ const hint = useInlineHint(hintProp);
266
449
  const hintId = useId();
267
450
  const errorId = useId();
268
451
  const textHint = isTextHint(hint);
@@ -3,7 +3,9 @@ import type { ReactNode } from "react";
3
3
  import { AlertCircle, Check, Loader2, Pencil, PencilLine } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
5
  import { Tooltip } from "./tooltip";
6
+ import { FOCUS_RING } from "./focus-ring";
6
7
  import { useKitLabels } from "../i18n/kit-labels";
8
+ import { useLargeText } from "../hooks/use-large-text";
7
9
 
8
10
  /**
9
11
  * Sync state for a field backed by a database row.
@@ -252,7 +254,10 @@ export interface FieldSyncIndicatorProps {
252
254
  error?: Error | null;
253
255
  /** Override any of the five English defaults. */
254
256
  labels?: Partial<FieldSyncLabels>;
255
- /** Render the label beside the icon instead of only in the tooltip. */
257
+ /** Render the label beside the icon instead of only in the tooltip. Always on at
258
+ * Large and Extra large (0.32, docs/text-size-harmonization.md §4 "No fact only in a
259
+ * tooltip"): the reader who asked for bigger type gets the words, not a 14 px glyph
260
+ * and a bubble. */
256
261
  showLabel?: boolean;
257
262
  /** When given, the error state renders a retry button after the icon. */
258
263
  onRetry?: () => void;
@@ -282,6 +287,8 @@ export function FieldSyncIndicator({
282
287
  className,
283
288
  }: FieldSyncIndicatorProps) {
284
289
  const l = useKitLabels("fieldSync", DEFAULT_FIELD_SYNC_LABELS, labels);
290
+ const large = useLargeText();
291
+ showLabel = showLabel || large;
285
292
  const { color, Icon } = STATE_STYLE[state];
286
293
  const text =
287
294
  state === "error" ? (error?.message?.trim() ? error.message : l.error) : l[state];
@@ -312,7 +319,11 @@ export function FieldSyncIndicator({
312
319
  <button
313
320
  type="button"
314
321
  onClick={onRetry}
315
- className="rounded underline underline-offset-2 hover:no-underline focus-visible:outline-2 focus-visible:outline-offset-2"
322
+ // The kit's focus frame (0.32, §5), stood off the word.
323
+ className={cn(
324
+ "rounded underline underline-offset-2 hover:no-underline focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)]",
325
+ FOCUS_RING,
326
+ )}
316
327
  style={{ color }}
317
328
  >
318
329
  {l.retry}
@@ -461,7 +472,10 @@ export function FieldSyncRow<T>({
461
472
  type="button"
462
473
  onClick={retry}
463
474
  aria-label={l.retry}
464
- className="pointer-events-auto flex rounded-full text-[var(--status-error)] focus-visible:outline-2 focus-visible:outline-offset-2"
475
+ className={cn(
476
+ "pointer-events-auto flex rounded-full text-[var(--status-error)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)]",
477
+ FOCUS_RING,
478
+ )}
465
479
  >
466
480
  <AlertCircle aria-hidden className="size-4" />
467
481
  </button>
@@ -177,12 +177,12 @@ export function Field({
177
177
  ? children(control, { labelId: label !== undefined ? labelId : undefined })
178
178
  : children}
179
179
  {hasHint && (
180
- <p id={hintId} className="text-[11px] leading-tight text-[var(--text-muted)]">
180
+ <p id={hintId} className="text-caption leading-tight text-[var(--text-muted)]">
181
181
  {hint}
182
182
  </p>
183
183
  )}
184
184
  {hasError && (
185
- <p id={errorId} className="text-[11px] leading-tight text-[var(--danger)]">
185
+ <p id={errorId} className="text-caption leading-tight text-[var(--danger)]">
186
186
  {error}
187
187
  </p>
188
188
  )}
@@ -14,6 +14,7 @@ import type {
14
14
  import { X } from "lucide-react";
15
15
 
16
16
  import { cn } from "../lib/cn";
17
+ import { FOCUS_RING } from "./focus-ring";
17
18
  import { dirOf, type Direction } from "../lib/direction";
18
19
  import { registerFloating } from "../lib/floating-stack";
19
20
  import { useKitLabels, useKitLink } from "../i18n/kit-labels";
@@ -243,7 +244,8 @@ export function FloatingActionButton({
243
244
  "bg-[var(--bg-surface)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-primary)]",
244
245
  pressed === true &&
245
246
  "border-[var(--brand)] bg-[var(--brand-bg)] text-[var(--brand)] hover:bg-[var(--brand-bg-hover)]",
246
- "outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)] focus-visible:ring-offset-2",
247
+ "outline-none focus-visible:ring-offset-2",
248
+ FOCUS_RING,
247
249
  className,
248
250
  )}
249
251
  >
@@ -505,7 +507,7 @@ export function FloatingPanel({
505
507
  setOpen(false);
506
508
  }}
507
509
  aria-label={labels.close}
508
- className="-me-1.5 shrink-0 rounded p-1.5 text-[var(--text-muted)] outline-none hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:ring-2 focus-visible:ring-[var(--brand)]"
510
+ className={cn("-me-1.5 shrink-0 rounded p-1.5 text-[var(--text-muted)] outline-none hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]", FOCUS_RING)}
509
511
  >
510
512
  <X aria-hidden className="size-5" />
511
513
  </button>
@@ -687,7 +689,8 @@ export function FloatingAction({
687
689
  const isLink = href !== undefined;
688
690
  const look = cn(
689
691
  "relative flex size-12 items-center justify-center transition-colors",
690
- "outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--brand)]",
692
+ "outline-none focus-visible:ring-inset",
693
+ FOCUS_RING,
691
694
  "disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:size-5",
692
695
  variant === "primary"
693
696
  ? "bg-[var(--brand)] text-[var(--brand-contrast)] hover:bg-[var(--brand-hover)]"
@@ -704,7 +707,7 @@ export function FloatingAction({
704
707
  aria-hidden
705
708
  data-badge=""
706
709
  className={cn(
707
- "pointer-events-none absolute end-1 top-1 h-4 min-w-4 rounded-full px-1 text-center text-[10px] leading-4 font-semibold",
710
+ "pointer-events-none absolute end-1 top-1 h-4 min-w-4 rounded-full px-1 text-center text-micro leading-4 font-semibold",
708
711
  BADGE_TONE[badgeTone],
709
712
  )}
710
713
  >
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The kit's focus frame (§5): a brand ring `--focus-ring-width` wide — 2 px, 3 px under
3
+ * More contrast (tokens.css) — on keyboard focus only. ONE frame instead of the hundred
4
+ * spelled-out `focus-visible:ring-2` copies, so the contrast setting can thicken all of
5
+ * them at once. Compose with `ring-inset` or `ring-offset-*` where a component needs it.
6
+ */
7
+ export const FOCUS_RING =
8
+ "focus-visible:outline-none focus-visible:ring-[length:var(--focus-ring-width)] focus-visible:ring-[var(--brand)]";
9
+
10
+ /** Just the width of {@link FOCUS_RING}, for a ring in another colour (a danger control's,
11
+ * `--brand-contrast` on a filled button): `cn(FOCUS_RING_WIDTH, "focus-visible:ring-[var(--danger)]")`. */
12
+ export const FOCUS_RING_WIDTH = "focus-visible:ring-[length:var(--focus-ring-width)]";
@@ -3,7 +3,8 @@ import type { ComponentPropsWithoutRef, ReactNode, Ref } from "react";
3
3
  import type { LucideIcon } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
5
  import { useKitLabels } from "../i18n/kit-labels";
6
- import { useMediaQuery } from "../hooks/use-media-query";
6
+ import { useBreakpoint, usePhoneLayout } from "../hooks/use-breakpoint";
7
+ import { useLargeText } from "../hooks/use-large-text";
7
8
  import { isApplePlatform } from "../hooks/use-hotkey";
8
9
  import { Button, Spinner, type ButtonProps, type ButtonSize, type ButtonVariant } from "./ui";
9
10
  import { useWriteLock } from "./write-lock";
@@ -43,11 +44,29 @@ export type FormActionsAlign = "start" | "center" | "end" | "between";
43
44
  */
44
45
  export type FormActionsPlacement = "inline" | "sticky" | "dialog";
45
46
 
47
+ /**
48
+ * When the row's buttons STACK, one per line at the row's full width (0.32,
49
+ * docs/text-size-harmonization.md §4 "FormActions stack full width on a phone layout"):
50
+ *
51
+ * - `"auto"` (default): on the phone layout (`usePhoneLayout()`, `max-md:` at the text
52
+ * size in force) at Large and Extra large. There a phone is 312 or 260 px of text
53
+ * wide, and Cancel and a "Save changes" side by side wrap into a ragged pair with
54
+ * one 48 px target squeezed against the other. Normal keeps the row the apps tuned.
55
+ * - `"phone"`: on the phone layout at every text size.
56
+ * - `"never"`: always the row.
57
+ *
58
+ * Stacked, the order is the DOM's — the start group (the destructive action), the extra
59
+ * actions, Cancel, then Save at the bottom, under the thumb — so the visual order and
60
+ * the tab order stay one.
61
+ */
62
+ export type FormActionsStack = "auto" | "phone" | "never";
63
+
46
64
  /**
47
65
  * A placement per breakpoint, mobile first — `{ base: "sticky", md: "inline" }` is a
48
66
  * Save row stuck to the bottom of a phone's long form and in the flow under the last
49
- * field from 768px up, where the form fits. The breakpoints are Tailwind's (sm 640,
50
- * md 768, lg 1024, xl 1280px), as `BulkActionBar`'s `variant` breakpoints are, and
67
+ * field from 768px up, where the form fits. The breakpoints are the classes' (sm 640,
68
+ * md 768, lg 1024, xl 1280px at Normal text size, and 1.25 / 1.5 times that at Large /
69
+ * Extra large, as `md:` is since 0.32), as `BulkActionBar`'s `variant` breakpoints are, and
51
70
  * resolved the same way, in JS: the sticky row is positioned by inline style, which a
52
71
  * `md:` class cannot reach. keksdose switched `placement` on its own media query at
53
72
  * every long form for want of this. The breakpoints are the VIEWPORT's, not the
@@ -61,12 +80,9 @@ export interface ResponsiveFormActionsPlacement {
61
80
  xl?: FormActionsPlacement;
62
81
  }
63
82
 
64
- const BREAKPOINTS = [
65
- ["xl", "(min-width: 1280px)"],
66
- ["lg", "(min-width: 1024px)"],
67
- ["md", "(min-width: 768px)"],
68
- ["sm", "(min-width: 640px)"],
69
- ] as const;
83
+ /** Widest first: the first that matches and names a value wins. Each is the class's own
84
+ * breakpoint at the text size in force (`useBreakpoint`), so `md` here is `md:` there. */
85
+ const BREAKPOINTS = ["xl", "lg", "md", "sm"] as const;
70
86
 
71
87
  /** The placement in force: the widest breakpoint that matches AND names one, else
72
88
  * `base`. The queries are subscribed unconditionally (hooks cannot be skipped); a
@@ -76,13 +92,13 @@ function useResolvedPlacement(
76
92
  placement: FormActionsPlacement | ResponsiveFormActionsPlacement,
77
93
  ): FormActionsPlacement {
78
94
  const matches = {
79
- xl: useMediaQuery(BREAKPOINTS[0][1], false),
80
- lg: useMediaQuery(BREAKPOINTS[1][1], false),
81
- md: useMediaQuery(BREAKPOINTS[2][1], false),
82
- sm: useMediaQuery(BREAKPOINTS[3][1], false),
95
+ xl: useBreakpoint("xl"),
96
+ lg: useBreakpoint("lg"),
97
+ md: useBreakpoint("md"),
98
+ sm: useBreakpoint("sm"),
83
99
  };
84
100
  if (typeof placement === "string") return placement;
85
- for (const [key] of BREAKPOINTS) {
101
+ for (const key of BREAKPOINTS) {
86
102
  const p = placement[key];
87
103
  if (matches[key] && p !== undefined) return p;
88
104
  }
@@ -296,6 +312,9 @@ export interface FormActionsProps extends Omit<ComponentPropsWithoutRef<"div">,
296
312
  * `form.submitShortcut` label), dimmed. Hidden on a touch-only device, which has no
297
313
  * keyboard to press it on. Default `false`. */
298
314
  submitShortcutHint?: boolean;
315
+ /** When the buttons stack full width — see {@link FormActionsStack}. Default `auto`:
316
+ * on the phone layout at Large and Extra large. */
317
+ stack?: FormActionsStack;
299
318
  /** Extra actions, placed before Cancel. */
300
319
  children?: ReactNode;
301
320
  }
@@ -360,11 +379,18 @@ export function FormActions({
360
379
  form,
361
380
  submitShortcut,
362
381
  submitShortcutHint = false,
382
+ stack = "auto",
363
383
  children,
364
384
  className,
365
385
  style,
366
386
  ...rest
367
387
  }: FormActionsProps) {
388
+ const phone = usePhoneLayout();
389
+ const large = useLargeText();
390
+ const stacked = stack !== "never" && phone && (stack === "phone" || large);
391
+ // Stacked, a button fills its line — also inside the reason line's wrapper, which the
392
+ // column stretches but which keeps its control at the control's own width.
393
+ const full = stacked ? "w-full" : undefined;
368
394
  const labels = useKitLabels("form", DEFAULT_FORM_ACTIONS_LABELS);
369
395
  const lock = useWriteLock();
370
396
  const rootRef = useRef<HTMLDivElement>(null);
@@ -430,6 +456,7 @@ export function FormActions({
430
456
  disabled={destructive.disabled}
431
457
  disabledReason={destructive.disabledReason}
432
458
  commit={commit}
459
+ className={full}
433
460
  >
434
461
  {destructive.label}
435
462
  </Button>
@@ -467,18 +494,27 @@ export function FormActions({
467
494
  }
468
495
  : style
469
496
  }
470
- className={cn("flex flex-wrap items-center gap-2", ALIGN_CLASS[justify], PLACEMENT_CLASS[placement], className)}
497
+ data-stacked={stacked || undefined}
498
+ className={cn(
499
+ stacked ? "flex flex-col items-stretch gap-2" : cn("flex flex-wrap items-center gap-2", ALIGN_CLASS[justify]),
500
+ PLACEMENT_CLASS[placement],
501
+ className,
502
+ )}
471
503
  >
472
504
  {(hasDestructive || hasStart) && (
473
- <div className="me-auto flex min-w-0 flex-wrap items-center gap-2">
505
+ <div
506
+ className={
507
+ stacked ? "flex min-w-0 flex-col items-stretch gap-2" : "me-auto flex min-w-0 flex-wrap items-center gap-2"
508
+ }
509
+ >
474
510
  {destructiveNode}
475
511
  {hasStart && startSlot}
476
512
  </div>
477
513
  )}
478
- <div className="flex flex-wrap items-center gap-2">
514
+ <div className={stacked ? "flex flex-col items-stretch gap-2" : "flex flex-wrap items-center gap-2"}>
479
515
  {children}
480
516
  {onCancel && (
481
- <Button type="button" variant={cancelVariant} size={size} onClick={onCancel}>
517
+ <Button type="button" variant={cancelVariant} size={size} onClick={onCancel} className={full}>
482
518
  {cancelLabel ?? labels.cancel}
483
519
  </Button>
484
520
  )}
@@ -502,6 +538,7 @@ export function FormActions({
502
538
  // spinner would say it had not.
503
539
  commit={commit && !pending}
504
540
  aria-busy={pending || undefined}
541
+ className={cn(submitProps?.className, full)}
505
542
  >
506
543
  {pending ? (
507
544
  <Spinner label={null} className={glyphClass} />
@@ -10,6 +10,17 @@ import { useOverlayHistory } from "../hooks/use-overlay-history";
10
10
  import { useCloseTransition } from "../hooks/use-close-transition";
11
11
  import { useFocusTrap } from "../hooks/use-focus-trap";
12
12
 
13
+ /**
14
+ * The padding around the dialog's scrolling body, as a CSS length (0.32,
15
+ * docs/text-size-harmonization.md §10.11). Exported because a caller's sticky footer has
16
+ * to reach across it: Chrome resolves a sticky offset against the scrollport's CONTENT
17
+ * box, so a pinned row sits this far short of the panel's edge unless it cancels it
18
+ * (keksdose's editor footer). In rem, so it grows with the text size — keksdose's px
19
+ * copy (`SCROLLER_GUTTER_PX = 12`) was 3–6 px off at 125/150 %. Use it in `calc()`:
20
+ * `bottom: calc(${inset}px - ${DIALOG_GUTTER})`.
21
+ */
22
+ export const DIALOG_GUTTER = "0.75rem";
23
+
13
24
  /**
14
25
  * Named and exported, because `ComponentProps<typeof FullBleedDialog>` was the only way
15
26
  * to say "the props of this" and five call sites across keksdose and kastlan had already
@@ -246,12 +257,15 @@ export function FullBleedDialog({
246
257
  <X className="size-5" />
247
258
  </button>
248
259
  </div>
249
- {/* px-3, not px-4: every pixel of chrome here is width the form fields lose
250
- on a phone (feedback #32). The body is the only thing that scrolls, so the
251
- header stays put and the page underneath cannot move at all. */}
260
+ {/* DIALOG_GUTTER (0.75rem, the px-3 py-3 it was), not px-4: every pixel of
261
+ chrome here is width the form fields lose on a phone (feedback #32). The
262
+ body is the only thing that scrolls, so the header stays put and the page
263
+ underneath cannot move at all. */}
252
264
  {/* `min-h-0` so it is the BODY that gives way to a footer, not the footer that
253
265
  is pushed off the bottom edge. */}
254
- <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-3 py-3">{children}</div>
266
+ <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain" style={{ padding: DIALOG_GUTTER }}>
267
+ {children}
268
+ </div>
255
269
  {footer !== undefined && footer !== null && footer !== false && (
256
270
  <div
257
271
  data-full-bleed-footer=""
@@ -139,7 +139,7 @@ export function ImageGrid({
139
139
  onClick={() => (onOpen ? onOpen(i) : setOpenIndex(i))}
140
140
  className={cn(
141
141
  "block w-full overflow-hidden rounded-md border border-[var(--border)] bg-[var(--bg-hover)]",
142
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--brand)]",
142
+ "focus-visible:outline-[length:var(--focus-ring-width)] focus-visible:outline-offset-2 focus-visible:outline-[var(--brand)]",
143
143
  ASPECT_CLASS[aspect],
144
144
  )}
145
145
  >