@eifi1/ui-kit 0.4.0 → 0.5.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 (410) hide show
  1. package/LICENSE +32 -0
  2. package/README.md +498 -57
  3. package/dist/chart.d.ts +10 -0
  4. package/dist/chart.js +10 -0
  5. package/dist/chart.js.map +1 -0
  6. package/dist/components/account-settings.d.ts +11 -0
  7. package/dist/components/account-settings.js +19 -12
  8. package/dist/components/account-settings.js.map +1 -1
  9. package/dist/components/alert-banner.d.ts +10 -6
  10. package/dist/components/alert-banner.js +20 -16
  11. package/dist/components/alert-banner.js.map +1 -1
  12. package/dist/components/amount-input.d.ts +30 -4
  13. package/dist/components/amount-input.js +36 -14
  14. package/dist/components/amount-input.js.map +1 -1
  15. package/dist/components/calculator.d.ts +24 -7
  16. package/dist/components/calculator.js +37 -20
  17. package/dist/components/calculator.js.map +1 -1
  18. package/dist/components/chart-zoom.d.ts +156 -0
  19. package/dist/components/chart-zoom.js +262 -0
  20. package/dist/components/chart-zoom.js.map +1 -0
  21. package/dist/components/chart.d.ts +28 -3
  22. package/dist/components/chart.js +71 -32
  23. package/dist/components/chart.js.map +1 -1
  24. package/dist/components/checkbox.d.ts +51 -0
  25. package/dist/components/checkbox.js +123 -0
  26. package/dist/components/checkbox.js.map +1 -0
  27. package/dist/components/chip.d.ts +128 -0
  28. package/dist/components/chip.js +357 -0
  29. package/dist/components/chip.js.map +1 -0
  30. package/dist/components/choice-parts.d.ts +24 -0
  31. package/dist/components/choice-parts.js +18 -0
  32. package/dist/components/choice-parts.js.map +1 -0
  33. package/dist/components/combobox-core.d.ts +52 -9
  34. package/dist/components/combobox-core.js +91 -20
  35. package/dist/components/combobox-core.js.map +1 -1
  36. package/dist/components/combobox.d.ts +43 -33
  37. package/dist/components/combobox.js +409 -340
  38. package/dist/components/combobox.js.map +1 -1
  39. package/dist/components/currency-select.d.ts +48 -8
  40. package/dist/components/currency-select.js +107 -65
  41. package/dist/components/currency-select.js.map +1 -1
  42. package/dist/components/data-table-filter-popover.d.ts +15 -3
  43. package/dist/components/data-table-filter-popover.js +23 -17
  44. package/dist/components/data-table-filter-popover.js.map +1 -1
  45. package/dist/components/data-table-filters.d.ts +1 -1
  46. package/dist/components/data-table-filters.js +3 -2
  47. package/dist/components/data-table-filters.js.map +1 -1
  48. package/dist/components/data-table-labels.d.ts +53 -2
  49. package/dist/components/data-table-labels.js +31 -2
  50. package/dist/components/data-table-labels.js.map +1 -1
  51. package/dist/components/data-table-pagination.d.ts +3 -1
  52. package/dist/components/data-table-pagination.js +16 -10
  53. package/dist/components/data-table-pagination.js.map +1 -1
  54. package/dist/components/data-table-sort.js +1 -0
  55. package/dist/components/data-table-sort.js.map +1 -1
  56. package/dist/components/data-table.d.ts +1 -1
  57. package/dist/components/data-table.js +201 -85
  58. package/dist/components/data-table.js.map +1 -1
  59. package/dist/components/date-picker.d.ts +25 -9
  60. package/dist/components/date-picker.js +232 -94
  61. package/dist/components/date-picker.js.map +1 -1
  62. package/dist/components/dropdown.d.ts +96 -20
  63. package/dist/components/dropdown.js +77 -33
  64. package/dist/components/dropdown.js.map +1 -1
  65. package/dist/components/entity-combobox.d.ts +5 -4
  66. package/dist/components/entity-combobox.js +107 -76
  67. package/dist/components/entity-combobox.js.map +1 -1
  68. package/dist/components/facing-pair.d.ts +36 -0
  69. package/dist/components/facing-pair.js +40 -0
  70. package/dist/components/facing-pair.js.map +1 -0
  71. package/dist/components/field-sync.d.ts +164 -0
  72. package/dist/components/field-sync.js +264 -0
  73. package/dist/components/field-sync.js.map +1 -0
  74. package/dist/components/file-dropzone.d.ts +38 -6
  75. package/dist/components/file-dropzone.js +34 -9
  76. package/dist/components/file-dropzone.js.map +1 -1
  77. package/dist/components/full-bleed-dialog.d.ts +37 -26
  78. package/dist/components/full-bleed-dialog.js +20 -5
  79. package/dist/components/full-bleed-dialog.js.map +1 -1
  80. package/dist/components/grouped-picker.d.ts +21 -10
  81. package/dist/components/grouped-picker.js +66 -52
  82. package/dist/components/grouped-picker.js.map +1 -1
  83. package/dist/components/hover-menu.d.ts +20 -4
  84. package/dist/components/hover-menu.js +15 -4
  85. package/dist/components/hover-menu.js.map +1 -1
  86. package/dist/components/mini-calendar.d.ts +102 -14
  87. package/dist/components/mini-calendar.js +256 -70
  88. package/dist/components/mini-calendar.js.map +1 -1
  89. package/dist/components/modal.d.ts +9 -3
  90. package/dist/components/modal.js +34 -45
  91. package/dist/components/modal.js.map +1 -1
  92. package/dist/components/month-picker.d.ts +79 -0
  93. package/dist/components/month-picker.js +342 -0
  94. package/dist/components/month-picker.js.map +1 -0
  95. package/dist/components/multi-entity-combobox.d.ts +7 -6
  96. package/dist/components/multi-entity-combobox.js +112 -72
  97. package/dist/components/multi-entity-combobox.js.map +1 -1
  98. package/dist/components/multi-select.d.ts +13 -11
  99. package/dist/components/multi-select.js +161 -84
  100. package/dist/components/multi-select.js.map +1 -1
  101. package/dist/components/number-field.d.ts +92 -0
  102. package/dist/components/number-field.js +111 -0
  103. package/dist/components/number-field.js.map +1 -0
  104. package/dist/components/number-input.d.ts +28 -2
  105. package/dist/components/number-input.js +12 -2
  106. package/dist/components/number-input.js.map +1 -1
  107. package/dist/components/numpad-sheet.d.ts +32 -4
  108. package/dist/components/numpad-sheet.js +30 -13
  109. package/dist/components/numpad-sheet.js.map +1 -1
  110. package/dist/components/page-contents.d.ts +90 -0
  111. package/dist/components/page-contents.js +170 -0
  112. package/dist/components/page-contents.js.map +1 -0
  113. package/dist/components/password-strength.d.ts +106 -0
  114. package/dist/components/password-strength.js +132 -0
  115. package/dist/components/password-strength.js.map +1 -0
  116. package/dist/components/picker-sheet.d.ts +23 -15
  117. package/dist/components/picker-sheet.js +34 -14
  118. package/dist/components/picker-sheet.js.map +1 -1
  119. package/dist/components/popover.d.ts +62 -4
  120. package/dist/components/popover.js +52 -2
  121. package/dist/components/popover.js.map +1 -1
  122. package/dist/components/search-field.d.ts +25 -7
  123. package/dist/components/search-field.js +7 -5
  124. package/dist/components/search-field.js.map +1 -1
  125. package/dist/components/series-chart-labels.d.ts +14 -0
  126. package/dist/components/series-chart-labels.js +11 -0
  127. package/dist/components/series-chart-labels.js.map +1 -0
  128. package/dist/components/series-chart.d.ts +207 -0
  129. package/dist/components/series-chart.js +223 -0
  130. package/dist/components/series-chart.js.map +1 -0
  131. package/dist/components/settings-fields.d.ts +14 -4
  132. package/dist/components/settings-fields.js +19 -8
  133. package/dist/components/settings-fields.js.map +1 -1
  134. package/dist/components/signature-pad.d.ts +119 -0
  135. package/dist/components/signature-pad.js +405 -0
  136. package/dist/components/signature-pad.js.map +1 -0
  137. package/dist/components/slider.d.ts +80 -0
  138. package/dist/components/slider.js +155 -0
  139. package/dist/components/slider.js.map +1 -0
  140. package/dist/components/sparkline.d.ts +93 -0
  141. package/dist/components/sparkline.js +205 -0
  142. package/dist/components/sparkline.js.map +1 -0
  143. package/dist/components/stat-tile.d.ts +151 -0
  144. package/dist/components/stat-tile.js +236 -0
  145. package/dist/components/stat-tile.js.map +1 -0
  146. package/dist/components/swipeable-row.d.ts +25 -2
  147. package/dist/components/swipeable-row.js +31 -0
  148. package/dist/components/swipeable-row.js.map +1 -1
  149. package/dist/components/switch.d.ts +60 -0
  150. package/dist/components/switch.js +81 -0
  151. package/dist/components/switch.js.map +1 -0
  152. package/dist/components/time-input.d.ts +58 -0
  153. package/dist/components/time-input.js +50 -0
  154. package/dist/components/time-input.js.map +1 -0
  155. package/dist/components/toggle-group.d.ts +18 -3
  156. package/dist/components/toggle-group.js +10 -6
  157. package/dist/components/toggle-group.js.map +1 -1
  158. package/dist/components/toggle-legend.d.ts +115 -0
  159. package/dist/components/toggle-legend.js +114 -0
  160. package/dist/components/toggle-legend.js.map +1 -0
  161. package/dist/components/tooltip.d.ts +40 -11
  162. package/dist/components/tooltip.js +77 -20
  163. package/dist/components/tooltip.js.map +1 -1
  164. package/dist/components/treemap.d.ts +132 -0
  165. package/dist/components/treemap.js +179 -0
  166. package/dist/components/treemap.js.map +1 -0
  167. package/dist/components/ui.d.ts +159 -46
  168. package/dist/components/ui.js +220 -115
  169. package/dist/components/ui.js.map +1 -1
  170. package/dist/components/use-mobile-reveal.js +1 -0
  171. package/dist/components/use-mobile-reveal.js.map +1 -1
  172. package/dist/components/use-table-state.d.ts +1 -1
  173. package/dist/components/use-table-state.js +38 -17
  174. package/dist/components/use-table-state.js.map +1 -1
  175. package/dist/components/user-avatar.d.ts +15 -4
  176. package/dist/components/user-avatar.js +5 -4
  177. package/dist/components/user-avatar.js.map +1 -1
  178. package/dist/components/wizard-stepper.js +3 -2
  179. package/dist/components/wizard-stepper.js.map +1 -1
  180. package/dist/{data-table-filters-aXyf0Xub.d.ts → data-table-filters-DM1auUfH.d.ts} +27 -5
  181. package/dist/data-table.d.ts +7 -0
  182. package/dist/data-table.js +12 -0
  183. package/dist/data-table.js.map +1 -0
  184. package/dist/feedback/feedback-attachment.js +12 -11
  185. package/dist/feedback/feedback-attachment.js.map +1 -1
  186. package/dist/feedback/feedback-dialog.js +2 -1
  187. package/dist/feedback/feedback-dialog.js.map +1 -1
  188. package/dist/feedback/feedback-inbox.d.ts +33 -7
  189. package/dist/feedback/feedback-inbox.js +49 -37
  190. package/dist/feedback/feedback-inbox.js.map +1 -1
  191. package/dist/feedback.d.ts +5 -0
  192. package/dist/feedback.js +5 -0
  193. package/dist/feedback.js.map +1 -0
  194. package/dist/hooks/use-anchored-panel.js +1 -0
  195. package/dist/hooks/use-anchored-panel.js.map +1 -1
  196. package/dist/hooks/use-anchored-rect.js +1 -0
  197. package/dist/hooks/use-anchored-rect.js.map +1 -1
  198. package/dist/hooks/use-announce.d.ts +46 -0
  199. package/dist/hooks/use-announce.js +37 -0
  200. package/dist/hooks/use-announce.js.map +1 -0
  201. package/dist/hooks/use-body-scroll-lock.js +1 -0
  202. package/dist/hooks/use-body-scroll-lock.js.map +1 -1
  203. package/dist/hooks/use-close-transition.js +1 -0
  204. package/dist/hooks/use-close-transition.js.map +1 -1
  205. package/dist/hooks/use-dismiss.js +1 -0
  206. package/dist/hooks/use-dismiss.js.map +1 -1
  207. package/dist/hooks/use-focus-trap.d.ts +16 -0
  208. package/dist/hooks/use-focus-trap.js +67 -0
  209. package/dist/hooks/use-focus-trap.js.map +1 -0
  210. package/dist/hooks/use-media-query.js +1 -0
  211. package/dist/hooks/use-media-query.js.map +1 -1
  212. package/dist/hooks/use-overlay-history.js +27 -1
  213. package/dist/hooks/use-overlay-history.js.map +1 -1
  214. package/dist/hooks/use-row-swipe.js +1 -0
  215. package/dist/hooks/use-row-swipe.js.map +1 -1
  216. package/dist/i18n/defaults.d.ts +31 -0
  217. package/dist/i18n/defaults.js +63 -0
  218. package/dist/i18n/defaults.js.map +1 -0
  219. package/dist/i18n/kit-labels.d.ts +250 -0
  220. package/dist/i18n/kit-labels.js +184 -0
  221. package/dist/i18n/kit-labels.js.map +1 -0
  222. package/dist/index.d.ts +47 -22
  223. package/dist/index.js +28 -0
  224. package/dist/index.js.map +1 -1
  225. package/dist/lib/calc.js +1 -0
  226. package/dist/lib/calc.js.map +1 -1
  227. package/dist/lib/cn.js +1 -0
  228. package/dist/lib/cn.js.map +1 -1
  229. package/dist/lib/dates.js +1 -0
  230. package/dist/lib/dates.js.map +1 -1
  231. package/dist/lib/logger.js +1 -0
  232. package/dist/lib/logger.js.map +1 -1
  233. package/dist/lib/safe-storage.js +1 -0
  234. package/dist/lib/safe-storage.js.map +1 -1
  235. package/dist/search/command-palette.d.ts +11 -1
  236. package/dist/search/command-palette.js +21 -15
  237. package/dist/search/command-palette.js.map +1 -1
  238. package/dist/search.d.ts +2 -0
  239. package/dist/search.js +3 -0
  240. package/dist/search.js.map +1 -0
  241. package/dist/shell/app-shell.d.ts +33 -4
  242. package/dist/shell/app-shell.js +275 -78
  243. package/dist/shell/app-shell.js.map +1 -1
  244. package/dist/shell/option-switcher-menu.js +3 -2
  245. package/dist/shell/option-switcher-menu.js.map +1 -1
  246. package/dist/shell/role-switcher.d.ts +6 -0
  247. package/dist/shell/role-switcher.js +7 -4
  248. package/dist/shell/role-switcher.js.map +1 -1
  249. package/dist/shell/top-bar.d.ts +15 -7
  250. package/dist/shell/top-bar.js +3 -5
  251. package/dist/shell/top-bar.js.map +1 -1
  252. package/dist/shell/topbar-action-menu.js +3 -2
  253. package/dist/shell/topbar-action-menu.js.map +1 -1
  254. package/dist/shell/topbar-controls.d.ts +5 -2
  255. package/dist/shell/topbar-controls.js +20 -15
  256. package/dist/shell/topbar-controls.js.map +1 -1
  257. package/dist/shell.d.ts +13 -0
  258. package/dist/shell.js +8 -0
  259. package/dist/shell.js.map +1 -0
  260. package/dist/theme/chart-palette.d.ts +19 -12
  261. package/dist/theme/chart-palette.js +24 -30
  262. package/dist/theme/chart-palette.js.map +1 -1
  263. package/dist/theme/color.d.ts +78 -0
  264. package/dist/theme/color.js +148 -0
  265. package/dist/theme/color.js.map +1 -0
  266. package/dist/theme/palette-derive.d.ts +140 -0
  267. package/dist/theme/palette-derive.js +376 -0
  268. package/dist/theme/palette-derive.js.map +1 -0
  269. package/dist/theme/palette-presets.d.ts +27 -5
  270. package/dist/theme/palette-presets.js +231 -33
  271. package/dist/theme/palette-presets.js.map +1 -1
  272. package/dist/theme/palette-store.js +1 -0
  273. package/dist/theme/palette-store.js.map +1 -1
  274. package/dist/theme/theme-store.js +1 -0
  275. package/dist/theme/theme-store.js.map +1 -1
  276. package/dist/tour/tour.d.ts +2 -1
  277. package/dist/tour/tour.js +15 -9
  278. package/dist/tour/tour.js.map +1 -1
  279. package/dist/tour.d.ts +2 -0
  280. package/dist/tour.js +3 -0
  281. package/dist/tour.js.map +1 -0
  282. package/dist/wizard/stepper-nav.js +10 -8
  283. package/dist/wizard/stepper-nav.js.map +1 -1
  284. package/dist/wizard/types.d.ts +18 -2
  285. package/dist/wizard/types.js +4 -1
  286. package/dist/wizard/types.js.map +1 -1
  287. package/dist/wizard/use-wizard.js +12 -4
  288. package/dist/wizard/use-wizard.js.map +1 -1
  289. package/dist/wizard/validation.js +1 -0
  290. package/dist/wizard/validation.js.map +1 -1
  291. package/dist/wizard/wizard-context.js +1 -0
  292. package/dist/wizard/wizard-context.js.map +1 -1
  293. package/dist/wizard/wizard-step.js +1 -0
  294. package/dist/wizard/wizard-step.js.map +1 -1
  295. package/dist/wizard/wizard-summary.js +5 -3
  296. package/dist/wizard/wizard-summary.js.map +1 -1
  297. package/dist/wizard.d.ts +8 -0
  298. package/dist/wizard.js +9 -0
  299. package/dist/wizard.js.map +1 -0
  300. package/package.json +67 -14
  301. package/src/chart.ts +22 -0
  302. package/src/components/account-settings.tsx +275 -0
  303. package/src/components/alert-banner.tsx +75 -0
  304. package/src/components/amount-input.tsx +471 -0
  305. package/src/components/calculator.tsx +252 -0
  306. package/src/components/chart-zoom.tsx +499 -0
  307. package/src/components/chart.tsx +363 -0
  308. package/src/components/checkbox.tsx +222 -0
  309. package/src/components/chip.tsx +570 -0
  310. package/src/components/choice-parts.ts +33 -0
  311. package/src/components/combobox-core.tsx +540 -0
  312. package/src/components/combobox.tsx +1026 -0
  313. package/src/components/currency-select.tsx +269 -0
  314. package/src/components/data-table-filter-popover.tsx +252 -0
  315. package/src/components/data-table-filters.ts +176 -0
  316. package/src/components/data-table-labels.ts +190 -0
  317. package/src/components/data-table-pagination.tsx +126 -0
  318. package/src/components/data-table-sort.ts +70 -0
  319. package/src/components/data-table.tsx +1740 -0
  320. package/src/components/date-picker.tsx +631 -0
  321. package/src/components/dropdown.tsx +341 -0
  322. package/src/components/entity-combobox.tsx +215 -0
  323. package/src/components/facing-pair.ts +86 -0
  324. package/src/components/field-sync.tsx +472 -0
  325. package/src/components/file-dropzone.tsx +181 -0
  326. package/src/components/full-bleed-dialog.tsx +179 -0
  327. package/src/components/grouped-picker.tsx +157 -0
  328. package/src/components/hover-menu.tsx +183 -0
  329. package/src/components/mini-calendar.tsx +554 -0
  330. package/src/components/modal.tsx +287 -0
  331. package/src/components/month-picker.tsx +549 -0
  332. package/src/components/multi-entity-combobox.tsx +226 -0
  333. package/src/components/multi-select.tsx +303 -0
  334. package/src/components/number-field.tsx +225 -0
  335. package/src/components/number-input.tsx +211 -0
  336. package/src/components/numpad-sheet.tsx +264 -0
  337. package/src/components/page-contents.tsx +289 -0
  338. package/src/components/password-strength.tsx +258 -0
  339. package/src/components/picker-sheet.tsx +251 -0
  340. package/src/components/popover.tsx +195 -0
  341. package/src/components/search-field.tsx +146 -0
  342. package/src/components/series-chart-labels.ts +26 -0
  343. package/src/components/series-chart.tsx +481 -0
  344. package/src/components/settings-fields.tsx +68 -0
  345. package/src/components/signature-pad.tsx +630 -0
  346. package/src/components/slider.tsx +313 -0
  347. package/src/components/sparkline.tsx +329 -0
  348. package/src/components/stat-tile.tsx +448 -0
  349. package/src/components/swipeable-row.tsx +271 -0
  350. package/src/components/switch.tsx +170 -0
  351. package/src/components/time-input.tsx +107 -0
  352. package/src/components/toggle-group.tsx +153 -0
  353. package/src/components/toggle-legend.tsx +229 -0
  354. package/src/components/tooltip.tsx +477 -0
  355. package/src/components/treemap.tsx +390 -0
  356. package/src/components/ui.tsx +1284 -0
  357. package/src/components/use-mobile-reveal.ts +54 -0
  358. package/src/components/use-table-state.ts +295 -0
  359. package/src/components/user-avatar.tsx +61 -0
  360. package/src/components/wizard-stepper.tsx +37 -0
  361. package/src/data-table.ts +19 -0
  362. package/src/feedback/feedback-attachment.tsx +246 -0
  363. package/src/feedback/feedback-dialog.tsx +209 -0
  364. package/src/feedback/feedback-inbox.tsx +615 -0
  365. package/src/feedback.ts +8 -0
  366. package/src/hooks/use-anchored-panel.ts +169 -0
  367. package/src/hooks/use-anchored-rect.ts +78 -0
  368. package/src/hooks/use-announce.ts +85 -0
  369. package/src/hooks/use-body-scroll-lock.ts +94 -0
  370. package/src/hooks/use-close-transition.ts +99 -0
  371. package/src/hooks/use-dismiss.ts +77 -0
  372. package/src/hooks/use-focus-trap.ts +160 -0
  373. package/src/hooks/use-media-query.ts +23 -0
  374. package/src/hooks/use-overlay-history.ts +345 -0
  375. package/src/hooks/use-row-swipe.ts +212 -0
  376. package/src/i18n/defaults.ts +68 -0
  377. package/src/i18n/kit-labels.tsx +447 -0
  378. package/src/index.ts +169 -0
  379. package/src/lib/calc.ts +309 -0
  380. package/src/lib/cn.ts +5 -0
  381. package/src/lib/dates.ts +187 -0
  382. package/src/lib/logger.ts +73 -0
  383. package/src/lib/safe-storage.ts +37 -0
  384. package/src/search/command-palette.tsx +342 -0
  385. package/src/search.ts +3 -0
  386. package/src/shell/app-shell.tsx +772 -0
  387. package/src/shell/option-switcher-menu.tsx +72 -0
  388. package/src/shell/role-switcher.tsx +46 -0
  389. package/src/shell/top-bar.tsx +39 -0
  390. package/src/shell/topbar-action-menu.tsx +91 -0
  391. package/src/shell/topbar-controls.tsx +229 -0
  392. package/src/shell.ts +10 -0
  393. package/src/theme/chart-palette.ts +145 -0
  394. package/src/theme/color.ts +266 -0
  395. package/src/theme/palette-derive.ts +681 -0
  396. package/src/theme/palette-presets.ts +760 -0
  397. package/src/theme/palette-store.ts +94 -0
  398. package/src/theme/theme-store.ts +145 -0
  399. package/src/tour/tour.tsx +586 -0
  400. package/src/tour.ts +3 -0
  401. package/src/vite-env.d.ts +26 -0
  402. package/src/wizard/stepper-nav.tsx +221 -0
  403. package/src/wizard/types.ts +178 -0
  404. package/src/wizard/use-wizard.ts +345 -0
  405. package/src/wizard/validation.ts +37 -0
  406. package/src/wizard/wizard-context.tsx +29 -0
  407. package/src/wizard/wizard-step.tsx +14 -0
  408. package/src/wizard/wizard-summary.tsx +69 -0
  409. package/src/wizard.ts +14 -0
  410. package/tokens.css +222 -20
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Colour maths for the palette system: sRGB ↔ OKLab ↔ OKLCH, WCAG contrast, gamut
3
+ * clipping, and colour-vision-deficiency simulation.
4
+ *
5
+ * Dependency-free on purpose. This package already ships 48MB of runtime dependencies
6
+ * for a 1.4MB library, and a colour-space conversion is forty lines of arithmetic that
7
+ * will not change — importing culori or chroma-js to get it would be the largest
8
+ * dependency in the kit, paid for by every consumer, to avoid writing down formulas
9
+ * that were published once and are now fixed.
10
+ *
11
+ * **Why OKLCH rather than HSL.** HSL's "lightness" is not lightness: `hsl(60 100% 50%)`
12
+ * (yellow) and `hsl(240 100% 50%)` (blue) claim the same 50% and differ by a factor of
13
+ * about twelve in perceived brightness. Any scheme that derives a palette by holding HSL
14
+ * lightness constant therefore produces a set that looks wildly uneven, and any scheme
15
+ * that solves for contrast in HSL has to solve separately per hue. OKLab is built so a
16
+ * given L means the same apparent lightness at every hue, which is what makes "same
17
+ * lightness, different hue" a usable rule and what makes a contrast solve converge.
18
+ *
19
+ * Reference: Björn Ottosson, "A perceptual color space for image processing" (2020).
20
+ */
21
+ interface Rgb {
22
+ /** 0–255. */
23
+ r: number;
24
+ g: number;
25
+ b: number;
26
+ }
27
+ interface Oklch {
28
+ /** Perceptual lightness, 0–1. */
29
+ l: number;
30
+ /** Chroma, 0–~0.4 in sRGB. */
31
+ c: number;
32
+ /** Hue angle in degrees, 0–360. */
33
+ h: number;
34
+ }
35
+ /** `#rgb` or `#rrggbb` → components, or null when it is neither. */
36
+ declare function parseHex(hex: string): Rgb | null;
37
+ declare function toHex({ r, g, b }: Rgb): string;
38
+ declare function rgbToOklch(rgb: Rgb): Oklch;
39
+ /**
40
+ * OKLCH → sRGB, reducing CHROMA until the colour fits.
41
+ *
42
+ * Clamping the channels instead — the obvious approach — shifts hue and lightness
43
+ * together and unpredictably: a saturated blue that overflows clips to a different blue
44
+ * that is also lighter. Holding L and h and giving up chroma keeps the colour
45
+ * recognisably the one that was asked for, which matters when the whole point is to
46
+ * derive a family at a fixed lightness.
47
+ */
48
+ declare function oklchToRgb(target: Oklch): Rgb;
49
+ declare function hexToOklch(hex: string): Oklch | null;
50
+ declare function oklchToHex(lch: Oklch): string;
51
+ /** WCAG 2.x relative luminance. */
52
+ declare function luminance(rgb: Rgb): number;
53
+ /** WCAG 2.x contrast ratio, 1–21. Order-independent. */
54
+ declare function contrast(a: Rgb | string, b: Rgb | string): number;
55
+ /**
56
+ * The lightness at hue `h`, chroma `c` that hits `target` contrast against `against`.
57
+ *
58
+ * Binary search rather than algebra: OKLab lightness does not map to WCAG luminance in
59
+ * closed form, and it is not even monotonic in the general case once gamut clipping
60
+ * starts reducing chroma. It IS monotonic over the half-range we search — away from the
61
+ * background — which is why the direction is chosen first.
62
+ *
63
+ * Returns the best lightness found. If the target is unreachable at this hue and chroma
64
+ * (a saturated yellow cannot make 7:1 against white at any lightness without going grey)
65
+ * it returns the extreme end, and the caller should check the contrast it actually got
66
+ * rather than assuming success — `deriveTokenSet` does exactly that.
67
+ */
68
+ declare function solveLightness(target: number, against: Rgb, h: number, c: number, direction: "lighter" | "darker"): number;
69
+ type CvdType = "protanopia" | "deuteranopia" | "tritanopia";
70
+ declare function simulateCvd(rgb: Rgb, type: CvdType): Rgb;
71
+ /**
72
+ * Perceptual distance in OKLab, which is what that space is FOR: a Euclidean distance
73
+ * in OKLab corresponds far more closely to "how different do these look" than one in
74
+ * sRGB, where the same numeric gap is obvious among greens and invisible among blues.
75
+ */
76
+ declare function deltaE(a: Rgb, b: Rgb): number;
77
+
78
+ export { type CvdType, type Oklch, type Rgb, contrast, deltaE, hexToOklch, luminance, oklchToHex, oklchToRgb, parseHex, rgbToOklch, simulateCvd, solveLightness, toHex };
@@ -0,0 +1,148 @@
1
+ "use client";
2
+ function parseHex(hex) {
3
+ const h = hex.trim().replace(/^#/, "");
4
+ const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
5
+ if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;
6
+ return {
7
+ r: parseInt(full.slice(0, 2), 16),
8
+ g: parseInt(full.slice(2, 4), 16),
9
+ b: parseInt(full.slice(4, 6), 16)
10
+ };
11
+ }
12
+ function toHex({ r, g, b }) {
13
+ const part = (v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0");
14
+ return `#${part(r)}${part(g)}${part(b)}`;
15
+ }
16
+ function toLinear(channel) {
17
+ const c = channel / 255;
18
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
19
+ }
20
+ function fromLinear(channel) {
21
+ const c = channel <= 31308e-7 ? 12.92 * channel : 1.055 * channel ** (1 / 2.4) - 0.055;
22
+ return c * 255;
23
+ }
24
+ function rgbToOklch(rgb) {
25
+ const r = toLinear(rgb.r);
26
+ const g = toLinear(rgb.g);
27
+ const b = toLinear(rgb.b);
28
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
29
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
30
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
31
+ const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
32
+ const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
33
+ const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
34
+ const c = Math.sqrt(A * A + B * B);
35
+ const h = c < 1e-6 ? 0 : (Math.atan2(B, A) * 180 / Math.PI + 360) % 360;
36
+ return { l: L, c, h };
37
+ }
38
+ function oklchToRgbRaw({ l, c, h }) {
39
+ const rad = h * Math.PI / 180;
40
+ const A = c * Math.cos(rad);
41
+ const B = c * Math.sin(rad);
42
+ const l_ = (l + 0.3963377774 * A + 0.2158037573 * B) ** 3;
43
+ const m_ = (l - 0.1055613458 * A - 0.0638541728 * B) ** 3;
44
+ const s_ = (l - 0.0894841775 * A - 1.291485548 * B) ** 3;
45
+ return {
46
+ r: fromLinear(4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_),
47
+ g: fromLinear(-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_),
48
+ b: fromLinear(-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_)
49
+ };
50
+ }
51
+ function inGamut({ r, g, b }) {
52
+ const ok = (v) => v >= -0.5 && v <= 255.5;
53
+ return ok(r) && ok(g) && ok(b);
54
+ }
55
+ function oklchToRgb(target) {
56
+ const direct = oklchToRgbRaw(target);
57
+ if (inGamut(direct)) return direct;
58
+ let lo = 0;
59
+ let hi = target.c;
60
+ for (let i = 0; i < 20; i++) {
61
+ const mid = (lo + hi) / 2;
62
+ if (inGamut(oklchToRgbRaw({ ...target, c: mid }))) lo = mid;
63
+ else hi = mid;
64
+ }
65
+ return oklchToRgbRaw({ ...target, c: lo });
66
+ }
67
+ function hexToOklch(hex) {
68
+ const rgb = parseHex(hex);
69
+ return rgb ? rgbToOklch(rgb) : null;
70
+ }
71
+ function oklchToHex(lch) {
72
+ return toHex(oklchToRgb(lch));
73
+ }
74
+ function luminance(rgb) {
75
+ return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);
76
+ }
77
+ function contrast(a, b) {
78
+ const ra = typeof a === "string" ? parseHex(a) : a;
79
+ const rb = typeof b === "string" ? parseHex(b) : b;
80
+ if (!ra || !rb) return 1;
81
+ const la = luminance(ra);
82
+ const lb = luminance(rb);
83
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
84
+ }
85
+ function solveLightness(target, against, h, c, direction) {
86
+ const bgL = rgbToOklch(against).l;
87
+ let lo = direction === "darker" ? 0 : bgL;
88
+ let hi = direction === "darker" ? bgL : 1;
89
+ for (let i = 0; i < 24; i++) {
90
+ const mid = (lo + hi) / 2;
91
+ const got = contrast(oklchToRgb({ l: mid, c, h }), against);
92
+ if (got < target) {
93
+ if (direction === "darker") hi = mid;
94
+ else lo = mid;
95
+ } else if (direction === "darker") lo = mid;
96
+ else hi = mid;
97
+ }
98
+ return direction === "darker" ? lo : hi;
99
+ }
100
+ const CVD_MATRIX = {
101
+ protanopia: [
102
+ [0.1705, 0.8295, 0],
103
+ [0.1705, 0.8295, 0],
104
+ [-45e-4, 45e-4, 1]
105
+ ],
106
+ deuteranopia: [
107
+ [0.33066, 0.66934, 0],
108
+ [0.33066, 0.66934, 0],
109
+ [-0.02785, 0.02785, 1]
110
+ ],
111
+ tritanopia: [
112
+ [1, 0.1273, -0.1273],
113
+ [0, 0.8739, 0.1261],
114
+ [0, 0.8739, 0.1261]
115
+ ]
116
+ };
117
+ function simulateCvd(rgb, type) {
118
+ const m = CVD_MATRIX[type];
119
+ const [r, g, b] = [toLinear(rgb.r), toLinear(rgb.g), toLinear(rgb.b)];
120
+ return {
121
+ r: fromLinear(m[0][0] * r + m[0][1] * g + m[0][2] * b),
122
+ g: fromLinear(m[1][0] * r + m[1][1] * g + m[1][2] * b),
123
+ b: fromLinear(m[2][0] * r + m[2][1] * g + m[2][2] * b)
124
+ };
125
+ }
126
+ function deltaE(a, b) {
127
+ const A = rgbToOklch(a);
128
+ const B = rgbToOklch(b);
129
+ const aA = A.c * Math.cos(A.h * Math.PI / 180);
130
+ const aB = A.c * Math.sin(A.h * Math.PI / 180);
131
+ const bA = B.c * Math.cos(B.h * Math.PI / 180);
132
+ const bB = B.c * Math.sin(B.h * Math.PI / 180);
133
+ return Math.hypot(A.l - B.l, aA - bA, aB - bB);
134
+ }
135
+ export {
136
+ contrast,
137
+ deltaE,
138
+ hexToOklch,
139
+ luminance,
140
+ oklchToHex,
141
+ oklchToRgb,
142
+ parseHex,
143
+ rgbToOklch,
144
+ simulateCvd,
145
+ solveLightness,
146
+ toHex
147
+ };
148
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * Colour maths for the palette system: sRGB ↔ OKLab ↔ OKLCH, WCAG contrast, gamut\n * clipping, and colour-vision-deficiency simulation.\n *\n * Dependency-free on purpose. This package already ships 48MB of runtime dependencies\n * for a 1.4MB library, and a colour-space conversion is forty lines of arithmetic that\n * will not change — importing culori or chroma-js to get it would be the largest\n * dependency in the kit, paid for by every consumer, to avoid writing down formulas\n * that were published once and are now fixed.\n *\n * **Why OKLCH rather than HSL.** HSL's \"lightness\" is not lightness: `hsl(60 100% 50%)`\n * (yellow) and `hsl(240 100% 50%)` (blue) claim the same 50% and differ by a factor of\n * about twelve in perceived brightness. Any scheme that derives a palette by holding HSL\n * lightness constant therefore produces a set that looks wildly uneven, and any scheme\n * that solves for contrast in HSL has to solve separately per hue. OKLab is built so a\n * given L means the same apparent lightness at every hue, which is what makes \"same\n * lightness, different hue\" a usable rule and what makes a contrast solve converge.\n *\n * Reference: Björn Ottosson, \"A perceptual color space for image processing\" (2020).\n */\n\nexport interface Rgb {\n /** 0–255. */\n r: number;\n g: number;\n b: number;\n}\n\nexport interface Oklch {\n /** Perceptual lightness, 0–1. */\n l: number;\n /** Chroma, 0–~0.4 in sRGB. */\n c: number;\n /** Hue angle in degrees, 0–360. */\n h: number;\n}\n\n/* ── sRGB ─────────────────────────────────────────────────────────────────── */\n\n/** `#rgb` or `#rrggbb` → components, or null when it is neither. */\nexport function parseHex(hex: string): Rgb | null {\n const h = hex.trim().replace(/^#/, \"\");\n const full =\n h.length === 3\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;\n return {\n r: parseInt(full.slice(0, 2), 16),\n g: parseInt(full.slice(2, 4), 16),\n b: parseInt(full.slice(4, 6), 16),\n };\n}\n\nexport function toHex({ r, g, b }: Rgb): string {\n const part = (v: number) =>\n Math.max(0, Math.min(255, Math.round(v)))\n .toString(16)\n .padStart(2, \"0\");\n return `#${part(r)}${part(g)}${part(b)}`;\n}\n\n/** sRGB 0–255 → linear-light 0–1. The transfer function, not a gamma of 2.2. */\nfunction toLinear(channel: number): number {\n const c = channel / 255;\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\nfunction fromLinear(channel: number): number {\n const c = channel <= 0.0031308 ? 12.92 * channel : 1.055 * channel ** (1 / 2.4) - 0.055;\n return c * 255;\n}\n\n/* ── OKLab / OKLCH ────────────────────────────────────────────────────────── */\n\nexport function rgbToOklch(rgb: Rgb): Oklch {\n const r = toLinear(rgb.r);\n const g = toLinear(rgb.g);\n const b = toLinear(rgb.b);\n\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n\n const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;\n const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;\n const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;\n\n const c = Math.sqrt(A * A + B * B);\n // Hue is meaningless at zero chroma; 0 keeps it stable rather than NaN-adjacent.\n const h = c < 1e-6 ? 0 : ((Math.atan2(B, A) * 180) / Math.PI + 360) % 360;\n return { l: L, c, h };\n}\n\n/** OKLCH → sRGB, WITHOUT gamut clipping. May return components outside 0–255. */\nfunction oklchToRgbRaw({ l, c, h }: Oklch): Rgb {\n const rad = (h * Math.PI) / 180;\n const A = c * Math.cos(rad);\n const B = c * Math.sin(rad);\n\n const l_ = (l + 0.3963377774 * A + 0.2158037573 * B) ** 3;\n const m_ = (l - 0.1055613458 * A - 0.0638541728 * B) ** 3;\n const s_ = (l - 0.0894841775 * A - 1.291485548 * B) ** 3;\n\n return {\n r: fromLinear(4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_),\n g: fromLinear(-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_),\n b: fromLinear(-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_),\n };\n}\n\nfunction inGamut({ r, g, b }: Rgb): boolean {\n const ok = (v: number) => v >= -0.5 && v <= 255.5;\n return ok(r) && ok(g) && ok(b);\n}\n\n/**\n * OKLCH → sRGB, reducing CHROMA until the colour fits.\n *\n * Clamping the channels instead — the obvious approach — shifts hue and lightness\n * together and unpredictably: a saturated blue that overflows clips to a different blue\n * that is also lighter. Holding L and h and giving up chroma keeps the colour\n * recognisably the one that was asked for, which matters when the whole point is to\n * derive a family at a fixed lightness.\n */\nexport function oklchToRgb(target: Oklch): Rgb {\n const direct = oklchToRgbRaw(target);\n if (inGamut(direct)) return direct;\n\n let lo = 0;\n let hi = target.c;\n // 20 halvings resolves chroma to ~4e-7, far below a perceptible step.\n for (let i = 0; i < 20; i++) {\n const mid = (lo + hi) / 2;\n if (inGamut(oklchToRgbRaw({ ...target, c: mid }))) lo = mid;\n else hi = mid;\n }\n return oklchToRgbRaw({ ...target, c: lo });\n}\n\nexport function hexToOklch(hex: string): Oklch | null {\n const rgb = parseHex(hex);\n return rgb ? rgbToOklch(rgb) : null;\n}\n\nexport function oklchToHex(lch: Oklch): string {\n return toHex(oklchToRgb(lch));\n}\n\n/* ── WCAG contrast ────────────────────────────────────────────────────────── */\n\n/** WCAG 2.x relative luminance. */\nexport function luminance(rgb: Rgb): number {\n return 0.2126 * toLinear(rgb.r) + 0.7152 * toLinear(rgb.g) + 0.0722 * toLinear(rgb.b);\n}\n\n/** WCAG 2.x contrast ratio, 1–21. Order-independent. */\nexport function contrast(a: Rgb | string, b: Rgb | string): number {\n const ra = typeof a === \"string\" ? parseHex(a) : a;\n const rb = typeof b === \"string\" ? parseHex(b) : b;\n if (!ra || !rb) return 1;\n const la = luminance(ra);\n const lb = luminance(rb);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * The lightness at hue `h`, chroma `c` that hits `target` contrast against `against`.\n *\n * Binary search rather than algebra: OKLab lightness does not map to WCAG luminance in\n * closed form, and it is not even monotonic in the general case once gamut clipping\n * starts reducing chroma. It IS monotonic over the half-range we search — away from the\n * background — which is why the direction is chosen first.\n *\n * Returns the best lightness found. If the target is unreachable at this hue and chroma\n * (a saturated yellow cannot make 7:1 against white at any lightness without going grey)\n * it returns the extreme end, and the caller should check the contrast it actually got\n * rather than assuming success — `deriveTokenSet` does exactly that.\n */\nexport function solveLightness(\n target: number,\n against: Rgb,\n h: number,\n c: number,\n direction: \"lighter\" | \"darker\",\n): number {\n // The half-range on the far side of the background from where we are heading.\n // Going darker, that is [0, backgroundL]; going lighter, [backgroundL, 1]. Contrast is\n // monotonic across each of those halves, which is what makes a binary search valid —\n // it is NOT monotonic across the whole 0..1 range, because it turns around at the\n // background's own lightness.\n const bgL = rgbToOklch(against).l;\n let lo = direction === \"darker\" ? 0 : bgL;\n let hi = direction === \"darker\" ? bgL : 1;\n\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2;\n const got = contrast(oklchToRgb({ l: mid, c, h }), against);\n // Moving AWAY from the background raises contrast; which way that is depends on\n // whether we are going lighter or darker.\n if (got < target) {\n if (direction === \"darker\") hi = mid;\n else lo = mid;\n } else if (direction === \"darker\") lo = mid;\n else hi = mid;\n }\n return direction === \"darker\" ? lo : hi;\n}\n\n/* ── Colour-vision deficiency ─────────────────────────────────────────────── */\n\nexport type CvdType = \"protanopia\" | \"deuteranopia\" | \"tritanopia\";\n\n/**\n * Brettel/Viénot-style dichromat simulation in linear RGB.\n *\n * An approximation, and a well-established one. It is here so the kit can FAIL a\n * categorical ramp that two of its series become indistinguishable under, rather than\n * asserting CVD-safety in a comment the way `chart-palette.ts` did — the audit found\n * four of the nine default light chart hues below the 3:1 they were claimed to hold.\n */\nconst CVD_MATRIX: Record<CvdType, number[][]> = {\n protanopia: [\n [0.1705, 0.8295, 0],\n [0.1705, 0.8295, 0],\n [-0.0045, 0.0045, 1],\n ],\n deuteranopia: [\n [0.33066, 0.66934, 0],\n [0.33066, 0.66934, 0],\n [-0.02785, 0.02785, 1],\n ],\n tritanopia: [\n [1, 0.1273, -0.1273],\n [0, 0.8739, 0.1261],\n [0, 0.8739, 0.1261],\n ],\n};\n\nexport function simulateCvd(rgb: Rgb, type: CvdType): Rgb {\n const m = CVD_MATRIX[type];\n const [r, g, b] = [toLinear(rgb.r), toLinear(rgb.g), toLinear(rgb.b)];\n return {\n r: fromLinear(m[0][0] * r + m[0][1] * g + m[0][2] * b),\n g: fromLinear(m[1][0] * r + m[1][1] * g + m[1][2] * b),\n b: fromLinear(m[2][0] * r + m[2][1] * g + m[2][2] * b),\n };\n}\n\n/**\n * Perceptual distance in OKLab, which is what that space is FOR: a Euclidean distance\n * in OKLab corresponds far more closely to \"how different do these look\" than one in\n * sRGB, where the same numeric gap is obvious among greens and invisible among blues.\n */\nexport function deltaE(a: Rgb, b: Rgb): number {\n const A = rgbToOklch(a);\n const B = rgbToOklch(b);\n const aA = A.c * Math.cos((A.h * Math.PI) / 180);\n const aB = A.c * Math.sin((A.h * Math.PI) / 180);\n const bA = B.c * Math.cos((B.h * Math.PI) / 180);\n const bB = B.c * Math.sin((B.h * Math.PI) / 180);\n return Math.hypot(A.l - B.l, aA - bA, aB - bB);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,140 @@
1
+ import { ThemeMode } from './theme-store.js';
2
+ import { TokenSet } from './palette-presets.js';
3
+ import { CvdType, Oklch } from './color.js';
4
+ import 'zustand/middleware';
5
+ import 'zustand';
6
+ import './chart-palette.js';
7
+
8
+ /**
9
+ * Build a whole palette from one brand colour, plus any number of colours pinned by
10
+ * hand, with every contrast ratio SOLVED rather than chosen and then hoped for.
11
+ *
12
+ * The presets this package shipped were assembled by eye and annotated with the ratios
13
+ * they were believed to hold — `tokens.css` records "7.0:1 and 4.8:1", and
14
+ * `palette-presets.ts` records "2.67:1 … lifted they clear it at 3.30:1". Nothing
15
+ * computed any of them, and the audit found four of the nine default light chart hues
16
+ * below the 3:1 that WCAG 1.4.11 requires of a graphical object. A comment is not a
17
+ * test. Everything here is derived to a target and then measured, and
18
+ * {@link auditPalette} returns the measurements so a caller can refuse a palette that
19
+ * does not hold up.
20
+ *
21
+ * WHAT IS DERIVED AND WHAT IS NOT. The UI palette and the CATEGORICAL CHART RAMP solve
22
+ * different problems and are deliberately kept apart. A UI colour needs contrast against
23
+ * one known surface and carries a fixed meaning; a chart colour needs to stay separable
24
+ * from eight unknown siblings, at roughly equal salience, with no implied order.
25
+ * Optimising a set for one makes it worse at the other, which is why `deriveTokenSet`
26
+ * leaves `chart` alone unless you explicitly ask for {@link deriveChartRamp}.
27
+ */
28
+ /** A role whose hue may be pinned by the caller instead of chosen by the deriver. */
29
+ type AnchorRole = "brand" | "accent" | "danger" | "warning" | "success" | "info";
30
+ type PaletteAnchors = {
31
+ brand: string;
32
+ } & Partial<Record<Exclude<AnchorRole, "brand">, string>>;
33
+ interface DeriveOptions {
34
+ anchors: PaletteAnchors;
35
+ mode: ThemeMode;
36
+ /**
37
+ * How strongly the surfaces carry the brand's hue, 0–1 of the maximum useful tint.
38
+ * 0 gives neutral greys; the shipped default is a visible warmth, because a page that
39
+ * is very slightly the brand's hue reads as considered where a pure grey reads as
40
+ * unstyled. Above ~0.5 the surfaces start competing with the content.
41
+ */
42
+ surfaceTint?: number;
43
+ /** Contrast targets for the three text roles against the WORST surface. */
44
+ textContrast?: {
45
+ primary?: number;
46
+ secondary?: number;
47
+ muted?: number;
48
+ };
49
+ }
50
+ /** The semantic layer, which lives in tokens.css rather than in `TokenSet`. */
51
+ interface SemanticTokens {
52
+ danger: string;
53
+ dangerHover: string;
54
+ dangerContrast: string;
55
+ dangerBorder: string;
56
+ dangerBg: string;
57
+ warning: string;
58
+ warningBorder: string;
59
+ warningBg: string;
60
+ info: string;
61
+ infoBorder: string;
62
+ infoBg: string;
63
+ success: string;
64
+ successBorder: string;
65
+ successBg: string;
66
+ /** Present only when a `accent` (secondary brand) anchor was supplied. */
67
+ accent?: string;
68
+ accentContrast?: string;
69
+ accentBg?: string;
70
+ }
71
+ interface ContrastCheck {
72
+ /** e.g. "textMuted on bgPage". */
73
+ pair: string;
74
+ ratio: number;
75
+ /** What WCAG asks of this pair. */
76
+ required: number;
77
+ /** Which rule the requirement comes from, for a reader who wants to argue with it. */
78
+ rule: string;
79
+ passes: boolean;
80
+ }
81
+ interface ContrastReport {
82
+ checks: ContrastCheck[];
83
+ failures: ContrastCheck[];
84
+ passes: boolean;
85
+ }
86
+ interface DerivedPalette {
87
+ tokens: TokenSet;
88
+ semantic: SemanticTokens;
89
+ audit: ContrastReport;
90
+ /** Compromises the deriver had to make, in plain words. Never silent. */
91
+ warnings: string[];
92
+ }
93
+ declare function derivePalette({ anchors, mode, surfaceTint, textContrast, }: DeriveOptions): DerivedPalette;
94
+ /**
95
+ * Measure every pair that has a requirement, and say which ones fail.
96
+ *
97
+ * This is the part the shipped presets were missing. `derivePalette` runs it on the way
98
+ * out, and the preset test runs it over everything the package ships, so a palette that
99
+ * does not hold up cannot be merged with a comment claiming that it does.
100
+ */
101
+ declare function auditPalette(t: TokenSet, semantic?: SemanticTokens): ContrastReport;
102
+ interface ChartRampReport {
103
+ colors: string[];
104
+ /** Series pairs that are too close under normal vision or any simulated CVD. */
105
+ collisions: Array<{
106
+ a: number;
107
+ b: number;
108
+ under: "normal" | CvdType;
109
+ distance: number;
110
+ }>;
111
+ /** Series that do not clear 3:1 against a surface they may be drawn on. */
112
+ lowContrast: Array<{
113
+ index: number;
114
+ surface: string;
115
+ ratio: number;
116
+ }>;
117
+ /** The worst pairwise separation, across normal vision and all three dichromacies. */
118
+ minSeparation: number;
119
+ /** The worst contrast any series reaches against any surface it was measured on. */
120
+ minContrast: number;
121
+ passes: boolean;
122
+ }
123
+ /**
124
+ * A categorical ramp of `count` hues, evenly spaced and equal in perceived lightness,
125
+ * then MEASURED for separability — including under each dichromacy.
126
+ *
127
+ * Offered but not used by default, and the distinction matters. Evenly spaced hues at
128
+ * one lightness are a reasonable generic answer; Paul Tol's "Muted" set, which this
129
+ * package ships, is a better specific one, because it was optimised against real
130
+ * confusion lines rather than derived from a formula. So this exists for a consumer who
131
+ * wants their charts to follow their brand and is willing to read the report — not as
132
+ * a silent upgrade to the default.
133
+ */
134
+ declare function deriveChartRamp(brandHex: string, mode: ThemeMode, surfaces: string[], count?: number): ChartRampReport;
135
+ /** Audit a ramp somebody else chose — the shipped Paul Tol set, say. */
136
+ declare function auditChartRamp(colors: string[], surfaces: string[]): ChartRampReport;
137
+ /** Convenience: the OKLCH of a hex, for a caller inspecting an anchor. */
138
+ declare function describe(hex: string): Oklch | null;
139
+
140
+ export { type AnchorRole, type ChartRampReport, type ContrastCheck, type ContrastReport, type DeriveOptions, type DerivedPalette, type PaletteAnchors, type SemanticTokens, auditChartRamp, auditPalette, deriveChartRamp, derivePalette, describe };