@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,6 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import { Link as RouterLink } from "react-router";
3
3
  import { cn } from "../lib/cn";
4
+ import { FOCUS_RING } from "../components/focus-ring";
4
5
  import { useKitLink } from "../i18n/kit-labels";
5
6
  import { pickLinkRenderer } from "../components/text-link";
6
7
  import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
@@ -58,7 +59,8 @@ export function TopBarBrand({
58
59
  href={to}
59
60
  className={cn(
60
61
  // `relative` contains the sr-only name.
61
- "relative flex min-w-0 items-center gap-2 rounded-md font-semibold text-[var(--text-primary)] hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)]",
62
+ "relative flex min-w-0 items-center gap-2 rounded-md font-semibold text-[var(--text-primary)] hover:opacity-80",
63
+ FOCUS_RING,
62
64
  className,
63
65
  )}
64
66
  >
@@ -326,7 +326,7 @@ export function TopBarActionMenu({
326
326
  const footerBody = typeof footer === "function" ? footer(close) : footer;
327
327
  const headerExtra = typeof header?.extra === "function" ? header.extra(close) : header?.extra;
328
328
  const footerClass =
329
- "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-2 [&_a:focus-visible]:ring-[var(--brand)]";
329
+ "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-[length:var(--focus-ring-width)] [&_a:focus-visible]:ring-[var(--brand)]";
330
330
  /** One action or link row — loose, or inside a heading's group. */
331
331
  const row = (entry: RowEntry) => (
332
332
  <li key={entry.key}>
@@ -374,7 +374,7 @@ export function TopBarActionMenu({
374
374
  </MenuSection>
375
375
  )}
376
376
  {heading && (
377
- <li className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
377
+ <li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
378
378
  {heading}
379
379
  </li>
380
380
  )}
@@ -397,7 +397,7 @@ export function TopBarActionMenu({
397
397
  id={headingId}
398
398
  role="presentation"
399
399
  className={cn(
400
- "px-3 pb-0.5 text-[11px] font-semibold uppercase tracking-wide text-[var(--text-muted)] [overflow-wrap:anywhere]",
400
+ "px-3 pb-0.5 text-caption font-semibold uppercase tracking-wide text-[var(--text-muted)] [overflow-wrap:anywhere]",
401
401
  tight ? "pt-1" : "pt-2.5",
402
402
  )}
403
403
  >
@@ -19,7 +19,7 @@ export const TOPBAR_MENU_ITEM_CLASS =
19
19
  /** The small uppercase heading row the top-bar menus share (`heading`). */
20
20
  function MenuHeading({ children }: { children: string }) {
21
21
  return (
22
- <li className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
22
+ <li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
23
23
  {children}
24
24
  </li>
25
25
  );
@@ -131,7 +131,7 @@ export function PaletteMenu({
131
131
  </span>
132
132
  <span className="flex min-w-0 flex-col text-start">
133
133
  <span className="truncate">{p.name}</span>
134
- <span className="truncate text-[11px] text-[var(--text-placeholder)]">
134
+ <span className="truncate text-caption text-[var(--text-placeholder)]">
135
135
  {p.blurb}
136
136
  </span>
137
137
  </span>
@@ -197,7 +197,7 @@ export function LanguageMenu({
197
197
  {active ? (
198
198
  <span
199
199
  aria-hidden
200
- className={`fi fi-${active.country} inline-block h-[15px] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
200
+ className={`fi fi-${active.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
201
201
  />
202
202
  ) : (
203
203
  <Globe className="size-5" />
@@ -224,7 +224,7 @@ export function LanguageMenu({
224
224
  <span className="flex items-center gap-2">
225
225
  <span
226
226
  aria-hidden
227
- className={`fi fi-${lang.country} inline-block h-[15px] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
227
+ className={`fi fi-${lang.country} inline-block h-[0.9375rem] w-5 shrink-0 rounded-sm shadow-[0_0_0_1px_rgba(0,0,0,0.08)]`}
228
228
  />
229
229
  {lang.label}
230
230
  </span>
@@ -0,0 +1,112 @@
1
+ import { contrast, oklchToRgb, parseHex, rgbToOklch, toHex } from "./color";
2
+ import type { TokenSet } from "./palette-presets";
3
+
4
+ /**
5
+ * The "More contrast" step on a token set (docs/text-size-harmonization.md §5, §10.5).
6
+ * INTERNAL — `applyTokenSet` and the contrast layer share it; it is not re-exported.
7
+ *
8
+ * The step happens HERE, in JS, because the tokens it changes are written inline on
9
+ * `<html>` by `applyTokenSet`: an inline value beats any `[data-contrast=more]` rule and
10
+ * any `@media (prefers-contrast: more)` block, so a stylesheet cannot do it (§10.5, the
11
+ * keksdose review). `--text-placeholder` is a CSS-derived token, and it is written inline
12
+ * here too rather than redefined per attribute in tokens.css.
13
+ *
14
+ * What moves (§5):
15
+ * - `--text-muted` becomes the set's `--text-secondary`;
16
+ * - `--text-secondary` moves halfway to `--text-primary`;
17
+ * - `--text-placeholder` becomes the set's `--text-muted` (4.6–5.5:1 on every shipped
18
+ * preset, where the CSS formula gives 2.4–2.8:1) — the order primary > secondary >
19
+ * muted > placeholder survives the step;
20
+ * - `--border` moves toward `--text-primary` until it clears 3:1 against the worst of the
21
+ * three surfaces (WCAG 1.4.11, a field's boundary), and never less far than
22
+ * `--border-strong`'s 45 % — so every border is at least as strong as a "strong" one was.
23
+ * `--border-strong` stays derived in tokens.css and grows with it.
24
+ */
25
+
26
+ /** The custom properties a contrast step writes. */
27
+ export const CONTRAST_STEPPED_VARS = ["--text-secondary", "--text-muted", "--text-placeholder", "--border"] as const;
28
+
29
+ export interface ContrastStep {
30
+ textSecondary: string;
31
+ textMuted: string;
32
+ textPlaceholder: string;
33
+ border: string;
34
+ }
35
+
36
+ interface Lab {
37
+ l: number;
38
+ a: number;
39
+ b: number;
40
+ }
41
+
42
+ function toLab(hex: string): Lab | null {
43
+ const rgb = parseHex(hex);
44
+ if (!rgb) return null;
45
+ const { l, c, h } = rgbToOklch(rgb);
46
+ const rad = (h * Math.PI) / 180;
47
+ return { l, a: c * Math.cos(rad), b: c * Math.sin(rad) };
48
+ }
49
+
50
+ /** `color-mix(in oklab, a p, b)` as a hex — the same formula tokens.css's derived roles
51
+ * use, so a stepped value sits where the stylesheet would have put it. A colour that is
52
+ * not hex (a consumer's own token set) comes back unmixed. */
53
+ function mix(a: string, b: string, p: number): string {
54
+ const A = toLab(a);
55
+ const B = toLab(b);
56
+ if (!A || !B) return a;
57
+ const l = A.l * p + B.l * (1 - p);
58
+ const x = A.a * p + B.a * (1 - p);
59
+ const y = A.b * p + B.b * (1 - p);
60
+ const c = Math.hypot(x, y);
61
+ const h = c < 1e-6 ? 0 : ((Math.atan2(y, x) * 180) / Math.PI + 360) % 360;
62
+ return toHex(oklchToRgb({ l, c, h }));
63
+ }
64
+
65
+ /** WCAG 1.4.11 for a component's boundary. */
66
+ const BORDER_TARGET = 3;
67
+ /** `--border-strong` in tokens.css: 55 % border, 45 % text. */
68
+ const BORDER_STRONG_SHARE = 0.45;
69
+
70
+ function worstContrast(color: string, t: TokenSet): number {
71
+ return Math.min(contrast(color, t.bgPage), contrast(color, t.bgSurface), contrast(color, t.bgSurface2));
72
+ }
73
+
74
+ function steppedBorder(t: TokenSet): string {
75
+ // The share of `--text-primary` in the border: at least --border-strong's, more until
76
+ // 3:1. Monotonic in the share (the mix walks from the border to the text, away from
77
+ // every surface), so a bisection finds the least that clears it.
78
+ let lo = BORDER_STRONG_SHARE;
79
+ if (worstContrast(mix(t.border, t.textPrimary, 1 - lo), t) >= BORDER_TARGET) return mix(t.border, t.textPrimary, 1 - lo);
80
+ let hi = 1;
81
+ for (let i = 0; i < 16; i++) {
82
+ const mid = (lo + hi) / 2;
83
+ if (worstContrast(mix(t.border, t.textPrimary, 1 - mid), t) >= BORDER_TARGET) hi = mid;
84
+ else lo = mid;
85
+ }
86
+ return mix(t.border, t.textPrimary, 1 - hi);
87
+ }
88
+
89
+ /** The four stepped values for `t`. Pure. */
90
+ export function contrastStep(t: TokenSet): ContrastStep {
91
+ return {
92
+ textSecondary: mix(t.textSecondary, t.textPrimary, 0.5),
93
+ textMuted: t.textSecondary,
94
+ textPlaceholder: t.textMuted,
95
+ border: steppedBorder(t),
96
+ };
97
+ }
98
+
99
+ /* ── The token set last written to an element ───────────────────────────── */
100
+
101
+ const lastWritten = new WeakMap<HTMLElement, TokenSet>();
102
+
103
+ /** Remember the set `applyTokenSet` wrote, so a contrast change can re-apply it. */
104
+ export function rememberTokenSet(el: HTMLElement, t: TokenSet): void {
105
+ lastWritten.set(el, t);
106
+ }
107
+
108
+ /** The set `applyTokenSet` last wrote to `el`, or undefined when nothing did — an app
109
+ * without the palette layer, whose base tokens come from tokens.css. */
110
+ export function rememberedTokenSet(el: HTMLElement): TokenSet | undefined {
111
+ return lastWritten.get(el);
112
+ }
@@ -0,0 +1,225 @@
1
+ import { useEffect } from "react";
2
+ import { create } from "zustand";
3
+ import { persist } from "zustand/middleware";
4
+ import { logger } from "../lib/logger";
5
+ import { readStored } from "../lib/safe-storage";
6
+ import { useMediaQuery } from "../hooks/use-media-query";
7
+ import { CONTRAST_STEPPED_VARS, contrastStep, rememberedTokenSet } from "./contrast-tokens";
8
+ import { applyTokenSet, DEFAULT_PRESET } from "./palette-presets";
9
+ import type { PersistedAppearanceOptions } from "./text-size";
10
+
11
+ /**
12
+ * "More contrast" (docs/text-size-harmonization.md §2.4, §5, §10.5, §10.6): a switch
13
+ * beside the text size, on by choice or — on "System" — when the device asks for more
14
+ * contrast (`prefers-contrast: more`). Any text size.
15
+ *
16
+ * In force it is `<html data-contrast="more">` (else `"standard"`), which does two things:
17
+ * - the inline colour tokens are re-written one step stronger (`applyTokenSet`'s
18
+ * contrast flag — see contrast-tokens.ts for what moves and why it is JS);
19
+ * - tokens.css raises `--focus-ring-width` from 2 to 3 px and turns on the `contrast:`
20
+ * variant for anything else a component wants to change.
21
+ *
22
+ * The names below are frozen like the text size's: server-kit's `CONTRAST_MODES`, the
23
+ * stored format, the attribute.
24
+ */
25
+
26
+ /** The stored choices (§10.6): server-kit's `CONTRAST_MODES`. "system" IS a stored value
27
+ * — the PATCH rule refuses an explicit null, so null means only "never chosen". */
28
+ export const CONTRAST_MODES = ["system", "standard", "more"] as const;
29
+
30
+ export type ContrastMode = (typeof CONTRAST_MODES)[number];
31
+
32
+ /** What is in force on `<html data-contrast>`: a mode with "system" resolved. */
33
+ export type ContrastLevel = "standard" | "more";
34
+
35
+ const CONTRAST_ATTRIBUTE = "data-contrast";
36
+ const PREFERS_MORE_QUERY = "(prefers-contrast: more)";
37
+
38
+ export function isContrastMode(value: unknown): value is ContrastMode {
39
+ return value === "system" || value === "standard" || value === "more";
40
+ }
41
+
42
+ /** The mode in force: **this device's own choice → the account's → "system"** (§2.6).
43
+ * The one order the pre-paint call, the apply hook and `useAccountAppearance` share. */
44
+ export function resolveContrastMode(device: unknown, account?: unknown): ContrastMode {
45
+ if (isContrastMode(device)) return device;
46
+ if (isContrastMode(account)) return account;
47
+ return "system";
48
+ }
49
+
50
+ /** Whether the device asks for more contrast now. False without `matchMedia`. */
51
+ export function prefersMoreContrast(): boolean {
52
+ try {
53
+ return typeof window !== "undefined" && typeof window.matchMedia === "function"
54
+ ? window.matchMedia(PREFERS_MORE_QUERY).matches
55
+ : false;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ /** A mode to the level it puts on `<html>`; "system" asks the device (or takes
62
+ * `prefersMore`, when the caller already knows). */
63
+ export function contrastLevel(mode: ContrastMode, prefersMore: boolean = prefersMoreContrast()): ContrastLevel {
64
+ return mode === "more" || (mode === "system" && prefersMore) ? "more" : "standard";
65
+ }
66
+
67
+ /** The level `<html>` carries now; "standard" without one. */
68
+ export function readContrastLevel(): ContrastLevel {
69
+ if (typeof document === "undefined") return "standard";
70
+ return document.documentElement.getAttribute(CONTRAST_ATTRIBUTE) === "more" ? "more" : "standard";
71
+ }
72
+
73
+ /**
74
+ * Re-write the contrast-dependent tokens on `el` for its current `data-contrast`.
75
+ *
76
+ * With a palette layer (`applyTokenSet` has written a set here), that set is applied
77
+ * again and steps itself. Without one — an app whose base tokens come from tokens.css,
78
+ * like Kurvenschmiede's — the four stepped tokens are written over the stylesheet for
79
+ * the default preset in the current light/dark, or removed again.
80
+ */
81
+ function reapplyContrastTokens(el: HTMLElement): void {
82
+ const written = rememberedTokenSet(el);
83
+ if (written) {
84
+ applyTokenSet(el, written);
85
+ return;
86
+ }
87
+ for (const name of CONTRAST_STEPPED_VARS) el.style.removeProperty(name);
88
+ if (el.getAttribute(CONTRAST_ATTRIBUTE) !== "more") return;
89
+ const step = contrastStep(DEFAULT_PRESET[el.classList.contains("dark") ? "dark" : "light"]);
90
+ el.style.setProperty("--text-secondary", step.textSecondary);
91
+ el.style.setProperty("--text-muted", step.textMuted);
92
+ el.style.setProperty("--text-placeholder", step.textPlaceholder);
93
+ el.style.setProperty("--border", step.border);
94
+ }
95
+
96
+ /** Put `level` on `<html>` and re-write the colour tokens for it. */
97
+ export function applyContrast(level: ContrastLevel): void {
98
+ if (typeof document === "undefined") return;
99
+ const root = document.documentElement;
100
+ root.setAttribute(CONTRAST_ATTRIBUTE, level);
101
+ reapplyContrastTokens(root);
102
+ }
103
+
104
+ /* ── Storage: the frozen format, the pre-paint call ─────────────────────── */
105
+
106
+ /**
107
+ * What a contrast store persists under its key — FROZEN, like the text size's:
108
+ *
109
+ * {"state":{"contrast":"more"},"version":1}
110
+ *
111
+ * `contrast` is one of {@link CONTRAST_MODES}, or `null` / absent when this device never
112
+ * chose.
113
+ */
114
+ export interface PersistedContrast {
115
+ state?: { contrast?: ContrastMode | null };
116
+ version?: number;
117
+ }
118
+
119
+ /** This device's own choice under `storageKey`, or `null` when it never chose. */
120
+ export function readPersistedContrast(storageKey: string): ContrastMode | null {
121
+ try {
122
+ const blob = JSON.parse(readStored(storageKey) ?? "null") as PersistedContrast | null;
123
+ const mode = blob?.state?.contrast;
124
+ return isContrastMode(mode) ? mode : null;
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Pre-paint (§10.3, §10.5): this device's choice under `storageKey` — else the account's
132
+ * last known value, else "system" — resolved against `prefers-contrast` and applied
133
+ * BEFORE `createRoot`. Call it next to `applyPersistedPalette`, in EITHER order — the
134
+ * palette steps itself when `data-contrast` is already set, and this re-applies the
135
+ * palette's set when it is not; no app has to put one first (0.32.1, keksdose's 0.32
136
+ * report asked which). Returns the level applied.
137
+ */
138
+ export function applyPersistedContrast(storageKey: string, options: PersistedAppearanceOptions = {}): ContrastLevel {
139
+ let account: unknown;
140
+ try {
141
+ account = options.account?.();
142
+ } catch {
143
+ account = undefined;
144
+ }
145
+ const level = contrastLevel(resolveContrastMode(readPersistedContrast(storageKey), account));
146
+ applyContrast(level);
147
+ return level;
148
+ }
149
+
150
+ /* ── The store ──────────────────────────────────────────────────────────── */
151
+
152
+ export interface ContrastState {
153
+ /** This device's own choice (persisted), or `null` when it never chose — then the
154
+ * account's value applies, else "system". */
155
+ contrast: ContrastMode | null;
156
+ /** Store this device's choice; `null` forgets it. */
157
+ setContrast: (contrast: ContrastMode | null) => void;
158
+ }
159
+
160
+ export interface ApplyContrastOptions {
161
+ /** The account's value (`/auth/me`'s `contrast`): it applies while this device has no
162
+ * choice of its own. */
163
+ account?: string | null;
164
+ }
165
+
166
+ /**
167
+ * Build a contrast store bound to a consumer-supplied localStorage key, shaped like
168
+ * `createThemeStore` and {@link createTextSizeStore} (§6). It holds the DEVICE's choice;
169
+ * the account's value comes in through `useApplyContrast({ account })`.
170
+ *
171
+ * `useApplyContrast` — mount it once near the root, beside `useApplyPalette`:
172
+ * - puts the level on `<html>` and steps the tokens;
173
+ * - on "System", follows `prefers-contrast: more` live (§10.5);
174
+ * - without a palette layer, re-writes the stepped tokens when the light/dark class
175
+ * changes, since they differ per theme.
176
+ *
177
+ * The persisted format is {@link PersistedContrast}, and frozen.
178
+ */
179
+ export function createContrastStore(storageKey: string) {
180
+ const useContrastStore = create<ContrastState>()(
181
+ logger(
182
+ persist(
183
+ (set) => ({
184
+ contrast: null,
185
+ setContrast: (contrast) => set({ contrast: isContrastMode(contrast) ? contrast : null }),
186
+ }),
187
+ {
188
+ name: storageKey,
189
+ version: 1,
190
+ partialize: (s) => ({ contrast: s.contrast }),
191
+ merge: (persisted, current) => {
192
+ const value = (persisted as { contrast?: unknown } | null)?.contrast;
193
+ return { ...current, contrast: isContrastMode(value) ? value : null };
194
+ },
195
+ },
196
+ ),
197
+ "contrast",
198
+ ),
199
+ );
200
+
201
+ function useApplyContrast({ account }: ApplyContrastOptions = {}): ContrastLevel {
202
+ const device = useContrastStore((s) => s.contrast);
203
+ // Subscribed whatever the mode (hooks cannot be skipped); only "system" reads it.
204
+ const prefersMore = useMediaQuery(PREFERS_MORE_QUERY, false);
205
+ const level = contrastLevel(resolveContrastMode(device, account), prefersMore);
206
+ useEffect(() => {
207
+ applyContrast(level);
208
+ }, [level]);
209
+ // Without a palette layer the stepped tokens are per theme: follow `.dark`.
210
+ useEffect(() => {
211
+ if (typeof MutationObserver === "undefined") return;
212
+ const root = document.documentElement;
213
+ const observer = new MutationObserver(() => {
214
+ if (!rememberedTokenSet(root)) reapplyContrastTokens(root);
215
+ });
216
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
217
+ return () => observer.disconnect();
218
+ }, []);
219
+ return level;
220
+ }
221
+
222
+ return { useContrastStore, useApplyContrast };
223
+ }
224
+
225
+ export type ContrastStore = ReturnType<typeof createContrastStore>;
@@ -15,6 +15,7 @@
15
15
  // (the default preset here mirrors those exactly).
16
16
 
17
17
  import type { HeatStops } from "./chart-palette";
18
+ import { contrastStep, rememberTokenSet } from "./contrast-tokens";
18
19
 
19
20
  export interface TokenSet {
20
21
  // Backgrounds & chrome
@@ -740,15 +741,34 @@ export function presetById(id: string): PalettePreset {
740
741
  * have been silent and only a dead function would have noticed. The list is gone;
741
742
  * the test beside this asserts instead that every field of a `TokenSet` reaches the
742
743
  * element, which is what that list was standing in for.
744
+ *
745
+ * **More contrast (0.32, docs/text-size-harmonization.md §5, §10.5).** With `contrast`
746
+ * the text and border tokens are written one step stronger, and `--text-placeholder` —
747
+ * otherwise derived in tokens.css — is written inline too; without it that inline
748
+ * placeholder is removed again, so the stylesheet's formula applies. The step is made
749
+ * HERE because these tokens are inline: no `[data-contrast=more]` rule could beat them.
750
+ *
751
+ * `contrast` defaults to the element's own `data-contrast="more"`, which the contrast
752
+ * layer (`applyContrast`, `useApplyContrast`) sets first. That is how the palette and the
753
+ * contrast cooperate without knowing each other: a palette change re-applies through
754
+ * here and keeps the contrast, and a contrast change re-applies the set remembered here.
743
755
  */
744
- export function applyTokenSet(el: HTMLElement, t: TokenSet): void {
756
+ export function applyTokenSet(
757
+ el: HTMLElement,
758
+ t: TokenSet,
759
+ contrast: boolean = el.getAttribute("data-contrast") === "more",
760
+ ): void {
761
+ rememberTokenSet(el, t);
762
+ const step = contrast ? contrastStep(t) : null;
745
763
  el.style.setProperty("--bg-page", t.bgPage);
746
764
  el.style.setProperty("--bg-surface", t.bgSurface);
747
765
  el.style.setProperty("--bg-surface-2", t.bgSurface2);
748
- el.style.setProperty("--border", t.border);
766
+ el.style.setProperty("--border", step?.border ?? t.border);
749
767
  el.style.setProperty("--text-primary", t.textPrimary);
750
- el.style.setProperty("--text-secondary", t.textSecondary);
751
- el.style.setProperty("--text-muted", t.textMuted);
768
+ el.style.setProperty("--text-secondary", step?.textSecondary ?? t.textSecondary);
769
+ el.style.setProperty("--text-muted", step?.textMuted ?? t.textMuted);
770
+ if (step) el.style.setProperty("--text-placeholder", step.textPlaceholder);
771
+ else el.style.removeProperty("--text-placeholder");
752
772
  el.style.setProperty("--brand", t.brand);
753
773
  el.style.setProperty("--brand-hover", t.brandHover);
754
774
  el.style.setProperty("--brand-contrast", t.brandContrast);
@@ -60,7 +60,12 @@ export function createPaletteStore(
60
60
  /** Apply the active preset's tokens to <html> as inline CSS custom properties,
61
61
  * re-applying when the preset or the light/dark theme changes. Tailwind v4
62
62
  * strips a bare root token block from the bundle, so this inline write from
63
- * the TS source of truth is what actually drives the colours. */
63
+ * the TS source of truth is what actually drives the colours.
64
+ *
65
+ * 0.32: keeps More contrast. `applyTokenSet` reads `<html data-contrast>`, which the
66
+ * contrast store sets, and steps the tokens itself; the contrast store re-applies
67
+ * the set written here when the contrast changes (docs/text-size-harmonization.md
68
+ * §10.5). Neither store needs the other. */
64
69
  function useApplyPalette(): void {
65
70
  const id = usePalette((s) => s.id);
66
71
  const mode = useTheme((s) => s.mode);