@eifi1/ui-kit 0.31.1 → 0.32.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 (538) 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/invitations-panel.d.ts +8 -3
  5. package/dist/admin/invitations-panel.js +29 -26
  6. package/dist/admin/invitations-panel.js.map +1 -1
  7. package/dist/admin/reviewer-scope-editor.d.ts +8 -3
  8. package/dist/admin/role-select.d.ts +10 -5
  9. package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
  10. package/dist/admin/user-roster.d.ts +10 -5
  11. package/dist/admin/user-roster.js +8 -47
  12. package/dist/admin/user-roster.js.map +1 -1
  13. package/dist/auth/auth-errors.d.ts +43 -8
  14. package/dist/auth/auth-errors.js +24 -1
  15. package/dist/auth/auth-errors.js.map +1 -1
  16. package/dist/auth/register-form.d.ts +8 -3
  17. package/dist/billing/billing-banner.d.ts +184 -0
  18. package/dist/billing/billing-banner.js +98 -0
  19. package/dist/billing/billing-banner.js.map +1 -0
  20. package/dist/billing/billing-labels.d.ts +100 -0
  21. package/dist/billing/billing-labels.js +74 -0
  22. package/dist/billing/billing-labels.js.map +1 -0
  23. package/dist/billing/billing-lock.d.ts +158 -0
  24. package/dist/billing/billing-lock.js +17 -0
  25. package/dist/billing/billing-lock.js.map +1 -0
  26. package/dist/billing/billing-standing.d.ts +149 -0
  27. package/dist/billing/billing-standing.js +28 -0
  28. package/dist/billing/billing-standing.js.map +1 -0
  29. package/dist/billing/plan-card.d.ts +248 -0
  30. package/dist/billing/plan-card.js +248 -0
  31. package/dist/billing/plan-card.js.map +1 -0
  32. package/dist/billing/plan-limit-notice.d.ts +156 -0
  33. package/dist/billing/plan-limit-notice.js +76 -0
  34. package/dist/billing/plan-limit-notice.js.map +1 -0
  35. package/dist/billing/plan-limit.d.ts +34 -0
  36. package/dist/billing/plan-limit.js +20 -0
  37. package/dist/billing/plan-limit.js.map +1 -0
  38. package/dist/billing/plan-price.d.ts +61 -0
  39. package/dist/billing/plan-price.js +45 -0
  40. package/dist/billing/plan-price.js.map +1 -0
  41. package/dist/billing/subscription-actions.d.ts +133 -0
  42. package/dist/billing/subscription-actions.js +28 -0
  43. package/dist/billing/subscription-actions.js.map +1 -0
  44. package/dist/billing/subscription-status-chip.d.ts +149 -0
  45. package/dist/billing/subscription-status-chip.js +48 -0
  46. package/dist/billing/subscription-status-chip.js.map +1 -0
  47. package/dist/chart.d.ts +9 -3
  48. package/dist/components/account-chips.d.ts +16 -4
  49. package/dist/components/account-chips.js +21 -3
  50. package/dist/components/account-chips.js.map +1 -1
  51. package/dist/components/alert-banner.d.ts +6 -1
  52. package/dist/components/alert-banner.js +4 -3
  53. package/dist/components/alert-banner.js.map +1 -1
  54. package/dist/components/amount-input.d.ts +8 -3
  55. package/dist/components/amount-input.js +5 -5
  56. package/dist/components/amount-input.js.map +1 -1
  57. package/dist/components/appearance-labels.d.ts +32 -0
  58. package/dist/components/appearance-labels.js +18 -0
  59. package/dist/components/appearance-labels.js.map +1 -0
  60. package/dist/components/autocomplete.js +2 -2
  61. package/dist/components/autocomplete.js.map +1 -1
  62. package/dist/components/breadcrumbs.js +3 -2
  63. package/dist/components/breadcrumbs.js.map +1 -1
  64. package/dist/components/bulk-action-bar.d.ts +2 -2
  65. package/dist/components/bulk-action-bar.js +7 -12
  66. package/dist/components/bulk-action-bar.js.map +1 -1
  67. package/dist/components/button-group.d.ts +8 -3
  68. package/dist/components/calculator.d.ts +8 -3
  69. package/dist/components/calculator.js +4 -3
  70. package/dist/components/calculator.js.map +1 -1
  71. package/dist/components/calendar-heatmap.js +5 -5
  72. package/dist/components/calendar-heatmap.js.map +1 -1
  73. package/dist/components/chart-height.d.ts +23 -0
  74. package/dist/components/chart-height.js +15 -0
  75. package/dist/components/chart-height.js.map +1 -0
  76. package/dist/components/chart-zoom.js +6 -1
  77. package/dist/components/chart-zoom.js.map +1 -1
  78. package/dist/components/checkbox-group.js +2 -2
  79. package/dist/components/checkbox-group.js.map +1 -1
  80. package/dist/components/checkbox.d.ts +10 -0
  81. package/dist/components/checkbox.js +25 -10
  82. package/dist/components/checkbox.js.map +1 -1
  83. package/dist/components/chip.d.ts +19 -3
  84. package/dist/components/chip.js +47 -21
  85. package/dist/components/chip.js.map +1 -1
  86. package/dist/components/choice-card.d.ts +1 -1
  87. package/dist/components/choice-card.js +8 -7
  88. package/dist/components/choice-card.js.map +1 -1
  89. package/dist/components/column-mapper.js +1 -1
  90. package/dist/components/column-mapper.js.map +1 -1
  91. package/dist/components/combobox-core.js +8 -7
  92. package/dist/components/combobox-core.js.map +1 -1
  93. package/dist/components/combobox.js +8 -8
  94. package/dist/components/combobox.js.map +1 -1
  95. package/dist/components/confirm-dialog.d.ts +8 -3
  96. package/dist/components/copy-button.d.ts +10 -5
  97. package/dist/components/country-select.js +1 -1
  98. package/dist/components/country-select.js.map +1 -1
  99. package/dist/components/currency-select.js +3 -3
  100. package/dist/components/currency-select.js.map +1 -1
  101. package/dist/components/danger-confirm.d.ts +8 -3
  102. package/dist/components/data-table-cells.d.ts +1 -1
  103. package/dist/components/data-table-filter-popover.d.ts +1 -1
  104. package/dist/components/data-table-filter-popover.js +4 -1
  105. package/dist/components/data-table-filter-popover.js.map +1 -1
  106. package/dist/components/data-table-filters.d.ts +1 -1
  107. package/dist/components/data-table-labels.d.ts +1 -1
  108. package/dist/components/data-table-pagination.d.ts +1 -1
  109. package/dist/components/data-table.d.ts +1 -1
  110. package/dist/components/data-table.js +123 -52
  111. package/dist/components/data-table.js.map +1 -1
  112. package/dist/components/date-picker.d.ts +1 -1
  113. package/dist/components/date-picker.js +7 -5
  114. package/dist/components/date-picker.js.map +1 -1
  115. package/dist/components/description-list.js +2 -2
  116. package/dist/components/description-list.js.map +1 -1
  117. package/dist/components/dialog-frame.js +2 -1
  118. package/dist/components/dialog-frame.js.map +1 -1
  119. package/dist/components/disclosure.js +3 -2
  120. package/dist/components/disclosure.js.map +1 -1
  121. package/dist/components/dropdown.d.ts +1 -1
  122. package/dist/components/dropdown.js +7 -1
  123. package/dist/components/dropdown.js.map +1 -1
  124. package/dist/components/facing-pair.d.ts +19 -6
  125. package/dist/components/facing-pair.js +3 -2
  126. package/dist/components/facing-pair.js.map +1 -1
  127. package/dist/components/field-parts.d.ts +102 -6
  128. package/dist/components/field-parts.js +81 -6
  129. package/dist/components/field-parts.js.map +1 -1
  130. package/dist/components/field-sync.d.ts +4 -1
  131. package/dist/components/field-sync.js +12 -2
  132. package/dist/components/field-sync.js.map +1 -1
  133. package/dist/components/field.js +2 -2
  134. package/dist/components/field.js.map +1 -1
  135. package/dist/components/file-button.d.ts +8 -3
  136. package/dist/components/file-dropzone.d.ts +8 -3
  137. package/dist/components/floating-panel.js +7 -4
  138. package/dist/components/floating-panel.js.map +1 -1
  139. package/dist/components/focus-ring.d.ts +12 -0
  140. package/dist/components/focus-ring.js +8 -0
  141. package/dist/components/focus-ring.js.map +1 -0
  142. package/dist/components/form-actions.d.ts +8 -3
  143. package/dist/components/form-actions.js +33 -19
  144. package/dist/components/form-actions.js.map +1 -1
  145. package/dist/components/full-bleed-dialog.d.ts +11 -1
  146. package/dist/components/full-bleed-dialog.js +3 -1
  147. package/dist/components/full-bleed-dialog.js.map +1 -1
  148. package/dist/components/iban-input.d.ts +8 -3
  149. package/dist/components/image-grid.js +1 -1
  150. package/dist/components/image-grid.js.map +1 -1
  151. package/dist/components/inline-edit-field.js +6 -3
  152. package/dist/components/inline-edit-field.js.map +1 -1
  153. package/dist/components/language-select.d.ts +8 -3
  154. package/dist/components/legal-page.d.ts +26 -5
  155. package/dist/components/legal-page.js +3 -1
  156. package/dist/components/legal-page.js.map +1 -1
  157. package/dist/components/legal.d.ts +8 -3
  158. package/dist/components/legal.js +2 -1
  159. package/dist/components/legal.js.map +1 -1
  160. package/dist/components/lightbox.js +24 -2
  161. package/dist/components/lightbox.js.map +1 -1
  162. package/dist/components/line-items.js +2 -1
  163. package/dist/components/line-items.js.map +1 -1
  164. package/dist/components/list.d.ts +5 -0
  165. package/dist/components/list.js +13 -9
  166. package/dist/components/list.js.map +1 -1
  167. package/dist/components/measured-grid.d.ts +3 -1
  168. package/dist/components/measured-grid.js +38 -20
  169. package/dist/components/measured-grid.js.map +1 -1
  170. package/dist/components/menu-item.js +2 -1
  171. package/dist/components/menu-item.js.map +1 -1
  172. package/dist/components/mini-calendar.js +8 -4
  173. package/dist/components/mini-calendar.js.map +1 -1
  174. package/dist/components/money-field.d.ts +8 -3
  175. package/dist/components/month-picker.js +9 -5
  176. package/dist/components/month-picker.js.map +1 -1
  177. package/dist/components/nav-pills.js +1 -1
  178. package/dist/components/nav-pills.js.map +1 -1
  179. package/dist/components/number-field.d.ts +8 -3
  180. package/dist/components/number-input.d.ts +11 -4
  181. package/dist/components/number-input.js +8 -6
  182. package/dist/components/number-input.js.map +1 -1
  183. package/dist/components/numpad-sheet.d.ts +8 -3
  184. package/dist/components/numpad-sheet.js +2 -1
  185. package/dist/components/numpad-sheet.js.map +1 -1
  186. package/dist/components/one-time-code-input.js +2 -2
  187. package/dist/components/one-time-code-input.js.map +1 -1
  188. package/dist/components/page-contents.js +2 -2
  189. package/dist/components/page-contents.js.map +1 -1
  190. package/dist/components/phone-input.d.ts +8 -3
  191. package/dist/components/phone-input.js +2 -2
  192. package/dist/components/phone-input.js.map +1 -1
  193. package/dist/components/pie-chart.d.ts +8 -2
  194. package/dist/components/pie-chart.js +11 -7
  195. package/dist/components/pie-chart.js.map +1 -1
  196. package/dist/components/popover.d.ts +9 -1
  197. package/dist/components/popover.js +8 -4
  198. package/dist/components/popover.js.map +1 -1
  199. package/dist/components/row-actions.d.ts +100 -0
  200. package/dist/components/row-actions.js +165 -0
  201. package/dist/components/row-actions.js.map +1 -0
  202. package/dist/components/scroll-area.js +1 -1
  203. package/dist/components/scroll-area.js.map +1 -1
  204. package/dist/components/search-field.js +3 -2
  205. package/dist/components/search-field.js.map +1 -1
  206. package/dist/components/series-chart-budget.d.ts +30 -7
  207. package/dist/components/series-chart-budget.js +12 -4
  208. package/dist/components/series-chart-budget.js.map +1 -1
  209. package/dist/components/series-chart-ticks.d.ts +7 -1
  210. package/dist/components/series-chart-ticks.js +6 -0
  211. package/dist/components/series-chart-ticks.js.map +1 -1
  212. package/dist/components/series-chart.d.ts +58 -28
  213. package/dist/components/series-chart.js +42 -32
  214. package/dist/components/series-chart.js.map +1 -1
  215. package/dist/components/settings-fields.d.ts +51 -6
  216. package/dist/components/settings-fields.js +37 -0
  217. package/dist/components/settings-fields.js.map +1 -1
  218. package/dist/components/share-card.d.ts +11 -6
  219. package/dist/components/share-card.js +2 -0
  220. package/dist/components/share-card.js.map +1 -1
  221. package/dist/components/sign-chip.d.ts +4 -0
  222. package/dist/components/sign-chip.js +17 -4
  223. package/dist/components/sign-chip.js.map +1 -1
  224. package/dist/components/signature-pad.d.ts +3 -1
  225. package/dist/components/signature-pad.js +8 -5
  226. package/dist/components/signature-pad.js.map +1 -1
  227. package/dist/components/slider.js +7 -6
  228. package/dist/components/slider.js.map +1 -1
  229. package/dist/components/sparkline.d.ts +5 -1
  230. package/dist/components/sparkline.js +4 -2
  231. package/dist/components/sparkline.js.map +1 -1
  232. package/dist/components/stat-tile.d.ts +17 -1
  233. package/dist/components/stat-tile.js +48 -20
  234. package/dist/components/stat-tile.js.map +1 -1
  235. package/dist/components/status-dot.d.ts +4 -0
  236. package/dist/components/swipeable-row.js +2 -1
  237. package/dist/components/swipeable-row.js.map +1 -1
  238. package/dist/components/switch.d.ts +21 -7
  239. package/dist/components/switch.js +24 -11
  240. package/dist/components/switch.js.map +1 -1
  241. package/dist/components/table.js +2 -2
  242. package/dist/components/table.js.map +1 -1
  243. package/dist/components/text-link.d.ts +8 -3
  244. package/dist/components/text-link.js +1 -1
  245. package/dist/components/text-link.js.map +1 -1
  246. package/dist/components/text.d.ts +7 -2
  247. package/dist/components/text.js +5 -5
  248. package/dist/components/text.js.map +1 -1
  249. package/dist/components/tile-radio.js +2 -1
  250. package/dist/components/tile-radio.js.map +1 -1
  251. package/dist/components/time-input.d.ts +8 -3
  252. package/dist/components/toast.d.ts +18 -2
  253. package/dist/components/toast.js +8 -5
  254. package/dist/components/toast.js.map +1 -1
  255. package/dist/components/toggle-group.d.ts +16 -1
  256. package/dist/components/toggle-group.js +40 -11
  257. package/dist/components/toggle-group.js.map +1 -1
  258. package/dist/components/toggle-legend.d.ts +10 -1
  259. package/dist/components/toggle-legend.js +21 -7
  260. package/dist/components/toggle-legend.js.map +1 -1
  261. package/dist/components/translation-review-parts.d.ts +4 -0
  262. package/dist/components/translation-review.d.ts +4 -0
  263. package/dist/components/translation-review.js +3 -3
  264. package/dist/components/translation-review.js.map +1 -1
  265. package/dist/components/tree-view.js +3 -2
  266. package/dist/components/tree-view.js.map +1 -1
  267. package/dist/components/treemap.d.ts +5 -2
  268. package/dist/components/treemap.js +33 -16
  269. package/dist/components/treemap.js.map +1 -1
  270. package/dist/components/ui.d.ts +8 -3
  271. package/dist/components/ui.js +202 -114
  272. package/dist/components/ui.js.map +1 -1
  273. package/dist/components/use-table-state.d.ts +1 -1
  274. package/dist/components/user-avatar.d.ts +5 -1
  275. package/dist/components/user-avatar.js +1 -1
  276. package/dist/components/user-avatar.js.map +1 -1
  277. package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
  278. package/dist/data-table.d.ts +1 -1
  279. package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-BvdQdbv3.d.ts} +605 -58
  280. package/dist/demo/demo-banner.js +1 -0
  281. package/dist/demo/demo-banner.js.map +1 -1
  282. package/dist/feedback/feedback-attachment.d.ts +8 -3
  283. package/dist/feedback/feedback-context.d.ts +8 -3
  284. package/dist/feedback/feedback-crash.d.ts +8 -3
  285. package/dist/feedback/feedback-dialog.d.ts +8 -3
  286. package/dist/feedback/feedback-dialog.js +3 -3
  287. package/dist/feedback/feedback-dialog.js.map +1 -1
  288. package/dist/feedback/feedback-inbox.d.ts +8 -3
  289. package/dist/feedback/feedback-inbox.js +2 -2
  290. package/dist/feedback/feedback-inbox.js.map +1 -1
  291. package/dist/feedback/feedback-labels.d.ts +8 -3
  292. package/dist/feedback/feedback-menu.d.ts +9 -4
  293. package/dist/feedback/feedback-record.d.ts +8 -3
  294. package/dist/feedback/feedback-row-detail.d.ts +8 -3
  295. package/dist/feedback/feedback-status-undo.d.ts +8 -3
  296. package/dist/feedback/feedback-submit.d.ts +8 -3
  297. package/dist/feedback/feedback-swipe.d.ts +8 -3
  298. package/dist/feedback/feedback-table.d.ts +8 -3
  299. package/dist/feedback/feedback-thread.d.ts +8 -3
  300. package/dist/feedback/feedback-thread.js +3 -3
  301. package/dist/feedback/feedback-thread.js.map +1 -1
  302. package/dist/feedback.d.ts +8 -3
  303. package/dist/hooks/use-anchored-panel.d.ts +32 -3
  304. package/dist/hooks/use-anchored-panel.js +21 -3
  305. package/dist/hooks/use-anchored-panel.js.map +1 -1
  306. package/dist/hooks/use-breakpoint.d.ts +77 -0
  307. package/dist/hooks/use-breakpoint.js +50 -0
  308. package/dist/hooks/use-breakpoint.js.map +1 -0
  309. package/dist/hooks/use-file-drop.d.ts +8 -3
  310. package/dist/hooks/use-large-text.d.ts +29 -0
  311. package/dist/hooks/use-large-text.js +20 -0
  312. package/dist/hooks/use-large-text.js.map +1 -0
  313. package/dist/hooks/use-windowed-rows.d.ts +52 -8
  314. package/dist/hooks/use-windowed-rows.js +94 -5
  315. package/dist/hooks/use-windowed-rows.js.map +1 -1
  316. package/dist/i18n/defaults.d.ts +8 -3
  317. package/dist/i18n/defaults.js +8 -0
  318. package/dist/i18n/defaults.js.map +1 -1
  319. package/dist/i18n/german.d.ts +8 -3
  320. package/dist/i18n/german.js +89 -2
  321. package/dist/i18n/german.js.map +1 -1
  322. package/dist/i18n/kit-labels.d.ts +8 -3
  323. package/dist/i18n/kit-labels.js.map +1 -1
  324. package/dist/i18n/languages.d.ts +10 -5
  325. package/dist/i18n/locales/de-CH.d.ts +8 -3
  326. package/dist/i18n/locales/en.d.ts +8 -3
  327. package/dist/i18n/locales/es.d.ts +8 -3
  328. package/dist/i18n/locales/es.js +84 -2
  329. package/dist/i18n/locales/es.js.map +1 -1
  330. package/dist/i18n/locales/fr.d.ts +8 -3
  331. package/dist/i18n/locales/fr.js +86 -2
  332. package/dist/i18n/locales/fr.js.map +1 -1
  333. package/dist/i18n/locales/hu.d.ts +8 -3
  334. package/dist/i18n/locales/hu.js +86 -2
  335. package/dist/i18n/locales/hu.js.map +1 -1
  336. package/dist/i18n/locales/it.d.ts +8 -3
  337. package/dist/i18n/locales/it.js +87 -2
  338. package/dist/i18n/locales/it.js.map +1 -1
  339. package/dist/i18n/locales/zh.d.ts +8 -3
  340. package/dist/i18n/locales/zh.js +84 -2
  341. package/dist/i18n/locales/zh.js.map +1 -1
  342. package/dist/i18n/review.d.ts +8 -3
  343. package/dist/i18n/review.js +15 -1
  344. package/dist/i18n/review.js.map +1 -1
  345. package/dist/index.d.ts +27 -11
  346. package/dist/index.js +75 -4
  347. package/dist/index.js.map +1 -1
  348. package/dist/landing/landing-actions.d.ts +8 -3
  349. package/dist/landing/landing-sections.d.ts +8 -3
  350. package/dist/landing/public-header.d.ts +9 -4
  351. package/dist/landing/public-header.js +3 -3
  352. package/dist/landing/public-header.js.map +1 -1
  353. package/dist/landing/routing.d.ts +8 -3
  354. package/dist/lib/cn.js +3 -2
  355. package/dist/lib/cn.js.map +1 -1
  356. package/dist/rhf/fields.d.ts +8 -3
  357. package/dist/rhf/fields.js +1 -1
  358. package/dist/rhf/fields.js.map +1 -1
  359. package/dist/rhf/form.d.ts +8 -3
  360. package/dist/rhf/form.js +2 -2
  361. package/dist/rhf/form.js.map +1 -1
  362. package/dist/rhf.d.ts +8 -3
  363. package/dist/search/command-palette.d.ts +1 -1
  364. package/dist/search/command-palette.js +9 -8
  365. package/dist/search/command-palette.js.map +1 -1
  366. package/dist/settings/settings-layout.js +2 -1
  367. package/dist/settings/settings-layout.js.map +1 -1
  368. package/dist/settings/settings-section.d.ts +8 -3
  369. package/dist/settings/use-account-appearance.d.ts +92 -0
  370. package/dist/settings/use-account-appearance.js +63 -0
  371. package/dist/settings/use-account-appearance.js.map +1 -0
  372. package/dist/settings/use-settings-route.d.ts +1 -1
  373. package/dist/settings/use-settings-route.js +2 -3
  374. package/dist/settings/use-settings-route.js.map +1 -1
  375. package/dist/shell/app-shell.d.ts +12 -171
  376. package/dist/shell/app-shell.js +144 -28
  377. package/dist/shell/app-shell.js.map +1 -1
  378. package/dist/shell/auth-layout.js +1 -1
  379. package/dist/shell/auth-layout.js.map +1 -1
  380. package/dist/shell/option-switcher-menu.js +1 -1
  381. package/dist/shell/option-switcher-menu.js.map +1 -1
  382. package/dist/shell/top-bar-brand.d.ts +8 -3
  383. package/dist/shell/top-bar-brand.js +3 -1
  384. package/dist/shell/top-bar-brand.js.map +1 -1
  385. package/dist/shell/topbar-action-menu.d.ts +4 -0
  386. package/dist/shell/topbar-action-menu.js +3 -3
  387. package/dist/shell/topbar-action-menu.js.map +1 -1
  388. package/dist/shell/topbar-controls.js +4 -4
  389. package/dist/shell/topbar-controls.js.map +1 -1
  390. package/dist/shell.d.ts +9 -5
  391. package/dist/theme/contrast-tokens.d.ts +41 -0
  392. package/dist/theme/contrast-tokens.js +59 -0
  393. package/dist/theme/contrast-tokens.js.map +1 -0
  394. package/dist/theme/contrast.d.ts +109 -0
  395. package/dist/theme/contrast.js +128 -0
  396. package/dist/theme/contrast.js.map +1 -0
  397. package/dist/theme/palette-presets.d.ts +12 -1
  398. package/dist/theme/palette-presets.js +9 -4
  399. package/dist/theme/palette-presets.js.map +1 -1
  400. package/dist/theme/palette-store.js.map +1 -1
  401. package/dist/theme/text-size.d.ts +157 -0
  402. package/dist/theme/text-size.js +121 -0
  403. package/dist/theme/text-size.js.map +1 -0
  404. package/dist/wizard/stepper-nav.d.ts +8 -3
  405. package/dist/wizard.d.ts +8 -3
  406. package/package.json +1 -1
  407. package/src/admin/invitations-panel.tsx +36 -29
  408. package/src/admin/user-roster.tsx +17 -72
  409. package/src/auth/auth-errors.ts +68 -7
  410. package/src/billing/billing-banner.tsx +205 -0
  411. package/src/billing/billing-labels.ts +224 -0
  412. package/src/billing/billing-lock.ts +75 -0
  413. package/src/billing/billing-standing.ts +72 -0
  414. package/src/billing/plan-card.tsx +458 -0
  415. package/src/billing/plan-limit-notice.tsx +145 -0
  416. package/src/billing/plan-limit.ts +53 -0
  417. package/src/billing/plan-price.ts +101 -0
  418. package/src/billing/subscription-actions.tsx +76 -0
  419. package/src/billing/subscription-status-chip.tsx +95 -0
  420. package/src/components/account-chips.tsx +36 -10
  421. package/src/components/alert-banner.tsx +11 -6
  422. package/src/components/amount-input.tsx +5 -5
  423. package/src/components/appearance-labels.ts +40 -0
  424. package/src/components/autocomplete.tsx +2 -2
  425. package/src/components/breadcrumbs.tsx +3 -3
  426. package/src/components/bulk-action-bar.tsx +11 -14
  427. package/src/components/calculator.tsx +4 -3
  428. package/src/components/calendar-heatmap.tsx +5 -5
  429. package/src/components/chart-height.ts +30 -0
  430. package/src/components/chart-zoom.tsx +6 -1
  431. package/src/components/checkbox-group.tsx +2 -2
  432. package/src/components/checkbox.tsx +62 -20
  433. package/src/components/chip.tsx +105 -24
  434. package/src/components/choice-card.tsx +15 -10
  435. package/src/components/column-mapper.tsx +1 -1
  436. package/src/components/combobox-core.tsx +8 -7
  437. package/src/components/combobox.tsx +8 -8
  438. package/src/components/country-select.tsx +1 -1
  439. package/src/components/currency-select.tsx +3 -3
  440. package/src/components/data-table-filter-popover.tsx +7 -1
  441. package/src/components/data-table.tsx +160 -43
  442. package/src/components/date-picker.tsx +12 -9
  443. package/src/components/description-list.tsx +2 -2
  444. package/src/components/dialog-frame.tsx +2 -1
  445. package/src/components/disclosure.tsx +3 -2
  446. package/src/components/dropdown.tsx +10 -2
  447. package/src/components/facing-pair.ts +12 -5
  448. package/src/components/field-parts.tsx +190 -7
  449. package/src/components/field-sync.tsx +17 -3
  450. package/src/components/field.tsx +2 -2
  451. package/src/components/floating-panel.tsx +7 -4
  452. package/src/components/focus-ring.ts +12 -0
  453. package/src/components/form-actions.tsx +55 -18
  454. package/src/components/full-bleed-dialog.tsx +18 -4
  455. package/src/components/image-grid.tsx +1 -1
  456. package/src/components/inline-edit-field.tsx +6 -3
  457. package/src/components/legal-page.tsx +28 -2
  458. package/src/components/legal.tsx +19 -1
  459. package/src/components/lightbox.tsx +24 -2
  460. package/src/components/line-items.tsx +7 -1
  461. package/src/components/list.tsx +36 -10
  462. package/src/components/measured-grid.tsx +49 -24
  463. package/src/components/menu-item.tsx +2 -2
  464. package/src/components/mini-calendar.tsx +8 -4
  465. package/src/components/month-picker.tsx +9 -5
  466. package/src/components/nav-pills.tsx +1 -1
  467. package/src/components/number-input.tsx +14 -7
  468. package/src/components/numpad-sheet.tsx +2 -2
  469. package/src/components/one-time-code-input.tsx +2 -2
  470. package/src/components/page-contents.tsx +2 -2
  471. package/src/components/phone-input.tsx +2 -2
  472. package/src/components/pie-chart.tsx +19 -9
  473. package/src/components/popover.tsx +22 -5
  474. package/src/components/row-actions.tsx +344 -0
  475. package/src/components/scroll-area.tsx +1 -1
  476. package/src/components/search-field.tsx +4 -2
  477. package/src/components/series-chart-budget.ts +40 -8
  478. package/src/components/series-chart-ticks.ts +10 -0
  479. package/src/components/series-chart.tsx +123 -72
  480. package/src/components/settings-fields.tsx +81 -2
  481. package/src/components/share-card.tsx +10 -0
  482. package/src/components/sign-chip.tsx +28 -4
  483. package/src/components/signature-pad.tsx +13 -7
  484. package/src/components/slider.tsx +22 -9
  485. package/src/components/sparkline.tsx +11 -3
  486. package/src/components/stat-tile.tsx +70 -14
  487. package/src/components/swipeable-row.tsx +2 -1
  488. package/src/components/switch.tsx +57 -23
  489. package/src/components/table.tsx +2 -2
  490. package/src/components/text-link.tsx +1 -1
  491. package/src/components/text.tsx +11 -6
  492. package/src/components/tile-radio.tsx +4 -1
  493. package/src/components/toast.tsx +27 -7
  494. package/src/components/toggle-group.tsx +99 -12
  495. package/src/components/toggle-legend.tsx +53 -8
  496. package/src/components/translation-review.tsx +10 -5
  497. package/src/components/tree-view.tsx +3 -2
  498. package/src/components/treemap.tsx +27 -10
  499. package/src/components/ui.tsx +331 -145
  500. package/src/components/user-avatar.tsx +1 -1
  501. package/src/demo/demo-banner.tsx +5 -0
  502. package/src/feedback/feedback-dialog.tsx +3 -3
  503. package/src/feedback/feedback-inbox.tsx +2 -2
  504. package/src/feedback/feedback-thread.tsx +3 -3
  505. package/src/hooks/use-anchored-panel.ts +67 -5
  506. package/src/hooks/use-breakpoint.ts +121 -0
  507. package/src/hooks/use-large-text.ts +41 -0
  508. package/src/hooks/use-windowed-rows.ts +168 -12
  509. package/src/i18n/defaults.ts +8 -0
  510. package/src/i18n/german.ts +103 -0
  511. package/src/i18n/kit-labels.tsx +12 -0
  512. package/src/i18n/locales/es.ts +94 -0
  513. package/src/i18n/locales/fr.ts +98 -0
  514. package/src/i18n/locales/hu.ts +96 -0
  515. package/src/i18n/locales/it.ts +99 -0
  516. package/src/i18n/locales/zh.ts +90 -0
  517. package/src/i18n/review.ts +14 -0
  518. package/src/index.ts +74 -6
  519. package/src/landing/public-header.tsx +4 -4
  520. package/src/lib/cn.ts +13 -2
  521. package/src/rhf/fields.tsx +1 -1
  522. package/src/rhf/form.tsx +2 -2
  523. package/src/search/command-palette.tsx +10 -9
  524. package/src/settings/settings-layout.tsx +2 -1
  525. package/src/settings/use-account-appearance.ts +148 -0
  526. package/src/settings/use-settings-route.ts +3 -4
  527. package/src/shell/app-shell.tsx +278 -54
  528. package/src/shell/auth-layout.tsx +1 -1
  529. package/src/shell/option-switcher-menu.tsx +1 -1
  530. package/src/shell/top-bar-brand.tsx +3 -1
  531. package/src/shell/topbar-action-menu.tsx +3 -3
  532. package/src/shell/topbar-controls.tsx +4 -4
  533. package/src/theme/contrast-tokens.ts +112 -0
  534. package/src/theme/contrast.ts +224 -0
  535. package/src/theme/palette-presets.ts +24 -4
  536. package/src/theme/palette-store.ts +6 -1
  537. package/src/theme/text-size.ts +267 -0
  538. package/tokens.css +143 -1
@@ -70,32 +70,45 @@ export function fromLogPosition(position: number, min: number, max: number, zero
70
70
  // runs the other way too. Firefox draws the fill itself with `::-moz-range-progress`,
71
71
  // which follows the direction on its own.
72
72
  //
73
- // The thumb's `-mt-[5px]` centres a 16px thumb on a 6px track: WebKit lays the thumb
74
- // out from the track's top edge, Firefox centres it, so only one carries the offset.
73
+ // The thumb's `-mt-[0.3125rem]` centres a 1rem thumb on a 0.375rem track — half the
74
+ // difference — in rem since 0.32 (§3.2): the thumb and track grow with the text, and the
75
+ // 5px it was left the thumb low of the track at 125 % and further at 150 %. WebKit lays
76
+ // the thumb out from the track's top edge, Firefox centres it, so only one carries the
77
+ // offset.
75
78
  //
76
79
  // The focus ring goes on the THUMB, not the box: the box is the full width of the
77
80
  // row, and a ring round all of it says "this row is focused" rather than "the handle
78
81
  // you are about to move is". Offset in the surface colour, because the thumb is
79
- // --brand and a --brand ring touching it would be invisible.
82
+ // --brand and a --brand ring touching it would be invisible. Its width is the kit's
83
+ // focus frame (`--focus-ring-width`, §5) — spelt out per engine, since `FOCUS_RING`'s
84
+ // classes cannot carry the pseudo-element.
85
+ //
86
+ // At Large the box is the 48 px touch target (§4) — the track stays centred in it, and
87
+ // the marks row below is pulled up by as much (see `MARKS_LARGE`).
80
88
  const TRACK = [
81
- "block h-6 w-full cursor-pointer appearance-none bg-transparent focus:outline-none disabled:cursor-not-allowed",
89
+ "block h-6 large:h-[48px] w-full cursor-pointer appearance-none bg-transparent focus:outline-none disabled:cursor-not-allowed",
82
90
  // WebKit / Blink
83
91
  "[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full",
84
92
  "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--brand)_var(--slider-fill),var(--border-strong)_var(--slider-fill))]",
85
93
  "rtl:[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_left,var(--brand)_var(--slider-fill),var(--border-strong)_var(--slider-fill))]",
86
- "[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none",
94
+ "[&::-webkit-slider-thumb]:-mt-[0.3125rem] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none",
87
95
  "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[var(--brand)] [&::-webkit-slider-thumb]:shadow",
88
- "focus-visible:[&::-webkit-slider-thumb]:ring-2 focus-visible:[&::-webkit-slider-thumb]:ring-[var(--brand)]",
96
+ "focus-visible:[&::-webkit-slider-thumb]:ring-[length:var(--focus-ring-width)] focus-visible:[&::-webkit-slider-thumb]:ring-[var(--brand)]",
89
97
  "focus-visible:[&::-webkit-slider-thumb]:ring-offset-2 focus-visible:[&::-webkit-slider-thumb]:ring-offset-[var(--bg-surface)]",
90
98
  // Firefox
91
99
  "[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-[var(--border-strong)]",
92
100
  "[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-[var(--brand)]",
93
101
  "[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0",
94
102
  "[&::-moz-range-thumb]:bg-[var(--brand)] [&::-moz-range-thumb]:shadow",
95
- "focus-visible:[&::-moz-range-thumb]:ring-2 focus-visible:[&::-moz-range-thumb]:ring-[var(--brand)]",
103
+ "focus-visible:[&::-moz-range-thumb]:ring-[length:var(--focus-ring-width)] focus-visible:[&::-moz-range-thumb]:ring-[var(--brand)]",
96
104
  "focus-visible:[&::-moz-range-thumb]:ring-offset-2 focus-visible:[&::-moz-range-thumb]:ring-offset-[var(--bg-surface)]",
97
105
  ].join(" ");
98
106
 
107
+ // The marks row sits `-mt-1.5` under a 1.5rem box, tucked against the track. In the 48 px
108
+ // box at Large the track's lower edge is `(48px + 0.375rem) / 2` down, so the row comes
109
+ // up by `(48px − 0.375rem) / 2` less the same 0.1875rem gap: `24px − 0.375rem`.
110
+ const MARKS_LARGE = "large:-mt-[calc(24px-0.375rem)]";
111
+
99
112
  /** A tick under the track. A bare number is a tick with no words. */
100
113
  export interface SliderMark {
101
114
  value: number;
@@ -293,7 +306,7 @@ export const Slider = forwardRef<HTMLInputElement, SliderProps>(function Slider(
293
306
  // without a `translate` that would need flipping for right-to-left.
294
307
  <div
295
308
  aria-hidden
296
- className={cn("relative mx-2 -mt-1.5", hasMarkLabels ? "h-5" : "h-1.5")}
309
+ className={cn("relative mx-2 -mt-1.5", MARKS_LARGE, hasMarkLabels ? "h-5" : "h-1.5")}
297
310
  >
298
311
  {normalizedMarks.map((m) => (
299
312
  <span
@@ -303,7 +316,7 @@ export const Slider = forwardRef<HTMLInputElement, SliderProps>(function Slider(
303
316
  >
304
317
  <span className="h-1.5 w-px shrink-0 bg-[var(--border-strong)]" />
305
318
  {m.label !== undefined && (
306
- <span className="mt-0.5 select-none whitespace-nowrap text-[11px] leading-tight text-[var(--text-muted)]">
319
+ <span className="mt-0.5 select-none whitespace-nowrap text-caption leading-tight text-[var(--text-muted)]">
307
320
  {m.label}
308
321
  </span>
309
322
  )}
@@ -1,6 +1,7 @@
1
1
  import type { ComponentPropsWithoutRef } from "react";
2
2
  import { cn } from "../lib/cn";
3
3
  import { useKitLabels, useKitLocale } from "../i18n/kit-labels";
4
+ import { useTextSize } from "../theme/text-size";
4
5
 
5
6
  /**
6
7
  * The words a sparkline speaks. It has no axes, no tooltip and no legend, so its
@@ -78,8 +79,12 @@ export interface SparklineProps
78
79
  /** The series, oldest first. `null`/`undefined`/`NaN` is a GAP — the line breaks
79
80
  * there rather than drawing a zero or bridging across the missing point. */
80
81
  data: ReadonlyArray<number | null | undefined>;
81
- /** Width in px. Ignored under `fluid`. */
82
+ /** Width in px at Normal. Ignored under `fluid`. The drawn box is this × the text
83
+ * size's scale (0.32, docs/text-size-harmonization.md §3.2): a sparkline stands beside
84
+ * a figure, and a 72 × 24 trend under a 150 % number reads as a speck. The strokes
85
+ * stay px (`non-scaling-stroke`), as every chart's do. */
82
86
  width?: number;
87
+ /** Height in px at Normal, × the scale likewise. */
83
88
  height?: number;
84
89
  /** Fill the container's width instead of a fixed one (the height stays fixed). */
85
90
  fluid?: boolean;
@@ -182,6 +187,7 @@ export function Sparkline({
182
187
  ...rest
183
188
  }: SparklineProps) {
184
189
  const text = useKitLabels("sparkline", DEFAULT_SPARKLINE_LABELS, labels);
190
+ const { scale } = useTextSize();
185
191
  const kitLocale = useKitLocale(locale);
186
192
  const format = formatValue ?? defaultFormat(kitLocale);
187
193
 
@@ -252,8 +258,10 @@ export function Sparkline({
252
258
  role="img"
253
259
  aria-label={name}
254
260
  {...rest}
255
- width={fluid ? "100%" : W}
256
- height={H}
261
+ // The drawing stays in its own units (the viewBox); only the box it is shown in
262
+ // grows, uniformly, so nothing is distorted and the strokes keep their px width.
263
+ width={fluid ? "100%" : W * scale}
264
+ height={H * scale}
257
265
  viewBox={`0 0 ${W} ${H}`}
258
266
  preserveAspectRatio="none"
259
267
  className={cn(fluid ? "block w-full" : "inline-block shrink-0 align-middle", "overflow-visible", className)}
@@ -5,6 +5,7 @@ import { cn } from "../lib/cn";
5
5
  import { useKitLabels, useKitLink, useKitLocale } from "../i18n/kit-labels";
6
6
  import { pickLinkRenderer, RenderedKitLink } from "./text-link";
7
7
  import { Card, FieldHint } from "./ui";
8
+ import { useInlineFacts, useLargeText } from "../hooks/use-large-text";
8
9
  import { Sparkline } from "./sparkline";
9
10
  import { Tooltip } from "./tooltip";
10
11
  // The kit's one placeholder look, so a loading tile and the Skeleton list beside it
@@ -151,7 +152,11 @@ export interface StatTileProps
151
152
  /** The explanation behind a "?" beside the label (a {@link FieldHint}) — hidden
152
153
  * until asked for, so it is for what a reader MIGHT want to know ("excludes
153
154
  * transfers between your own accounts"). For a line everyone should read, use
154
- * {@link StatTileProps.description} instead. */
155
+ * {@link StatTileProps.description} instead.
156
+ *
157
+ * 0.32 (docs/text-size-harmonization.md §4 "No fact only in a tooltip"): at Large
158
+ * and Extra large, and on a touch screen, the words are shown under the label
159
+ * instead of behind the "?". */
155
160
  hint?: string;
156
161
  /**
157
162
  * Keep the label to ONE line, cut with an ellipsis, and show the whole of it in a
@@ -169,6 +174,9 @@ export interface StatTileProps
169
174
  * your own `<Tooltip>` for the full text (keksdose's admin metrics-panel.tsx:134
170
175
  * does, from before this prop). A wrapped label shows a bubble even when nothing is
171
176
  * cut, and nests its trigger inside this one's.
177
+ *
178
+ * Ignored at Large and Extra large (0.32, §4): nothing truncates there, and the full
179
+ * label would otherwise be a fact only in a tooltip.
172
180
  */
173
181
  truncateLabel?: boolean;
174
182
  /**
@@ -199,6 +207,15 @@ export interface StatTileProps
199
207
  * Withheld while `loading`, like the sub-values.
200
208
  */
201
209
  projection?: StatTileSubValue;
210
+ /**
211
+ * Smaller figures under the headline, label left and figure right.
212
+ *
213
+ * At Large and Extra large (0.32, §4 "StatTile shows its one big number; the sparkline
214
+ * and sub-values move below"): the tile leads with the headline and its delta, and the
215
+ * projection, these rows and the sparkline follow beneath the description, each row's
216
+ * label over its figure — a 2-up tile at 150 % is 120 px of text wide, and a label
217
+ * beside a figure there squeezed both.
218
+ */
202
219
  subValues?: StatTileSubValue[];
203
220
  /** Shorthand for a fluid {@link Sparkline} named after the tile and speaking in
204
221
  * the tile's number format. The name is the label's text; see
@@ -310,6 +327,11 @@ export function StatTile({
310
327
  ...rest
311
328
  }: StatTileProps) {
312
329
  const text = useKitLabels("statTile", DEFAULT_STAT_TILE_LABELS, labels);
330
+ // §4: at Large the one big number leads and the rest follows below it; the label
331
+ // never truncates; on touch or at Large the hint is words, not a "?".
332
+ const large = useLargeText();
333
+ const inlineHint = useInlineFacts() && !!hint;
334
+ if (large) truncateLabel = false;
313
335
  // A loading StatTileGrid puts every tile in it into the loading state and says
314
336
  // "Loading…" ONCE for all of them; the tile then keeps its own sentence to itself
315
337
  // (see StatTileGrid's `loading`).
@@ -342,7 +364,9 @@ export function StatTile({
342
364
  const stretched = cn(
343
365
  "text-start uppercase tracking-wide",
344
366
  "after:absolute after:inset-0 after:rounded-lg after:content-['']",
345
- "focus-visible:outline-none focus-visible:after:ring-2 focus-visible:after:ring-inset focus-visible:after:ring-[var(--brand)]",
367
+ // The kit's focus frame (0.32, §5), on the overlay — `FOCUS_RING`'s classes cannot
368
+ // carry the `after:`, so its width is spelt out.
369
+ "focus-visible:outline-none focus-visible:after:ring-[length:var(--focus-ring-width)] focus-visible:after:ring-inset focus-visible:after:ring-[var(--brand)]",
346
370
  );
347
371
  const describedBy = d ? `${valueId} ${deltaId}` : valueId;
348
372
  let labelNode: ReactNode = label;
@@ -417,7 +441,7 @@ export function StatTile({
417
441
  {!gridLoading && <span className="sr-only">{text.loading}</span>}
418
442
  </span>
419
443
  ) : hasValue ? (
420
- <span id={valueId} data-private={priv} className={cn(valueClass, valueLabel != null && "ms-auto")}>
444
+ <span id={valueId} data-private={priv} className={cn(valueClass, valueLabel != null && !large && "ms-auto")}>
421
445
  {show(value)}
422
446
  {unit != null && (
423
447
  <span className="ms-1 text-[0.7em] font-medium text-[var(--text-muted)]">{unit}</span>
@@ -480,7 +504,7 @@ export function StatTile({
480
504
  </span>
481
505
  )}
482
506
  {/* Above the stretched link's overlay, so it stays hoverable and focusable. */}
483
- {hint && <FieldHint label={hint} side="top" className="relative z-10 shrink-0" />}
507
+ {hint && !inlineHint && <FieldHint label={hint} side="top" className="relative z-10 shrink-0" />}
484
508
  </div>
485
509
  {icon != null && (
486
510
  <span aria-hidden className="shrink-0 text-[var(--text-muted)] [&_svg]:size-4">
@@ -489,10 +513,19 @@ export function StatTile({
489
513
  )}
490
514
  </div>
491
515
 
516
+ {inlineHint && (
517
+ // §4: the hint as words under the label — the same muted type as `description`,
518
+ // in the reading case of a sentence rather than the label's capitals.
519
+ <p data-slot="stat-tile-hint" className="mt-0.5 text-xs text-[var(--text-muted)]">
520
+ {hint}
521
+ </p>
522
+ )}
523
+
492
524
  {valueLabel != null ? (
493
525
  // Period-left / value-right, matching the sub-rows below it. flex-wrap lets a
494
- // too-long amount drop to its own end-aligned line instead of overflowing.
495
- <div className="mt-1 flex flex-wrap items-baseline gap-x-1.5">
526
+ // too-long amount drop to its own end-aligned line instead of overflowing. At
527
+ // Large the period goes over the figure, which then has the tile's width.
528
+ <div className={cn("mt-1 flex items-baseline gap-x-1.5", large ? "flex-col items-start" : "flex-wrap")}>
496
529
  <span className="shrink-0 text-xs uppercase tracking-wide text-[var(--text-muted)]">{valueLabel}</span>
497
530
  {headline}
498
531
  </div>
@@ -500,7 +533,9 @@ export function StatTile({
500
533
  <div className="mt-1">{headline}</div>
501
534
  )}
502
535
 
503
- {!loading && projection && (
536
+ {/* At Large the delta and description follow the figure at once, and the
537
+ projection moves down with the other figures (see `subValues`). */}
538
+ {!large && !loading && projection && (
504
539
  <dl className="mt-0.5">
505
540
  <FigureRow figure={projection} tileTone={tone} show={show} priv={priv} />
506
541
  </dl>
@@ -508,16 +543,25 @@ export function StatTile({
508
543
 
509
544
  {deltaNode}
510
545
  {description != null && <div className="mt-1 text-xs text-[var(--text-muted)]">{description}</div>}
511
- {graphic && <div className="mt-2">{graphic}</div>}
546
+ {!large && graphic && <div className="mt-2">{graphic}</div>}
512
547
 
513
- {!loading && subValues && subValues.length > 0 && (
514
- <dl className="mt-1.5 space-y-0.5">
515
- {subValues.map((sv, i) => (
516
- <FigureRow key={i} figure={sv} tileTone={tone} show={show} priv={priv} />
548
+ {!loading && ((large && projection) || (subValues && subValues.length > 0)) && (
549
+ <dl data-slot="stat-tile-figures" className={cn("mt-1.5", large ? "space-y-1.5" : "space-y-0.5")}>
550
+ {large && projection && (
551
+ <FigureRow figure={projection} tileTone={tone} show={show} priv={priv} stacked />
552
+ )}
553
+ {subValues?.map((sv, i) => (
554
+ <FigureRow key={i} figure={sv} tileTone={tone} show={show} priv={priv} stacked={large} />
517
555
  ))}
518
556
  </dl>
519
557
  )}
520
558
 
559
+ {large && graphic && (
560
+ <div data-slot="stat-tile-graphic" className="mt-2">
561
+ {graphic}
562
+ </div>
563
+ )}
564
+
521
565
  {footer != null && <div className="mt-2">{footer}</div>}
522
566
  </Card>
523
567
  );
@@ -529,11 +573,14 @@ function FigureRow({
529
573
  tileTone,
530
574
  show,
531
575
  priv,
576
+ stacked = false,
532
577
  }: {
533
578
  figure: StatTileSubValue;
534
579
  tileTone: StatTileTone;
535
580
  show: (v: ReactNode) => ReactNode;
536
581
  priv: "" | undefined;
582
+ /** The label over the figure (Large, §4) instead of beside it. */
583
+ stacked?: boolean;
537
584
  }) {
538
585
  const own = figure.tone ?? (tileTone === "neutral" ? undefined : tileTone);
539
586
  const svTone = own === "none" ? undefined : own;
@@ -543,11 +590,20 @@ function FigureRow({
543
590
  // the period it belongs to. Now the label breaks within itself and the amount stays
544
591
  // on the label's first line, end-aligned like every other row.
545
592
  return (
546
- <div className="flex items-baseline gap-x-2 text-xs tabular-nums text-[var(--text-muted)]">
593
+ <div
594
+ className={cn(
595
+ "flex text-xs tabular-nums text-[var(--text-muted)]",
596
+ stacked ? "flex-col items-start" : "items-baseline gap-x-2",
597
+ )}
598
+ >
547
599
  <dt className="min-w-0 uppercase tracking-wide [overflow-wrap:anywhere]">{figure.label}</dt>
548
600
  <dd
549
601
  data-private={priv}
550
- className={cn("ms-auto shrink-0 whitespace-nowrap", svTone && [toneClass(svTone, figure.value), "opacity-75"])}
602
+ className={cn(
603
+ "shrink-0 whitespace-nowrap",
604
+ !stacked && "ms-auto",
605
+ svTone && [toneClass(svTone, figure.value), "opacity-75"],
606
+ )}
551
607
  >
552
608
  {show(figure.value)}
553
609
  </dd>
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
2
  import type { ReactNode } from "react";
3
3
  import { cn } from "../lib/cn";
4
+ import { FOCUS_RING } from "./focus-ring";
4
5
  import { useRowSwipe, type SwipeStage } from "../hooks/use-row-swipe";
5
6
  import { DEFAULT_SWIPEABLE_ROW_LABELS, useKitLabels } from "../i18n/kit-labels";
6
7
 
@@ -270,7 +271,7 @@ export function SwipeableRow({
270
271
  className={cn(
271
272
  "sr-only whitespace-nowrap rounded-md border border-[var(--border)] bg-[var(--bg-surface)] px-2 py-1 text-xs font-medium text-[var(--text-primary)] shadow-sm",
272
273
  "focus:not-sr-only focus:pointer-events-auto focus:inline-flex focus:items-center focus:gap-1.5",
273
- "focus-visible:ring-2 focus-visible:ring-[var(--brand)]",
274
+ FOCUS_RING,
274
275
  )}
275
276
  >
276
277
  {/* The label is the button's accessible name, so the caller's icon is
@@ -2,7 +2,14 @@ import { forwardRef, useId } from "react";
2
2
  import type { ChangeEvent, InputHTMLAttributes, ReactNode } from "react";
3
3
  import { cn } from "../lib/cn";
4
4
  import { hasMessage, mergeDescribedBy } from "./choice-parts";
5
- import { LockedReason, useLockReason } from "./field-parts";
5
+ import {
6
+ DISABLED_REASON_LINE_CLASS,
7
+ LockedReason,
8
+ useDisabledReasonLine,
9
+ useLockReason,
10
+ type DisabledReasonDisplay,
11
+ } from "./field-parts";
12
+ import { FOCUS_RING } from "./focus-ring";
6
13
 
7
14
  /**
8
15
  * An on/off switch that is still `<input type="checkbox">`, with `role="switch"`.
@@ -14,16 +21,20 @@ import { LockedReason, useLockReason } from "./field-parts";
14
21
  * the moment it is flipped; a choice that waits for a Save button is a checkbox.
15
22
  */
16
23
 
17
- /** Geometry per size. The thumb sits `2px` in from each end of the track, so its
18
- * checked offset is `track width − thumb − 2px`. `start-*`, never `left-*` or a
19
- * `translate-x`: an inline-start offset follows the writing direction, so in a
20
- * right-to-left form the thumb travels right-to-left with no `rtl:` override to
21
- * keep in step. */
24
+ /** Geometry per size. The thumb sits `start-0.5` (0.125rem, 2 px at Normal) in from
25
+ * each end of the track, so its checked offset is `track width − thumb − 0.125rem`.
26
+ * All of it in rem since 0.32 (docs/text-size-harmonization.md §3.2, §4): the track
27
+ * and thumb already grew with the text, and a checked offset left in px (14 / 18) put
28
+ * the thumb short of the end at 125 % and in the middle at 150 %. `start-*`, never
29
+ * `left-*` or a `translate-x`: an inline-start offset follows the writing direction,
30
+ * so in a right-to-left form the thumb travels right-to-left with no `rtl:` override
31
+ * to keep in step. */
22
32
  const SIZES = {
23
- // 28×16 track, 12px thumb: the dense setting rows in Kastlan's tables.
24
- sm: { track: "h-4 w-7", thumb: "size-3 peer-checked:start-[14px]" },
25
- // 36×20 track, 16px thumb.
26
- md: { track: "h-5 w-9", thumb: "size-4 peer-checked:start-[18px]" },
33
+ // 1.75 × 1rem track, 0.75rem thumb (28×16 / 12 at Normal): the dense setting rows in
34
+ // Kastlan's tables. Checked: 1.75 − 0.75 − 0.125 = 0.875rem.
35
+ sm: { track: "h-4 w-7", thumb: "size-3 peer-checked:start-3.5" },
36
+ // 2.25 × 1.25rem track, 1rem thumb (36×20 / 16). Checked: 2.25 − 1 − 0.125 = 1.125rem.
37
+ md: { track: "h-5 w-9", thumb: "size-4 peer-checked:start-4.5" },
27
38
  } as const;
28
39
 
29
40
  export type SwitchSize = keyof typeof SIZES;
@@ -38,7 +49,8 @@ export type SwitchSize = keyof typeof SIZES;
38
49
  const TRACK =
39
50
  "peer block shrink-0 cursor-pointer appearance-none rounded-full bg-[var(--border-strong)] transition-colors " +
40
51
  "checked:bg-[var(--brand)] " +
41
- "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)] " +
52
+ // The kit's focus frame (§5), stood off the track so it never merges with the fill.
53
+ `focus:outline-none ${FOCUS_RING} focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)] ` +
42
54
  "disabled:cursor-not-allowed";
43
55
 
44
56
  // The thumb, laid over the input and ignoring the pointer so every click still reaches
@@ -93,6 +105,13 @@ export interface SwitchProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
93
105
  * switch anew (it moves in or out of the Tooltip), as a locked Button is.
94
106
  */
95
107
  disabledReason?: ReactNode;
108
+ /**
109
+ * Where `disabledReason` shows (0.32, §4 "No fact only in a tooltip"): by default, at
110
+ * Large and on a touch screen, as a line in the words column under the description (a
111
+ * bare switch: under the track), and in the tooltip otherwise. See
112
+ * {@link DisabledReasonDisplay}.
113
+ */
114
+ disabledReasonDisplay?: DisabledReasonDisplay;
96
115
  /**
97
116
  * This switch COMMITS — flipping it saves. Under a locked {@link WriteLockProvider} it
98
117
  * is locked the `disabledReason` way with the lock's reason (which wins over its
@@ -122,6 +141,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
122
141
  disabled,
123
142
  required,
124
143
  disabledReason,
144
+ disabledReasonDisplay,
125
145
  commit,
126
146
  onClick,
127
147
  ...rest
@@ -132,6 +152,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
132
152
  const inputId = id ?? generated;
133
153
  const lock = useLockReason(commit, disabledReason);
134
154
  const locked = lock.locked;
155
+ const reasonLine = useDisabledReasonLine(disabledReasonDisplay);
135
156
  // Locked is disabled the focusable way — the row fades and the words stop inviting a
136
157
  // click, as for `disabled`.
137
158
  const looksDisabled = Boolean(disabled) || locked;
@@ -177,21 +198,28 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
177
198
  </span>
178
199
  );
179
200
  // The Tooltip goes round the track, not the row — the row is the caller's layout. A
180
- // bare switch hands its `className` to the wrapper, then the outermost element.
181
- const control = locked ? (
182
- <LockedReason lock={lock} className={cn("shrink-0", bare && className)}>
183
- {track}
184
- </LockedReason>
185
- ) : (
186
- track
187
- );
201
+ // bare switch hands its `className` to the wrapper, then the outermost element. With
202
+ // words beside it, the reason LINE goes under them instead (below), so the track
203
+ // stays where the row puts it.
204
+ const lineInWords = locked && reasonLine && !bare;
205
+ const control =
206
+ locked && !lineInWords ? (
207
+ <LockedReason lock={lock} line={reasonLine} className={cn("shrink-0", bare && className)}>
208
+ {track}
209
+ </LockedReason>
210
+ ) : (
211
+ track
212
+ );
188
213
 
189
214
  if (bare) return control;
190
215
 
191
- // The switch's cell is one label line tall (text-sm, 20px) so it centres on the
216
+ // The switch's cell is one label line tall (text-sm, 1.25rem) so it centres on the
192
217
  // FIRST line of a label that wraps, and a description underneath does not drag it
193
218
  // down to the middle of the paragraph.
194
- const cell = <span className="flex h-5 shrink-0 items-center">{control}</span>;
219
+ // With the reason line in the words, the row's fade moves onto its parts: the line is
220
+ // the one thing on the row still to be read, and at 60 % it would not be.
221
+ const dimParts = lineInWords;
222
+ const cell = <span className={cn("flex h-5 shrink-0 items-center", dimParts && "opacity-60")}>{control}</span>;
195
223
  const words = (
196
224
  <span className="min-w-0 flex-1">
197
225
  {label !== undefined && (
@@ -200,6 +228,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
200
228
  className={cn(
201
229
  "block text-sm leading-5 text-[var(--text-primary)]",
202
230
  looksDisabled ? "cursor-not-allowed" : "cursor-pointer select-none",
231
+ dimParts && "opacity-60",
203
232
  )}
204
233
  >
205
234
  {label}
@@ -211,15 +240,20 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(function Switch(
211
240
  </label>
212
241
  )}
213
242
  {showDescription && (
214
- <span id={descriptionId} className="mt-0.5 block text-xs text-[var(--text-muted)]">
243
+ <span id={descriptionId} className={cn("mt-0.5 block text-xs text-[var(--text-muted)]", dimParts && "opacity-60")}>
215
244
  {description}
216
245
  </span>
217
246
  )}
247
+ {lineInWords && (
248
+ <span id={lock.reasonId} className={DISABLED_REASON_LINE_CLASS}>
249
+ {lock.reason}
250
+ </span>
251
+ )}
218
252
  </span>
219
253
  );
220
254
 
221
255
  return (
222
- <div className={cn("flex items-start gap-3", looksDisabled && "opacity-60", className)}>
256
+ <div className={cn("flex items-start gap-3", looksDisabled && !dimParts && "opacity-60", className)}>
223
257
  {switchPosition === "end" ? (
224
258
  <>
225
259
  {words}
@@ -242,7 +242,7 @@ const STACK_PHONE_CLASSES = cn(
242
242
  "max-sm:[&_tbody_td]:block max-sm:[&_tbody_td]:px-0 max-sm:[&_tbody_td]:py-0.5 max-sm:[&_tbody_td]:text-start",
243
243
  "max-sm:[&_tfoot_td]:block max-sm:[&_tfoot_td]:px-0 max-sm:[&_tfoot_td]:py-0.5",
244
244
  "max-sm:[&_tbody_td:first-child]:font-medium",
245
- "max-sm:[&_td[data-label]]:before:block max-sm:[&_td[data-label]]:before:text-[11px] max-sm:[&_td[data-label]]:before:font-medium max-sm:[&_td[data-label]]:before:text-[var(--text-muted)] max-sm:[&_td[data-label]]:before:content-[attr(data-label)]",
245
+ "max-sm:[&_td[data-label]]:before:block max-sm:[&_td[data-label]]:before:text-caption max-sm:[&_td[data-label]]:before:font-medium max-sm:[&_td[data-label]]:before:text-[var(--text-muted)] max-sm:[&_td[data-label]]:before:content-[attr(data-label)]",
246
246
  );
247
247
 
248
248
  /**
@@ -353,7 +353,7 @@ export function Table({
353
353
  // rounded corners.
354
354
  framed && "rounded-md border border-[var(--border)]",
355
355
  THIN_SCROLLBAR_CLASS,
356
- "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--brand)]",
356
+ "focus-visible:outline-[length:var(--focus-ring-width)] focus-visible:-outline-offset-2 focus-visible:outline-[var(--brand)]",
357
357
  wrapperClassName,
358
358
  )}
359
359
  >
@@ -288,7 +288,7 @@ export function TextLink({
288
288
 
289
289
  const look = cn(
290
290
  "rounded-sm underline-offset-2 transition-colors",
291
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--brand)]",
291
+ "focus-visible:outline-[length:var(--focus-ring-width)] focus-visible:outline-offset-2 focus-visible:outline-[var(--brand)]",
292
292
  TONE[tone],
293
293
  UNDERLINE[underline],
294
294
  ariaCurrent && "font-semibold",
@@ -35,10 +35,14 @@ export type SectionLabelSize = "xs" | "md" | "sm";
35
35
  * reached as a string than wrapped in a component that only takes a `className`. The
36
36
  * uppercase is CSS, so a screen reader reads the text as written: write it in normal
37
37
  * case.
38
+ *
39
+ * The sizes are the kit's named rem steps since 0.32 (`text-micro` 0.625rem,
40
+ * `text-caption` 0.6875rem — 10 and 11 px at Normal), so a label grows with the text
41
+ * size like everything around it (docs/text-size-harmonization.md §3.2).
38
42
  */
39
43
  export const SECTION_LABEL_CLASS: Record<SectionLabelSize, string> = {
40
- xs: "text-[10px] font-semibold uppercase tracking-wide text-[var(--text-muted)]",
41
- md: "text-[11px] font-semibold uppercase tracking-wide text-[var(--text-muted)]",
44
+ xs: "text-micro font-semibold uppercase tracking-wide text-[var(--text-muted)]",
45
+ md: "text-caption font-semibold uppercase tracking-wide text-[var(--text-muted)]",
42
46
  sm: "text-xs font-semibold uppercase tracking-wide text-[var(--text-muted)]",
43
47
  };
44
48
 
@@ -61,8 +65,8 @@ export type SectionLabelVariant = "plain" | "band";
61
65
  * a font-size arbitrary `text-` value from a colour one. (Not spelt out as class
62
66
  * names here: Tailwind scans comments, and a literal one broke the showcase CSS.) */
63
67
  const SECTION_LABEL_BAND_CLASS: Record<SectionLabelSize, string> = {
64
- xs: "text-[10px]",
65
- md: "text-[11px]",
68
+ xs: "text-micro",
69
+ md: "text-caption",
66
70
  sm: "text-xs",
67
71
  };
68
72
  const BAND =
@@ -112,14 +116,15 @@ export function SectionLabel({
112
116
  /* ── Caption ──────────────────────────────────────────────────────────────── */
113
117
 
114
118
  /**
115
- * The caption type as a class string: 11px, snug, muted. lenkbank's `CAPTION`
119
+ * The caption type as a class string: `text-caption` (0.6875rem, 11 px at Normal, §3.2),
120
+ * snug, muted. lenkbank's `CAPTION`
116
121
  * (shared/ui/caption.ts:23) — written out 31 times before it was a constant — as a
117
122
  * token colour rather than `slate-500 dark:slate-400`, so it follows the palette.
118
123
  * Exported for the same reason lenkbank's is a string: every caller positions it
119
124
  * differently (`mt-1`, `self-end pb-2`, `border-t`), and what must be one thing is the
120
125
  * type, not the box.
121
126
  */
122
- export const CAPTION_CLASS = "text-[11px] leading-snug text-[var(--text-muted)]";
127
+ export const CAPTION_CLASS = "text-caption leading-snug text-[var(--text-muted)]";
123
128
 
124
129
  export interface CaptionProps extends ComponentPropsWithoutRef<"p"> {
125
130
  /** Default `p`; `span` for a caption inside a line of other content. */
@@ -2,6 +2,7 @@ import { useId, useRef } from "react";
2
2
  import type { KeyboardEvent, ReactNode } from "react";
3
3
  import { Ban, Check } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
+ import { FOCUS_RING } from "./focus-ring";
5
6
  import { horizontalStep } from "../lib/direction";
6
7
  import { Tooltip } from "./tooltip";
7
8
 
@@ -133,7 +134,9 @@ export interface TileRadioGroupProps<T extends string> {
133
134
  */
134
135
  const TILE_BASE =
135
136
  "relative inline-flex shrink-0 items-center justify-center rounded-md border transition-colors " +
136
- "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)] " +
137
+ "focus:outline-none " +
138
+ FOCUS_RING +
139
+ " focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-surface)] " +
137
140
  "disabled:cursor-not-allowed disabled:opacity-50";
138
141
  const TILE_IDLE =
139
142
  "border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--border-strong)] hover:bg-[var(--bg-hover)]";
@@ -1,8 +1,7 @@
1
1
  import { useEffect, useState, useSyncExternalStore } from "react";
2
2
  import type { CSSProperties, MouseEvent, ReactElement, ReactNode } from "react";
3
3
  import { useKitLabels } from "../i18n/kit-labels";
4
- import { useMediaQuery } from "../hooks/use-media-query";
5
- import { PHONE_QUERY } from "./ui";
4
+ import { usePhoneLayout } from "../hooks/use-breakpoint";
6
5
 
7
6
  /**
8
7
  * The kit's toast layer: a `toast` API and a `<Toaster>`, over sonner.
@@ -298,7 +297,9 @@ export type ToastPosition =
298
297
 
299
298
  export type ToastSwipeDirection = "top" | "right" | "bottom" | "left";
300
299
 
301
- /** sonner's offset: one value for every edge, or per edge. Numbers are px. */
300
+ /** sonner's offset: one value for every edge, or per edge. Numbers are px, which do not
301
+ * follow the text size — prefer a CSS length (rem, or a `calc()` on
302
+ * {@link TOASTER_OFFSET_TOP} / {@link TOASTER_OFFSET_BOTTOM}). */
302
303
  export type ToasterOffset =
303
304
  | string
304
305
  | number
@@ -397,6 +398,22 @@ function subscribeDocumentTheme(onChange: () => void): () => void {
397
398
 
398
399
  const px = (v: string | number) => (typeof v === "number" ? `${v}px` : v);
399
400
 
401
+ /**
402
+ * The Toaster's default distance from the top edge (desktop), as a CSS length (0.32,
403
+ * docs/text-size-harmonization.md §10.11): below a 3rem top bar with 1rem to spare, and
404
+ * the safe area. In rem, so it moves with the text size along with the top bar — an
405
+ * app's `offset={{ top: 64 }}` (keksdose) did not. Pass it, or a `calc()` built on it,
406
+ * instead of a px number.
407
+ */
408
+ export const TOASTER_OFFSET_TOP = "calc(env(safe-area-inset-top, 0px) + 4rem)";
409
+
410
+ /**
411
+ * The Toaster's default distance from the bottom edge (phone), as a CSS length: above
412
+ * AppShell's measured bottom nav (`--app-nav-h`) or the home indicator, whichever is
413
+ * taller, with 0.75rem to spare.
414
+ */
415
+ export const TOASTER_OFFSET_BOTTOM = "calc(max(var(--app-nav-h, 0px), env(safe-area-inset-bottom, 0px)) + 0.75rem)";
416
+
400
417
  /**
401
418
  * Where the toasts appear, with the kit's defaults — mount ONE, near the root and
402
419
  * inside `<UiKitProvider>` (it reads the `toast` labels) and inside the router if a
@@ -480,14 +497,17 @@ export function Toaster({
480
497
  }, [dismissOnMiddleClick, mod]);
481
498
 
482
499
  const documentTheme = useSyncExternalStore(subscribeDocumentTheme, readDocumentTheme, () => "light" as const);
483
- const phone = useMediaQuery(PHONE_QUERY, false);
500
+ const phone = usePhoneLayout();
484
501
  const resolved: ToastPosition = position ?? (phone ? "bottom-center" : "top-center");
485
502
 
486
503
  // Above the bottom nav and the home indicator: whichever is taller, since the nav
487
504
  // already pads itself by the safe area (the same sum BulkActionBar floats on).
488
- const bottom = `calc(max(${navOffset === undefined ? "var(--app-nav-h, 0px)" : px(navOffset)}, env(safe-area-inset-bottom, 0px)) + 0.75rem)`;
489
- // Below a 48px TopBar with the same 1rem clearance.
490
- const top = "calc(env(safe-area-inset-top, 0px) + 4rem)";
505
+ const bottom =
506
+ navOffset === undefined
507
+ ? TOASTER_OFFSET_BOTTOM
508
+ : `calc(max(${px(navOffset)}, env(safe-area-inset-bottom, 0px)) + 0.75rem)`;
509
+ // Below a 3rem TopBar with the same 1rem clearance.
510
+ const top = TOASTER_OFFSET_TOP;
491
511
  const computed = resolved.startsWith("bottom") ? { bottom } : { top };
492
512
 
493
513
  if (!mod) return null;