@panneau/ui 5.0.0-alpha.1 → 5.0.0-alpha.2

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 (384) hide show
  1. package/README.md +263 -6
  2. package/bin/check-theme.js +5 -0
  3. package/dist/alert/Alert.css +28 -42
  4. package/dist/alert/Alert.d.ts +18 -4
  5. package/dist/alert/Alert.d.ts.map +1 -1
  6. package/dist/alert/Alert.js +4 -3
  7. package/dist/alert/Alert.js.map +1 -1
  8. package/dist/alert/Alert.module.js +14 -16
  9. package/dist/alert/Alert.module.js.map +1 -1
  10. package/dist/alert/tokens.d.ts +3 -0
  11. package/dist/alert/tokens.d.ts.map +1 -0
  12. package/dist/alert/tokens.js +21 -0
  13. package/dist/alert/tokens.js.map +1 -0
  14. package/dist/badge/Badge.css +24 -36
  15. package/dist/badge/Badge.d.ts +18 -3
  16. package/dist/badge/Badge.d.ts.map +1 -1
  17. package/dist/badge/Badge.js +3 -2
  18. package/dist/badge/Badge.js.map +1 -1
  19. package/dist/badge/Badge.module.js +9 -13
  20. package/dist/badge/Badge.module.js.map +1 -1
  21. package/dist/badge/tokens.d.ts +3 -0
  22. package/dist/badge/tokens.d.ts.map +1 -0
  23. package/dist/badge/tokens.js +23 -0
  24. package/dist/badge/tokens.js.map +1 -0
  25. package/dist/button/Button.css +58 -42
  26. package/dist/button/Button.d.ts +30 -3
  27. package/dist/button/Button.d.ts.map +1 -1
  28. package/dist/button/Button.js +27 -2
  29. package/dist/button/Button.js.map +1 -1
  30. package/dist/button/Button.module.js +14 -14
  31. package/dist/button/Button.module.js.map +1 -1
  32. package/dist/button/tokens.d.ts +3 -0
  33. package/dist/button/tokens.d.ts.map +1 -0
  34. package/dist/button/tokens.js +25 -0
  35. package/dist/button/tokens.js.map +1 -0
  36. package/dist/card/Card.css +27 -20
  37. package/dist/card/Card.js +1 -1
  38. package/dist/card/Card.js.map +1 -1
  39. package/dist/card/Card.module.js +5 -5
  40. package/dist/card/Card.module.js.map +1 -1
  41. package/dist/card/tokens.d.ts +3 -0
  42. package/dist/card/tokens.d.ts.map +1 -0
  43. package/dist/card/tokens.js +31 -0
  44. package/dist/card/tokens.js.map +1 -0
  45. package/dist/checkbox/Checkbox.css +22 -21
  46. package/dist/checkbox/Checkbox.d.ts.map +1 -1
  47. package/dist/checkbox/Checkbox.js +1 -0
  48. package/dist/checkbox/Checkbox.js.map +1 -1
  49. package/dist/checkbox/Checkbox.module.js +4 -4
  50. package/dist/checkbox/Checkbox.module.js.map +1 -1
  51. package/dist/checkbox/ChoiceChrome.d.ts +3 -1
  52. package/dist/checkbox/ChoiceChrome.d.ts.map +1 -1
  53. package/dist/checkbox/ChoiceChrome.js +2 -2
  54. package/dist/checkbox/ChoiceChrome.js.map +1 -1
  55. package/dist/checkbox/tokens.d.ts +3 -0
  56. package/dist/checkbox/tokens.d.ts.map +1 -0
  57. package/dist/checkbox/tokens.js +21 -0
  58. package/dist/checkbox/tokens.js.map +1 -0
  59. package/dist/combo-box/ComboBox.css +4 -4
  60. package/dist/combo-box/ComboBox.js +1 -1
  61. package/dist/combo-box/ComboBox.js.map +1 -1
  62. package/dist/combo-box/ComboBox.module.js +3 -3
  63. package/dist/container/Container.css +32 -0
  64. package/dist/container/Container.d.ts +15 -0
  65. package/dist/container/Container.d.ts.map +1 -0
  66. package/dist/container/Container.js +18 -0
  67. package/dist/container/Container.js.map +1 -0
  68. package/dist/container/Container.module.js +20 -0
  69. package/dist/container/Container.module.js.map +1 -0
  70. package/dist/container/tokens.d.ts +3 -0
  71. package/dist/container/tokens.d.ts.map +1 -0
  72. package/dist/container/tokens.js +19 -0
  73. package/dist/container/tokens.js.map +1 -0
  74. package/dist/date-picker/DatePicker.css +30 -30
  75. package/dist/date-picker/DatePicker.js +1 -1
  76. package/dist/date-picker/DatePicker.js.map +1 -1
  77. package/dist/date-picker/DatePicker.module.js +11 -11
  78. package/dist/date-picker/DatePicker.module.js.map +1 -1
  79. package/dist/date-picker/calendarTokens.d.ts +3 -0
  80. package/dist/date-picker/calendarTokens.d.ts.map +1 -0
  81. package/dist/date-picker/calendarTokens.js +17 -0
  82. package/dist/date-picker/calendarTokens.js.map +1 -0
  83. package/dist/dialog/Dialog.css +40 -36
  84. package/dist/dialog/Dialog.js +1 -1
  85. package/dist/dialog/Dialog.js.map +1 -1
  86. package/dist/dialog/Dialog.module.js +11 -11
  87. package/dist/dialog/Dialog.module.js.map +1 -1
  88. package/dist/dialog/tokens.d.ts +3 -0
  89. package/dist/dialog/tokens.d.ts.map +1 -0
  90. package/dist/dialog/tokens.js +29 -0
  91. package/dist/dialog/tokens.js.map +1 -0
  92. package/dist/disclosure/Disclosure.css +12 -12
  93. package/dist/disclosure/Disclosure.d.ts.map +1 -1
  94. package/dist/disclosure/Disclosure.js +1 -1
  95. package/dist/disclosure/Disclosure.js.map +1 -1
  96. package/dist/disclosure/Disclosure.module.js +7 -7
  97. package/dist/disclosure/Disclosure.module.js.map +1 -1
  98. package/dist/disclosure/tokens.d.ts +3 -0
  99. package/dist/disclosure/tokens.d.ts.map +1 -0
  100. package/dist/disclosure/tokens.js +15 -0
  101. package/dist/disclosure/tokens.js.map +1 -0
  102. package/dist/divider/Divider.css +17 -0
  103. package/dist/divider/Divider.d.ts +8 -0
  104. package/dist/divider/Divider.d.ts.map +1 -0
  105. package/dist/divider/Divider.js +21 -0
  106. package/dist/divider/Divider.js.map +1 -0
  107. package/dist/divider/Divider.module.js +12 -0
  108. package/dist/divider/Divider.module.js.map +1 -0
  109. package/dist/divider/tokens.d.ts +3 -0
  110. package/dist/divider/tokens.d.ts.map +1 -0
  111. package/dist/divider/tokens.js +13 -0
  112. package/dist/divider/tokens.js.map +1 -0
  113. package/dist/field/Field.css +40 -37
  114. package/dist/field/Field.module.js +11 -11
  115. package/dist/field/Field.module.js.map +1 -1
  116. package/dist/field/Fieldset.js +1 -1
  117. package/dist/field/Fieldset.js.map +1 -1
  118. package/dist/field/inputTokens.d.ts +3 -0
  119. package/dist/field/inputTokens.d.ts.map +1 -0
  120. package/dist/field/inputTokens.js +21 -0
  121. package/dist/field/inputTokens.js.map +1 -0
  122. package/dist/field/tokens.d.ts +3 -0
  123. package/dist/field/tokens.d.ts.map +1 -0
  124. package/dist/field/tokens.js +15 -0
  125. package/dist/field/tokens.js.map +1 -0
  126. package/dist/grid/Grid.css +174 -0
  127. package/dist/grid/Grid.d.ts +28 -0
  128. package/dist/grid/Grid.d.ts.map +1 -0
  129. package/dist/grid/Grid.js +31 -0
  130. package/dist/grid/Grid.js.map +1 -0
  131. package/dist/grid/Grid.module.js +12 -0
  132. package/dist/grid/Grid.module.js.map +1 -0
  133. package/dist/grid/GridItem.css +59 -0
  134. package/dist/grid/GridItem.module.js +7 -0
  135. package/dist/grid/GridItem.module.js.map +1 -0
  136. package/dist/grid/tokens.d.ts +3 -0
  137. package/dist/grid/tokens.d.ts.map +1 -0
  138. package/dist/grid/tokens.js +10 -0
  139. package/dist/grid/tokens.js.map +1 -0
  140. package/dist/heading/Heading.css +52 -0
  141. package/dist/heading/Heading.d.ts +17 -0
  142. package/dist/heading/Heading.d.ts.map +1 -0
  143. package/dist/heading/Heading.js +17 -0
  144. package/dist/heading/Heading.js.map +1 -0
  145. package/dist/heading/Heading.module.js +16 -0
  146. package/dist/heading/Heading.module.js.map +1 -0
  147. package/dist/heading/tokens.d.ts +6 -0
  148. package/dist/heading/tokens.d.ts.map +1 -0
  149. package/dist/heading/tokens.js +66 -0
  150. package/dist/heading/tokens.js.map +1 -0
  151. package/dist/icon/Icon.css +1 -1
  152. package/dist/icon/Icon.js +1 -1
  153. package/dist/icon/Icon.js.map +1 -1
  154. package/dist/icon/Icon.module.js +1 -1
  155. package/dist/icon-button/IconButton.css +2 -2
  156. package/dist/icon-button/IconButton.d.ts +1 -1
  157. package/dist/icon-button/IconButton.d.ts.map +1 -1
  158. package/dist/icon-button/IconButton.js +3 -3
  159. package/dist/icon-button/IconButton.js.map +1 -1
  160. package/dist/icon-button/IconButton.module.js +2 -2
  161. package/dist/index.d.ts +25 -5
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +20 -2
  164. package/dist/inline/Inline.css +142 -0
  165. package/dist/inline/Inline.d.ts +17 -0
  166. package/dist/inline/Inline.d.ts.map +1 -0
  167. package/dist/inline/Inline.js +21 -0
  168. package/dist/inline/Inline.js.map +1 -0
  169. package/dist/inline/Inline.module.js +12 -0
  170. package/dist/inline/Inline.module.js.map +1 -0
  171. package/dist/inline/tokens.d.ts +3 -0
  172. package/dist/inline/tokens.d.ts.map +1 -0
  173. package/dist/inline/tokens.js +10 -0
  174. package/dist/inline/tokens.js.map +1 -0
  175. package/dist/layout/responsive.d.ts +38 -0
  176. package/dist/layout/responsive.d.ts.map +1 -0
  177. package/dist/layout/responsive.js +49 -0
  178. package/dist/layout/responsive.js.map +1 -0
  179. package/dist/menu/Menu.js +2 -2
  180. package/dist/menu/Menu.js.map +1 -1
  181. package/dist/number-input/NumberInput.css +12 -12
  182. package/dist/number-input/NumberInput.js +1 -1
  183. package/dist/number-input/NumberInput.js.map +1 -1
  184. package/dist/number-input/NumberInput.module.js +4 -4
  185. package/dist/number-input/NumberInput.module.js.map +1 -1
  186. package/dist/number-input/tokens.d.ts +3 -0
  187. package/dist/number-input/tokens.d.ts.map +1 -0
  188. package/dist/number-input/tokens.js +10 -0
  189. package/dist/number-input/tokens.js.map +1 -0
  190. package/dist/popover/Popover.css +13 -12
  191. package/dist/popover/Popover.js +2 -2
  192. package/dist/popover/Popover.js.map +1 -1
  193. package/dist/popover/Popover.module.js +3 -3
  194. package/dist/popover/Popover.module.js.map +1 -1
  195. package/dist/popover/tokens.d.ts +3 -0
  196. package/dist/popover/tokens.d.ts.map +1 -0
  197. package/dist/popover/tokens.js +19 -0
  198. package/dist/popover/tokens.js.map +1 -0
  199. package/dist/prose/Prose.css +141 -0
  200. package/dist/prose/Prose.d.ts +11 -0
  201. package/dist/prose/Prose.d.ts.map +1 -0
  202. package/dist/prose/Prose.js +18 -0
  203. package/dist/prose/Prose.js.map +1 -0
  204. package/dist/prose/Prose.module.js +8 -0
  205. package/dist/prose/Prose.module.js.map +1 -0
  206. package/dist/prose/tokens.d.ts +3 -0
  207. package/dist/prose/tokens.d.ts.map +1 -0
  208. package/dist/prose/tokens.js +33 -0
  209. package/dist/prose/tokens.js.map +1 -0
  210. package/dist/radio-group/RadioGroup.css +21 -20
  211. package/dist/radio-group/RadioGroup.d.ts.map +1 -1
  212. package/dist/radio-group/RadioGroup.js +2 -2
  213. package/dist/radio-group/RadioGroup.js.map +1 -1
  214. package/dist/radio-group/RadioGroup.module.js +6 -6
  215. package/dist/radio-group/RadioGroup.module.js.map +1 -1
  216. package/dist/radio-group/tokens.d.ts +3 -0
  217. package/dist/radio-group/tokens.d.ts.map +1 -0
  218. package/dist/radio-group/tokens.js +19 -0
  219. package/dist/radio-group/tokens.js.map +1 -0
  220. package/dist/root/PanneauRoot.css +243 -20
  221. package/dist/root/PanneauRoot.d.ts +28 -4
  222. package/dist/root/PanneauRoot.d.ts.map +1 -1
  223. package/dist/root/PanneauRoot.js +104 -21
  224. package/dist/root/PanneauRoot.js.map +1 -1
  225. package/dist/root/PanneauRoot.module.js +11 -3
  226. package/dist/root/PanneauRoot.module.js.map +1 -1
  227. package/dist/root/ThemeScope.d.ts +24 -0
  228. package/dist/root/ThemeScope.d.ts.map +1 -0
  229. package/dist/root/ThemeScope.js +49 -0
  230. package/dist/root/ThemeScope.js.map +1 -0
  231. package/dist/select/ListBox.css +23 -22
  232. package/dist/select/ListBox.module.js +12 -12
  233. package/dist/select/ListBox.module.js.map +1 -1
  234. package/dist/select/Select.js +1 -1
  235. package/dist/select/Select.js.map +1 -1
  236. package/dist/select/listBoxTokens.d.ts +3 -0
  237. package/dist/select/listBoxTokens.d.ts.map +1 -0
  238. package/dist/select/listBoxTokens.js +19 -0
  239. package/dist/select/listBoxTokens.js.map +1 -0
  240. package/dist/spinner/Spinner.css +8 -8
  241. package/dist/spinner/Spinner.js +1 -1
  242. package/dist/spinner/Spinner.js.map +1 -1
  243. package/dist/spinner/Spinner.module.js +5 -5
  244. package/dist/spinner/Spinner.module.js.map +1 -1
  245. package/dist/spinner/tokens.d.ts +3 -0
  246. package/dist/spinner/tokens.d.ts.map +1 -0
  247. package/dist/spinner/tokens.js +10 -0
  248. package/dist/spinner/tokens.js.map +1 -0
  249. package/dist/stack/Stack.css +135 -0
  250. package/dist/stack/Stack.d.ts +15 -0
  251. package/dist/stack/Stack.d.ts.map +1 -0
  252. package/dist/stack/Stack.js +21 -0
  253. package/dist/stack/Stack.js.map +1 -0
  254. package/dist/stack/Stack.module.js +8 -0
  255. package/dist/stack/Stack.module.js.map +1 -0
  256. package/dist/stack/tokens.d.ts +3 -0
  257. package/dist/stack/tokens.d.ts.map +1 -0
  258. package/dist/stack/tokens.js +10 -0
  259. package/dist/stack/tokens.js.map +1 -0
  260. package/dist/switch/Switch.css +34 -25
  261. package/dist/switch/Switch.d.ts.map +1 -1
  262. package/dist/switch/Switch.js +1 -0
  263. package/dist/switch/Switch.js.map +1 -1
  264. package/dist/switch/Switch.module.js +4 -4
  265. package/dist/switch/Switch.module.js.map +1 -1
  266. package/dist/switch/tokens.d.ts +3 -0
  267. package/dist/switch/tokens.d.ts.map +1 -0
  268. package/dist/switch/tokens.js +25 -0
  269. package/dist/switch/tokens.js.map +1 -0
  270. package/dist/tabs/Tabs.css +24 -23
  271. package/dist/tabs/Tabs.js +4 -4
  272. package/dist/tabs/Tabs.js.map +1 -1
  273. package/dist/tabs/Tabs.module.js +4 -4
  274. package/dist/tabs/Tabs.module.js.map +1 -1
  275. package/dist/tabs/tokens.d.ts +3 -0
  276. package/dist/tabs/tokens.d.ts.map +1 -0
  277. package/dist/tabs/tokens.js +25 -0
  278. package/dist/tabs/tokens.js.map +1 -0
  279. package/dist/text/Text.css +74 -0
  280. package/dist/text/Text.d.ts +27 -0
  281. package/dist/text/Text.d.ts.map +1 -0
  282. package/dist/text/Text.js +16 -0
  283. package/dist/text/Text.js.map +1 -0
  284. package/dist/text/Text.module.js +30 -0
  285. package/dist/text/Text.module.js.map +1 -0
  286. package/dist/text/tokens.d.ts +3 -0
  287. package/dist/text/tokens.d.ts.map +1 -0
  288. package/dist/text/tokens.js +10 -0
  289. package/dist/text/tokens.js.map +1 -0
  290. package/dist/text-area/TextArea.css +1 -1
  291. package/dist/text-area/TextArea.js +1 -1
  292. package/dist/text-area/TextArea.js.map +1 -1
  293. package/dist/text-area/TextArea.module.js +1 -1
  294. package/dist/text-input/TextInput.js +1 -1
  295. package/dist/text-input/TextInput.js.map +1 -1
  296. package/dist/theme/Tone.css +70 -0
  297. package/dist/theme/Tone.module.js +20 -0
  298. package/dist/theme/Tone.module.js.map +1 -0
  299. package/dist/theme/bootstrap.d.ts +10 -0
  300. package/dist/theme/bootstrap.d.ts.map +1 -1
  301. package/dist/theme/bootstrap.js +48 -5
  302. package/dist/theme/bootstrap.js.map +1 -1
  303. package/dist/theme/chartColors.d.ts +23 -0
  304. package/dist/theme/chartColors.d.ts.map +1 -0
  305. package/dist/theme/chartColors.js +27 -0
  306. package/dist/theme/chartColors.js.map +1 -0
  307. package/dist/theme/checkTheme.d.ts +39 -0
  308. package/dist/theme/checkTheme.d.ts.map +1 -0
  309. package/dist/theme/checkTheme.js +132 -0
  310. package/dist/theme/checkTheme.js.map +1 -0
  311. package/dist/theme/cli.d.ts +14 -0
  312. package/dist/theme/cli.d.ts.map +1 -0
  313. package/dist/theme/cli.js +61 -0
  314. package/dist/theme/cli.js.map +1 -0
  315. package/dist/theme/color.d.ts +30 -0
  316. package/dist/theme/color.d.ts.map +1 -0
  317. package/dist/theme/color.js +94 -0
  318. package/dist/theme/color.js.map +1 -0
  319. package/dist/theme/colorVision.d.ts +23 -0
  320. package/dist/theme/colorVision.d.ts.map +1 -0
  321. package/dist/theme/colorVision.js +140 -0
  322. package/dist/theme/colorVision.js.map +1 -0
  323. package/dist/theme/componentTokens.d.ts +24 -0
  324. package/dist/theme/componentTokens.d.ts.map +1 -0
  325. package/dist/theme/componentTokens.js +20 -0
  326. package/dist/theme/componentTokens.js.map +1 -0
  327. package/dist/theme/components.d.ts +38 -0
  328. package/dist/theme/components.d.ts.map +1 -0
  329. package/dist/theme/components.js +65 -0
  330. package/dist/theme/components.js.map +1 -0
  331. package/dist/theme/defineTheme.d.ts +85 -0
  332. package/dist/theme/defineTheme.d.ts.map +1 -0
  333. package/dist/theme/defineTheme.js +130 -0
  334. package/dist/theme/defineTheme.js.map +1 -0
  335. package/dist/theme/fonts.d.ts +32 -0
  336. package/dist/theme/fonts.d.ts.map +1 -0
  337. package/dist/theme/fonts.js +36 -0
  338. package/dist/theme/fonts.js.map +1 -0
  339. package/dist/theme/index.d.ts +13 -0
  340. package/dist/theme/index.d.ts.map +1 -0
  341. package/dist/theme/index.js +10 -0
  342. package/dist/theme/semanticTokens.d.ts +9 -0
  343. package/dist/theme/semanticTokens.d.ts.map +1 -0
  344. package/dist/theme/semanticTokens.js +110 -0
  345. package/dist/theme/semanticTokens.js.map +1 -0
  346. package/dist/theme/theme.d.ts +61 -7
  347. package/dist/theme/theme.d.ts.map +1 -1
  348. package/dist/theme/theme.js +100 -10
  349. package/dist/theme/theme.js.map +1 -1
  350. package/dist/theme/toneClass.d.ts +8 -0
  351. package/dist/theme/toneClass.d.ts.map +1 -0
  352. package/dist/theme/toneClass.js +15 -0
  353. package/dist/theme/toneClass.js.map +1 -0
  354. package/dist/theme/tones.d.ts +89 -0
  355. package/dist/theme/tones.d.ts.map +1 -0
  356. package/dist/theme/tones.js +127 -0
  357. package/dist/theme/tones.js.map +1 -0
  358. package/dist/time-input/TimeInput.js +1 -1
  359. package/dist/time-input/TimeInput.js.map +1 -1
  360. package/dist/toast/Toast.css +22 -44
  361. package/dist/toast/Toast.d.ts +5 -3
  362. package/dist/toast/Toast.d.ts.map +1 -1
  363. package/dist/toast/Toast.js +4 -3
  364. package/dist/toast/Toast.js.map +1 -1
  365. package/dist/toast/Toast.module.js +10 -16
  366. package/dist/toast/Toast.module.js.map +1 -1
  367. package/dist/toast/tokens.d.ts +3 -0
  368. package/dist/toast/tokens.d.ts.map +1 -0
  369. package/dist/toast/tokens.js +21 -0
  370. package/dist/toast/tokens.js.map +1 -0
  371. package/dist/tooltip/Tooltip.css +12 -11
  372. package/dist/tooltip/Tooltip.js +1 -1
  373. package/dist/tooltip/Tooltip.js.map +1 -1
  374. package/dist/tooltip/Tooltip.module.js +2 -2
  375. package/dist/tooltip/Tooltip.module.js.map +1 -1
  376. package/dist/tooltip/tokens.d.ts +3 -0
  377. package/dist/tooltip/tokens.d.ts.map +1 -0
  378. package/dist/tooltip/tokens.js +21 -0
  379. package/dist/tooltip/tokens.js.map +1 -0
  380. package/dist/utils/cx.d.ts +1 -1
  381. package/dist/utils/cx.d.ts.map +1 -1
  382. package/dist/utils/cx.js +2 -2
  383. package/dist/utils/cx.js.map +1 -1
  384. package/package.json +15 -3
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/theme/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAkD5B,CAAC"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/theme/bootstrap.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,MAAM,EACb,EAAE,SAA6B,EAAE,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,UAAU,CAUZ;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAoFF,CAAC"}
@@ -1,5 +1,22 @@
1
1
  //#region src/theme/bootstrap.ts
2
2
  /**
3
+ * A tone from one of Bootstrap's theme colors, read from its CSS variables (`--bs-<color>`,
4
+ * `-text-emphasis`, `-bg-subtle`, `-border-subtle`), so that a site's custom colors carry over:
5
+ * `tones: { brand: bootstrapTone('brand') }`. Text on the subtle background uses the emphasis
6
+ * color, as Bootstrap's alerts do. Light colors need dark solid text: `{ solidText: 'var(--bs-dark)' }`.
7
+ */
8
+ function bootstrapTone(color, { solidText = "var(--bs-white)" } = {}) {
9
+ return {
10
+ text: `var(--bs-${color}-text-emphasis)`,
11
+ subtle: `var(--bs-${color}-bg-subtle)`,
12
+ border: `var(--bs-${color}-border-subtle)`,
13
+ outline: `var(--bs-${color})`,
14
+ solid: `var(--bs-${color})`,
15
+ solidHover: `color-mix(in srgb, var(--bs-${color}), #000 15%)`,
16
+ solidText
17
+ };
18
+ }
19
+ /**
3
20
  * Maps Panneau's tokens to the CSS variables of Bootstrap 5.3, for sites that use Bootstrap: Panneau
4
21
  * then follows the site's colors, fonts and radii, and its color mode (`data-bs-theme`), without
5
22
  * using any Bootstrap class. Bootstrap's CSS must be loaded by the site.
@@ -32,16 +49,24 @@ var bootstrapTheme = {
32
49
  "panneau-color-success": "var(--bs-success-text-emphasis)",
33
50
  "panneau-color-success-subtle": "var(--bs-success-bg-subtle)",
34
51
  "panneau-color-success-border": "var(--bs-success-border-subtle)",
52
+ "panneau-color-success-solid": "var(--bs-success)",
53
+ "panneau-color-success-solid-hover": "color-mix(in srgb, var(--bs-success), #000 15%)",
54
+ "panneau-color-success-solid-text": "var(--bs-white)",
35
55
  "panneau-color-warning": "var(--bs-warning-text-emphasis)",
36
56
  "panneau-color-warning-subtle": "var(--bs-warning-bg-subtle)",
37
57
  "panneau-color-warning-border": "var(--bs-warning-border-subtle)",
58
+ "panneau-color-warning-solid": "var(--bs-warning)",
59
+ "panneau-color-warning-solid-hover": "color-mix(in srgb, var(--bs-warning), #000 10%)",
60
+ "panneau-color-warning-solid-text": "var(--bs-dark)",
38
61
  "panneau-color-info": "var(--bs-info-text-emphasis)",
39
62
  "panneau-color-info-subtle": "var(--bs-info-bg-subtle)",
40
63
  "panneau-color-info-border": "var(--bs-info-border-subtle)",
41
- "panneau-font-family": "var(--bs-body-font-family)",
64
+ "panneau-color-info-solid": "var(--bs-info)",
65
+ "panneau-color-info-solid-hover": "color-mix(in srgb, var(--bs-info), #000 10%)",
66
+ "panneau-color-info-solid-text": "var(--bs-dark)",
67
+ "panneau-font-family-body": "var(--bs-body-font-family)",
42
68
  "panneau-font-family-mono": "var(--bs-font-monospace)",
43
69
  "panneau-font-size-md": "var(--bs-body-font-size)",
44
- "panneau-font-size-control": "var(--bs-body-font-size)",
45
70
  "panneau-line-height": "var(--bs-body-line-height)",
46
71
  "panneau-radius-sm": "var(--bs-border-radius-sm)",
47
72
  "panneau-radius-md": "var(--bs-border-radius)",
@@ -51,11 +76,29 @@ var bootstrapTheme = {
51
76
  "panneau-shadow-sm": "var(--bs-box-shadow-sm)",
52
77
  "panneau-shadow-md": "var(--bs-box-shadow)",
53
78
  "panneau-shadow-lg": "var(--bs-box-shadow-lg)",
79
+ "panneau-heading-color": "var(--bs-heading-color)",
80
+ "panneau-badge-radius": "var(--bs-border-radius)",
81
+ "panneau-card-radius": "var(--bs-border-radius)",
82
+ "panneau-card-border-color": "var(--bs-border-color-translucent)",
83
+ "panneau-popover-radius": "var(--bs-border-radius)",
84
+ "panneau-popover-border-color": "var(--bs-border-color-translucent)",
85
+ "panneau-tooltip-bg": "var(--bs-emphasis-color)",
86
+ "panneau-tooltip-color": "var(--bs-body-bg)",
87
+ "panneau-tooltip-radius": "var(--bs-border-radius)"
88
+ },
89
+ tones: {
90
+ accent: bootstrapTone("primary"),
91
+ primary: bootstrapTone("primary"),
92
+ secondary: bootstrapTone("secondary")
93
+ },
94
+ densities: { comfortable: {
95
+ "panneau-font-size-control": "var(--bs-body-font-size)",
54
96
  "panneau-control-height": "calc(1.5em + 0.75rem + 2 * var(--bs-border-width))",
55
- "panneau-control-height-sm": "calc(1.5em + 0.5rem + 2 * var(--bs-border-width))"
56
- }
97
+ "panneau-control-height-sm": "calc(1.5em + 0.5rem + 2 * var(--bs-border-width))",
98
+ "panneau-control-padding-x": "0.75rem"
99
+ } }
57
100
  };
58
101
  //#endregion
59
- export { bootstrapTheme };
102
+ export { bootstrapTheme, bootstrapTone };
60
103
 
61
104
  //# sourceMappingURL=bootstrap.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.js","names":[],"sources":["../../src/theme/bootstrap.ts"],"sourcesContent":["import type { Theme } from './theme';\n\n/**\n * Maps Panneau's tokens to the CSS variables of Bootstrap 5.3, for sites that use Bootstrap: Panneau\n * then follows the site's colors, fonts and radii, and its color mode (`data-bs-theme`), without\n * using any Bootstrap class. Bootstrap's CSS must be loaded by the site.\n */\nexport const bootstrapTheme: Theme = {\n label: 'Bootstrap',\n tokens: {\n 'panneau-color-bg': 'var(--bs-body-bg)',\n 'panneau-color-surface': 'var(--bs-body-bg)',\n 'panneau-color-surface-sunken': 'var(--bs-tertiary-bg)',\n 'panneau-color-surface-raised': 'var(--bs-body-bg)',\n 'panneau-color-hover': 'rgba(var(--bs-emphasis-color-rgb), 0.06)',\n 'panneau-color-pressed': 'rgba(var(--bs-emphasis-color-rgb), 0.1)',\n 'panneau-color-text': 'var(--bs-body-color)',\n 'panneau-color-text-muted': 'var(--bs-secondary-color)',\n 'panneau-color-link': 'var(--bs-link-color)',\n 'panneau-color-border': 'var(--bs-border-color)',\n 'panneau-color-border-control': 'var(--bs-border-color)',\n 'panneau-color-accent': 'var(--bs-primary)',\n 'panneau-color-accent-hover': 'color-mix(in srgb, var(--bs-primary), #000 15%)',\n 'panneau-color-accent-text': 'var(--bs-white)',\n 'panneau-color-accent-subtle': 'var(--bs-primary-bg-subtle)',\n 'panneau-color-focus': 'rgba(var(--bs-primary-rgb), 0.5)',\n 'panneau-color-danger': 'var(--bs-danger-text-emphasis)',\n 'panneau-color-danger-subtle': 'var(--bs-danger-bg-subtle)',\n 'panneau-color-danger-border': 'var(--bs-danger-border-subtle)',\n 'panneau-color-danger-solid': 'var(--bs-danger)',\n 'panneau-color-danger-solid-hover': 'color-mix(in srgb, var(--bs-danger), #000 15%)',\n 'panneau-color-danger-solid-text': 'var(--bs-white)',\n 'panneau-color-success': 'var(--bs-success-text-emphasis)',\n 'panneau-color-success-subtle': 'var(--bs-success-bg-subtle)',\n 'panneau-color-success-border': 'var(--bs-success-border-subtle)',\n 'panneau-color-warning': 'var(--bs-warning-text-emphasis)',\n 'panneau-color-warning-subtle': 'var(--bs-warning-bg-subtle)',\n 'panneau-color-warning-border': 'var(--bs-warning-border-subtle)',\n 'panneau-color-info': 'var(--bs-info-text-emphasis)',\n 'panneau-color-info-subtle': 'var(--bs-info-bg-subtle)',\n 'panneau-color-info-border': 'var(--bs-info-border-subtle)',\n 'panneau-font-family': 'var(--bs-body-font-family)',\n 'panneau-font-family-mono': 'var(--bs-font-monospace)',\n 'panneau-font-size-md': 'var(--bs-body-font-size)',\n 'panneau-font-size-control': 'var(--bs-body-font-size)',\n 'panneau-line-height': 'var(--bs-body-line-height)',\n 'panneau-radius-sm': 'var(--bs-border-radius-sm)',\n 'panneau-radius-md': 'var(--bs-border-radius)',\n 'panneau-radius-lg': 'var(--bs-border-radius-lg)',\n 'panneau-radius-full': 'var(--bs-border-radius-pill)',\n 'panneau-border-width': 'var(--bs-border-width)',\n 'panneau-shadow-sm': 'var(--bs-box-shadow-sm)',\n 'panneau-shadow-md': 'var(--bs-box-shadow)',\n 'panneau-shadow-lg': 'var(--bs-box-shadow-lg)',\n 'panneau-control-height': 'calc(1.5em + 0.75rem + 2 * var(--bs-border-width))',\n 'panneau-control-height-sm': 'calc(1.5em + 0.5rem + 2 * var(--bs-border-width))',\n },\n};\n"],"mappings":";;;;;;AAOA,IAAa,iBAAwB;CACjC,OAAO;CACP,QAAQ;EACJ,oBAAoB;EACpB,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,uBAAuB;EACvB,yBAAyB;EACzB,sBAAsB;EACtB,4BAA4B;EAC5B,sBAAsB;EACtB,wBAAwB;EACxB,gCAAgC;EAChC,wBAAwB;EACxB,8BAA8B;EAC9B,6BAA6B;EAC7B,+BAA+B;EAC/B,uBAAuB;EACvB,wBAAwB;EACxB,+BAA+B;EAC/B,+BAA+B;EAC/B,8BAA8B;EAC9B,oCAAoC;EACpC,mCAAmC;EACnC,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,sBAAsB;EACtB,6BAA6B;EAC7B,6BAA6B;EAC7B,uBAAuB;EACvB,4BAA4B;EAC5B,wBAAwB;EACxB,6BAA6B;EAC7B,uBAAuB;EACvB,qBAAqB;EACrB,qBAAqB;EACrB,qBAAqB;EACrB,uBAAuB;EACvB,wBAAwB;EACxB,qBAAqB;EACrB,qBAAqB;EACrB,qBAAqB;EACrB,0BAA0B;EAC1B,6BAA6B;CACjC;AACJ"}
1
+ {"version":3,"file":"bootstrap.js","names":[],"sources":["../../src/theme/bootstrap.ts"],"sourcesContent":["import type { ThemeDefinition } from './defineTheme';\nimport type { Theme } from './theme';\nimport type { ToneValues } from './tones';\n\n/**\n * A tone from one of Bootstrap's theme colors, read from its CSS variables (`--bs-<color>`,\n * `-text-emphasis`, `-bg-subtle`, `-border-subtle`), so that a site's custom colors carry over:\n * `tones: { brand: bootstrapTone('brand') }`. Text on the subtle background uses the emphasis\n * color, as Bootstrap's alerts do. Light colors need dark solid text: `{ solidText: 'var(--bs-dark)' }`.\n */\nexport function bootstrapTone(\n color: string,\n { solidText = 'var(--bs-white)' }: { solidText?: string } = {},\n): ToneValues {\n return {\n text: `var(--bs-${color}-text-emphasis)`,\n subtle: `var(--bs-${color}-bg-subtle)`,\n border: `var(--bs-${color}-border-subtle)`,\n outline: `var(--bs-${color})`,\n solid: `var(--bs-${color})`,\n solidHover: `color-mix(in srgb, var(--bs-${color}), #000 15%)`,\n solidText,\n };\n}\n\n/**\n * Maps Panneau's tokens to the CSS variables of Bootstrap 5.3, for sites that use Bootstrap: Panneau\n * then follows the site's colors, fonts and radii, and its color mode (`data-bs-theme`), without\n * using any Bootstrap class. Bootstrap's CSS must be loaded by the site.\n */\nexport const bootstrapTheme: Theme = {\n label: 'Bootstrap',\n tokens: {\n 'panneau-color-bg': 'var(--bs-body-bg)',\n 'panneau-color-surface': 'var(--bs-body-bg)',\n 'panneau-color-surface-sunken': 'var(--bs-tertiary-bg)',\n 'panneau-color-surface-raised': 'var(--bs-body-bg)',\n 'panneau-color-hover': 'rgba(var(--bs-emphasis-color-rgb), 0.06)',\n 'panneau-color-pressed': 'rgba(var(--bs-emphasis-color-rgb), 0.1)',\n 'panneau-color-text': 'var(--bs-body-color)',\n 'panneau-color-text-muted': 'var(--bs-secondary-color)',\n 'panneau-color-link': 'var(--bs-link-color)',\n 'panneau-color-border': 'var(--bs-border-color)',\n 'panneau-color-border-control': 'var(--bs-border-color)',\n 'panneau-color-accent': 'var(--bs-primary)',\n 'panneau-color-accent-hover': 'color-mix(in srgb, var(--bs-primary), #000 15%)',\n 'panneau-color-accent-text': 'var(--bs-white)',\n 'panneau-color-accent-subtle': 'var(--bs-primary-bg-subtle)',\n 'panneau-color-focus': 'rgba(var(--bs-primary-rgb), 0.5)',\n 'panneau-color-danger': 'var(--bs-danger-text-emphasis)',\n 'panneau-color-danger-subtle': 'var(--bs-danger-bg-subtle)',\n 'panneau-color-danger-border': 'var(--bs-danger-border-subtle)',\n 'panneau-color-danger-solid': 'var(--bs-danger)',\n 'panneau-color-danger-solid-hover': 'color-mix(in srgb, var(--bs-danger), #000 15%)',\n 'panneau-color-danger-solid-text': 'var(--bs-white)',\n 'panneau-color-success': 'var(--bs-success-text-emphasis)',\n 'panneau-color-success-subtle': 'var(--bs-success-bg-subtle)',\n 'panneau-color-success-border': 'var(--bs-success-border-subtle)',\n 'panneau-color-success-solid': 'var(--bs-success)',\n 'panneau-color-success-solid-hover': 'color-mix(in srgb, var(--bs-success), #000 15%)',\n 'panneau-color-success-solid-text': 'var(--bs-white)',\n 'panneau-color-warning': 'var(--bs-warning-text-emphasis)',\n 'panneau-color-warning-subtle': 'var(--bs-warning-bg-subtle)',\n 'panneau-color-warning-border': 'var(--bs-warning-border-subtle)',\n 'panneau-color-warning-solid': 'var(--bs-warning)',\n 'panneau-color-warning-solid-hover': 'color-mix(in srgb, var(--bs-warning), #000 10%)',\n // Like Bootstrap's .btn-warning and .text-bg-warning: dark text on yellow.\n 'panneau-color-warning-solid-text': 'var(--bs-dark)',\n 'panneau-color-info': 'var(--bs-info-text-emphasis)',\n 'panneau-color-info-subtle': 'var(--bs-info-bg-subtle)',\n 'panneau-color-info-border': 'var(--bs-info-border-subtle)',\n 'panneau-color-info-solid': 'var(--bs-info)',\n 'panneau-color-info-solid-hover': 'color-mix(in srgb, var(--bs-info), #000 10%)',\n 'panneau-color-info-solid-text': 'var(--bs-dark)',\n 'panneau-font-family-body': 'var(--bs-body-font-family)',\n 'panneau-font-family-mono': 'var(--bs-font-monospace)',\n 'panneau-font-size-md': 'var(--bs-body-font-size)',\n 'panneau-line-height': 'var(--bs-body-line-height)',\n 'panneau-radius-sm': 'var(--bs-border-radius-sm)',\n 'panneau-radius-md': 'var(--bs-border-radius)',\n 'panneau-radius-lg': 'var(--bs-border-radius-lg)',\n 'panneau-radius-full': 'var(--bs-border-radius-pill)',\n 'panneau-border-width': 'var(--bs-border-width)',\n 'panneau-shadow-sm': 'var(--bs-box-shadow-sm)',\n 'panneau-shadow-md': 'var(--bs-box-shadow)',\n 'panneau-shadow-lg': 'var(--bs-box-shadow-lg)',\n // Bootstrap 5.3 declares its component variables (--bs-btn-*, --bs-card-*) on the classes\n // of its components, out of reach: components map to the root variables those default to.\n 'panneau-heading-color': 'var(--bs-heading-color)',\n 'panneau-badge-radius': 'var(--bs-border-radius)',\n 'panneau-card-radius': 'var(--bs-border-radius)',\n 'panneau-card-border-color': 'var(--bs-border-color-translucent)',\n 'panneau-popover-radius': 'var(--bs-border-radius)',\n 'panneau-popover-border-color': 'var(--bs-border-color-translucent)',\n 'panneau-tooltip-bg': 'var(--bs-emphasis-color)',\n 'panneau-tooltip-color': 'var(--bs-body-bg)',\n 'panneau-tooltip-radius': 'var(--bs-border-radius)',\n },\n // Bootstrap's theme colors as tones: accent is primary, and primary and secondary are there by\n // their Bootstrap names (declare them in PanneauTones to use them).\n tones: {\n accent: bootstrapTone('primary'),\n primary: bootstrapTone('primary'),\n secondary: bootstrapTone('secondary'),\n },\n // Bootstrap's sizes of controls are those of the comfortable density: the others keep Panneau's.\n densities: {\n comfortable: {\n 'panneau-font-size-control': 'var(--bs-body-font-size)',\n 'panneau-control-height': 'calc(1.5em + 0.75rem + 2 * var(--bs-border-width))',\n 'panneau-control-height-sm': 'calc(1.5em + 0.5rem + 2 * var(--bs-border-width))',\n 'panneau-control-padding-x': '0.75rem',\n },\n },\n} satisfies ThemeDefinition;\n"],"mappings":";;;;;;;AAUA,SAAgB,cACZ,OACA,EAAE,YAAY,sBAA8C,CAAC,GACnD;CACV,OAAO;EACH,MAAM,YAAY,MAAK;EACvB,QAAQ,YAAY,MAAK;EACzB,QAAQ,YAAY,MAAK;EACzB,SAAS,YAAY,MAAK;EAC1B,OAAO,YAAY,MAAK;EACxB,YAAY,+BAA+B,MAAK;EAChD;CACJ;AACJ;;;;;;AAOA,IAAa,iBAAwB;CACjC,OAAO;CACP,QAAQ;EACJ,oBAAoB;EACpB,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,uBAAuB;EACvB,yBAAyB;EACzB,sBAAsB;EACtB,4BAA4B;EAC5B,sBAAsB;EACtB,wBAAwB;EACxB,gCAAgC;EAChC,wBAAwB;EACxB,8BAA8B;EAC9B,6BAA6B;EAC7B,+BAA+B;EAC/B,uBAAuB;EACvB,wBAAwB;EACxB,+BAA+B;EAC/B,+BAA+B;EAC/B,8BAA8B;EAC9B,oCAAoC;EACpC,mCAAmC;EACnC,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,+BAA+B;EAC/B,qCAAqC;EACrC,oCAAoC;EACpC,yBAAyB;EACzB,gCAAgC;EAChC,gCAAgC;EAChC,+BAA+B;EAC/B,qCAAqC;EAErC,oCAAoC;EACpC,sBAAsB;EACtB,6BAA6B;EAC7B,6BAA6B;EAC7B,4BAA4B;EAC5B,kCAAkC;EAClC,iCAAiC;EACjC,4BAA4B;EAC5B,4BAA4B;EAC5B,wBAAwB;EACxB,uBAAuB;EACvB,qBAAqB;EACrB,qBAAqB;EACrB,qBAAqB;EACrB,uBAAuB;EACvB,wBAAwB;EACxB,qBAAqB;EACrB,qBAAqB;EACrB,qBAAqB;EAGrB,yBAAyB;EACzB,wBAAwB;EACxB,uBAAuB;EACvB,6BAA6B;EAC7B,0BAA0B;EAC1B,gCAAgC;EAChC,sBAAsB;EACtB,yBAAyB;EACzB,0BAA0B;CAC9B;CAGA,OAAO;EACH,QAAQ,cAAc,SAAS;EAC/B,SAAS,cAAc,SAAS;EAChC,WAAW,cAAc,WAAW;CACxC;CAEA,WAAW,EACP,aAAa;EACT,6BAA6B;EAC7B,0BAA0B;EAC1B,6BAA6B;EAC7B,6BAA6B;CACjC,EACJ;AACJ"}
@@ -0,0 +1,23 @@
1
+ /** The number of categorical chart colors: series beyond it reuse them in order. */
2
+ export declare const CHART_CATEGORICAL_COUNT = 8;
3
+ /** The number of steps of the sequential chart ramp. */
4
+ export declare const CHART_SEQUENTIAL_COUNT = 5;
5
+ /**
6
+ * The colors of charts, as CSS variables for `fill` and `stroke`: they follow the theme, the color
7
+ * scheme and the scope of the chart. Dashboard charts use only these.
8
+ *
9
+ * - `categorical(index)`: the color of a series, in a fixed order (`index` from 0), repeating after
10
+ * 8. Each one reaches 3:1 against the surface and stays distinct with color vision deficiencies.
11
+ * - `sequential(step)`: a step from 1 (low) to 5 (high) of a ramp, for quantities.
12
+ * - `diverging`: two colors around a neutral midpoint, for values above and below a reference.
13
+ */
14
+ export declare const chartColors: Readonly<{
15
+ categorical: (index: number) => string;
16
+ sequential: (step: 1 | 2 | 3 | 4 | 5) => string;
17
+ diverging: Readonly<{
18
+ negative: "var(--panneau-chart-diverging-negative)";
19
+ neutral: "var(--panneau-chart-diverging-neutral)";
20
+ positive: "var(--panneau-chart-diverging-positive)";
21
+ }>;
22
+ }>;
23
+ //# sourceMappingURL=chartColors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chartColors.d.ts","sourceRoot":"","sources":["../../src/theme/chartColors.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;yBACC,MAAM,KAAG,MAAM;uBAEjB,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,KAAG,MAAM;;;;;;EAM/C,CAAC"}
@@ -0,0 +1,27 @@
1
+ //#region src/theme/chartColors.ts
2
+ /** The number of categorical chart colors: series beyond it reuse them in order. */
3
+ var CHART_CATEGORICAL_COUNT = 8;
4
+ /** The number of steps of the sequential chart ramp. */
5
+ var CHART_SEQUENTIAL_COUNT = 5;
6
+ /**
7
+ * The colors of charts, as CSS variables for `fill` and `stroke`: they follow the theme, the color
8
+ * scheme and the scope of the chart. Dashboard charts use only these.
9
+ *
10
+ * - `categorical(index)`: the color of a series, in a fixed order (`index` from 0), repeating after
11
+ * 8. Each one reaches 3:1 against the surface and stays distinct with color vision deficiencies.
12
+ * - `sequential(step)`: a step from 1 (low) to 5 (high) of a ramp, for quantities.
13
+ * - `diverging`: two colors around a neutral midpoint, for values above and below a reference.
14
+ */
15
+ var chartColors = Object.freeze({
16
+ categorical: (index) => `var(--panneau-chart-categorical-${(Math.floor(index) % 8 + 8) % 8 + 1})`,
17
+ sequential: (step) => `var(--panneau-chart-sequential-${step})`,
18
+ diverging: Object.freeze({
19
+ negative: "var(--panneau-chart-diverging-negative)",
20
+ neutral: "var(--panneau-chart-diverging-neutral)",
21
+ positive: "var(--panneau-chart-diverging-positive)"
22
+ })
23
+ });
24
+ //#endregion
25
+ export { CHART_CATEGORICAL_COUNT, CHART_SEQUENTIAL_COUNT, chartColors };
26
+
27
+ //# sourceMappingURL=chartColors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chartColors.js","names":[],"sources":["../../src/theme/chartColors.ts"],"sourcesContent":["/** The number of categorical chart colors: series beyond it reuse them in order. */\nexport const CHART_CATEGORICAL_COUNT = 8;\n\n/** The number of steps of the sequential chart ramp. */\nexport const CHART_SEQUENTIAL_COUNT = 5;\n\n/**\n * The colors of charts, as CSS variables for `fill` and `stroke`: they follow the theme, the color\n * scheme and the scope of the chart. Dashboard charts use only these.\n *\n * - `categorical(index)`: the color of a series, in a fixed order (`index` from 0), repeating after\n * 8. Each one reaches 3:1 against the surface and stays distinct with color vision deficiencies.\n * - `sequential(step)`: a step from 1 (low) to 5 (high) of a ramp, for quantities.\n * - `diverging`: two colors around a neutral midpoint, for values above and below a reference.\n */\nexport const chartColors = Object.freeze({\n categorical: (index: number): string =>\n `var(--panneau-chart-categorical-${(((Math.floor(index) % CHART_CATEGORICAL_COUNT) + CHART_CATEGORICAL_COUNT) % CHART_CATEGORICAL_COUNT) + 1})`,\n sequential: (step: 1 | 2 | 3 | 4 | 5): string => `var(--panneau-chart-sequential-${step})`,\n diverging: Object.freeze({\n negative: 'var(--panneau-chart-diverging-negative)',\n neutral: 'var(--panneau-chart-diverging-neutral)',\n positive: 'var(--panneau-chart-diverging-positive)',\n }),\n});\n"],"mappings":";;AACA,IAAa,0BAA0B;;AAGvC,IAAa,yBAAyB;;;;;;;;;;AAWtC,IAAa,cAAc,OAAO,OAAO;CACrC,cAAc,UACV,oCAAsC,KAAK,MAAM,KAAK,IAAA,IAAA,KAAA,IAAqF,EAAC;CAChJ,aAAa,SAAoC,kCAAkC,KAAI;CACvF,WAAW,OAAO,OAAO;EACrB,UAAU;EACV,SAAS;EACT,UAAU;CACd,CAAC;AACL,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { type ColorDifferenceCheck } from './colorVision';
2
+ import { type Theme } from './theme';
3
+ /** A pair of colors that a theme must keep readable. */
4
+ export interface ThemeContrastCheck {
5
+ scheme: 'light' | 'dark';
6
+ /** What is checked: `tone signal: text on surface`, `tokens: text on bg`. */
7
+ name: string;
8
+ foreground: string;
9
+ background: string;
10
+ /** The contrast ratio, or `null` when a color can't be read (a CSS variable, `color-mix()`). */
11
+ ratio: number | null;
12
+ minimum: number;
13
+ }
14
+ export interface ThemeReport {
15
+ /** Every pair checked. */
16
+ checks: ThemeContrastCheck[];
17
+ /** Pairs below their minimum ratio. */
18
+ failures: ThemeContrastCheck[];
19
+ /** Pairs with a color Panneau can't read: check them in a browser. */
20
+ unchecked: ThemeContrastCheck[];
21
+ /**
22
+ * Categorical chart colors of the theme that look too alike, for typical vision or with a
23
+ * color vision deficiency.
24
+ */
25
+ colorDifferences: (ColorDifferenceCheck & {
26
+ scheme: 'light' | 'dark';
27
+ })[];
28
+ }
29
+ /**
30
+ * Checks the contrast of a theme, like Bootstrap's `$min-contrast-ratio`, but as a report: text
31
+ * tokens against backgrounds (4.5:1), control borders (3:1), and every tone of the theme (its text
32
+ * on the surface, its solid text on the solid background and its hover), and chart colors (3:1
33
+ * against the surface, and categorical colors distinct with color vision deficiencies). Pairs where a color comes
34
+ * from the default theme and is not set by the theme are skipped, except the surface, which falls
35
+ * back to the default one. Values that refer to the theme's palette or tokens with `var()` are
36
+ * checked as what they refer to; colors Panneau can't read are listed as unchecked.
37
+ */
38
+ export declare function checkTheme(theme: Theme): ThemeReport;
39
+ //# sourceMappingURL=checkTheme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkTheme.d.ts","sourceRoot":"","sources":["../../src/theme/checkTheme.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,oBAAoB,EAAyB,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,KAAK,KAAK,EAAqD,MAAM,SAAS,CAAC;AAGxF,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,0BAA0B;IAC1B,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,sEAAsE;IACtE,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC;;;OAGG;IACH,gBAAgB,EAAE,CAAC,oBAAoB,GAAG;QAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CAC7E;AA2CD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAgHpD"}
@@ -0,0 +1,132 @@
1
+ import { WHITE, contrastRatio, mixColors, parseColor } from "./color.js";
2
+ import { MIN_CONTRAST_RATIO, SUBTLE_AMOUNTS, resolveTone } from "./tones.js";
3
+ import { paletteVariables, resolveReference, themeSurfaces } from "./theme.js";
4
+ import { checkColorDifferences } from "./colorVision.js";
5
+ //#region src/theme/checkTheme.ts
6
+ /** Surfaces of the default theme, used when a theme doesn't set its own. */
7
+ var DEFAULT_SURFACES = {
8
+ light: WHITE,
9
+ dark: {
10
+ r: 24,
11
+ g: 24,
12
+ b: 27
13
+ }
14
+ };
15
+ /** Chart colors that must stand out from the surface: series, the high step and diverging ends. */
16
+ var CHART_TOKENS = [
17
+ ...Array.from({ length: 8 }, (_, index) => `panneau-chart-categorical-${index + 1}`),
18
+ "panneau-chart-sequential-5",
19
+ "panneau-chart-diverging-negative",
20
+ "panneau-chart-diverging-positive"
21
+ ];
22
+ /** Non-text elements (borders of controls, outlines) need 3:1 (WCAG 2 non-text contrast). */
23
+ var MIN_NON_TEXT_RATIO = 3;
24
+ var TOKEN_PAIRS = [
25
+ [
26
+ "text on bg",
27
+ "panneau-color-text",
28
+ "panneau-color-bg",
29
+ MIN_CONTRAST_RATIO
30
+ ],
31
+ [
32
+ "text on surface",
33
+ "panneau-color-text",
34
+ "panneau-color-surface",
35
+ MIN_CONTRAST_RATIO
36
+ ],
37
+ [
38
+ "muted text on surface",
39
+ "panneau-color-text-muted",
40
+ "panneau-color-surface",
41
+ MIN_CONTRAST_RATIO
42
+ ],
43
+ [
44
+ "link on surface",
45
+ "panneau-color-link",
46
+ "panneau-color-surface",
47
+ MIN_CONTRAST_RATIO
48
+ ],
49
+ [
50
+ "accent text on accent",
51
+ "panneau-color-accent-text",
52
+ "panneau-color-accent",
53
+ MIN_CONTRAST_RATIO
54
+ ],
55
+ [
56
+ "control border on surface",
57
+ "panneau-color-border-control",
58
+ "panneau-color-surface",
59
+ MIN_NON_TEXT_RATIO
60
+ ]
61
+ ];
62
+ /**
63
+ * Checks the contrast of a theme, like Bootstrap's `$min-contrast-ratio`, but as a report: text
64
+ * tokens against backgrounds (4.5:1), control borders (3:1), and every tone of the theme (its text
65
+ * on the surface, its solid text on the solid background and its hover), and chart colors (3:1
66
+ * against the surface, and categorical colors distinct with color vision deficiencies). Pairs where a color comes
67
+ * from the default theme and is not set by the theme are skipped, except the surface, which falls
68
+ * back to the default one. Values that refer to the theme's palette or tokens with `var()` are
69
+ * checked as what they refer to; colors Panneau can't read are listed as unchecked.
70
+ */
71
+ function checkTheme(theme) {
72
+ const checks = [];
73
+ const colorDifferences = [];
74
+ const palette = paletteVariables(theme.palette);
75
+ const surfaces = themeSurfaces(theme);
76
+ ["light", "dark"].forEach((scheme) => {
77
+ const variables = {
78
+ ...palette,
79
+ ...theme.tokens,
80
+ ...scheme === "dark" ? theme.dark : void 0
81
+ };
82
+ const tokens = Object.fromEntries(Object.entries(variables).map(([name, value]) => [name, resolveReference(value, variables)]));
83
+ const surface = tokens["panneau-color-surface"];
84
+ const surfaceColor = surface !== void 0 ? parseColor(surface) : DEFAULT_SURFACES[scheme];
85
+ const add = (name, foreground, background, minimum, backgroundColor = parseColor(background)) => {
86
+ const foregroundColor = parseColor(foreground);
87
+ checks.push({
88
+ scheme,
89
+ name,
90
+ foreground,
91
+ background,
92
+ ratio: foregroundColor !== null && backgroundColor !== null ? Math.round(contrastRatio(foregroundColor, backgroundColor) * 100) / 100 : null,
93
+ minimum
94
+ });
95
+ };
96
+ TOKEN_PAIRS.forEach(([name, foreground, background, minimum]) => {
97
+ const foregroundValue = tokens[foreground];
98
+ const backgroundValue = tokens[background];
99
+ if (foregroundValue !== void 0 && backgroundValue !== void 0) add(`tokens: ${name}`, foregroundValue, backgroundValue, minimum);
100
+ });
101
+ const surfaceValue = surface ?? (scheme === "light" ? "#ffffff" : "#18181b");
102
+ CHART_TOKENS.forEach((token) => {
103
+ const value = tokens[token];
104
+ if (value !== void 0) add(`chart: ${token.replace("panneau-chart-", "")} on surface`, value, surfaceValue, MIN_NON_TEXT_RATIO, surfaceColor);
105
+ });
106
+ const categorical = CHART_TOKENS.filter((token) => token.includes("categorical")).flatMap((token) => tokens[token] !== void 0 ? [tokens[token]] : []);
107
+ colorDifferences.push(...checkColorDifferences(categorical).map((check) => ({
108
+ ...check,
109
+ scheme
110
+ })));
111
+ Object.entries(theme.tones ?? {}).forEach(([tone, toneInput]) => {
112
+ const input = typeof toneInput === "string" ? resolveReference(toneInput, palette) : toneInput;
113
+ const values = resolveTone(input, surfaces)[scheme];
114
+ add(`tone ${tone}: text on surface`, values.text, surface ?? (scheme === "light" ? "#ffffff" : "#18181b"), MIN_CONTRAST_RATIO, surfaceColor);
115
+ const color = typeof input === "string" ? parseColor(input) : null;
116
+ const subtleColor = parseColor(values.subtle) ?? (color !== null && surfaceColor !== null ? mixColors(surfaceColor, color, SUBTLE_AMOUNTS[scheme]) : null);
117
+ add(`tone ${tone}: text on subtle`, values.text, values.subtle, MIN_CONTRAST_RATIO, subtleColor);
118
+ add(`tone ${tone}: solid text on solid`, values.solidText, values.solid, MIN_CONTRAST_RATIO);
119
+ add(`tone ${tone}: solid text on hover`, values.solidText, values.solidHover, MIN_CONTRAST_RATIO);
120
+ });
121
+ });
122
+ return {
123
+ checks,
124
+ failures: checks.filter((check) => check.ratio !== null && check.ratio < check.minimum),
125
+ unchecked: checks.filter((check) => check.ratio === null),
126
+ colorDifferences
127
+ };
128
+ }
129
+ //#endregion
130
+ export { checkTheme };
131
+
132
+ //# sourceMappingURL=checkTheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkTheme.js","names":[],"sources":["../../src/theme/checkTheme.ts"],"sourcesContent":["import { type Rgb, WHITE, contrastRatio, mixColors, parseColor } from './color';\nimport { type ColorDifferenceCheck, checkColorDifferences } from './colorVision';\nimport { type Theme, paletteVariables, resolveReference, themeSurfaces } from './theme';\nimport { MIN_CONTRAST_RATIO, SUBTLE_AMOUNTS, resolveTone } from './tones';\n\n/** A pair of colors that a theme must keep readable. */\nexport interface ThemeContrastCheck {\n scheme: 'light' | 'dark';\n /** What is checked: `tone signal: text on surface`, `tokens: text on bg`. */\n name: string;\n foreground: string;\n background: string;\n /** The contrast ratio, or `null` when a color can't be read (a CSS variable, `color-mix()`). */\n ratio: number | null;\n minimum: number;\n}\n\nexport interface ThemeReport {\n /** Every pair checked. */\n checks: ThemeContrastCheck[];\n /** Pairs below their minimum ratio. */\n failures: ThemeContrastCheck[];\n /** Pairs with a color Panneau can't read: check them in a browser. */\n unchecked: ThemeContrastCheck[];\n /**\n * Categorical chart colors of the theme that look too alike, for typical vision or with a\n * color vision deficiency.\n */\n colorDifferences: (ColorDifferenceCheck & { scheme: 'light' | 'dark' })[];\n}\n\n/** Surfaces of the default theme, used when a theme doesn't set its own. */\nconst DEFAULT_SURFACES: Record<'light' | 'dark', Rgb> = {\n light: WHITE,\n dark: { r: 24, g: 24, b: 27 },\n};\n\n/** Chart colors that must stand out from the surface: series, the high step and diverging ends. */\nconst CHART_TOKENS = [\n ...Array.from({ length: 8 }, (_, index) => `panneau-chart-categorical-${index + 1}`),\n 'panneau-chart-sequential-5',\n 'panneau-chart-diverging-negative',\n 'panneau-chart-diverging-positive',\n];\n\n/** Non-text elements (borders of controls, outlines) need 3:1 (WCAG 2 non-text contrast). */\nconst MIN_NON_TEXT_RATIO = 3;\n\nconst TOKEN_PAIRS: [name: string, foreground: string, background: string, minimum: number][] = [\n ['text on bg', 'panneau-color-text', 'panneau-color-bg', MIN_CONTRAST_RATIO],\n ['text on surface', 'panneau-color-text', 'panneau-color-surface', MIN_CONTRAST_RATIO],\n [\n 'muted text on surface',\n 'panneau-color-text-muted',\n 'panneau-color-surface',\n MIN_CONTRAST_RATIO,\n ],\n ['link on surface', 'panneau-color-link', 'panneau-color-surface', MIN_CONTRAST_RATIO],\n [\n 'accent text on accent',\n 'panneau-color-accent-text',\n 'panneau-color-accent',\n MIN_CONTRAST_RATIO,\n ],\n [\n 'control border on surface',\n 'panneau-color-border-control',\n 'panneau-color-surface',\n MIN_NON_TEXT_RATIO,\n ],\n];\n\n/**\n * Checks the contrast of a theme, like Bootstrap's `$min-contrast-ratio`, but as a report: text\n * tokens against backgrounds (4.5:1), control borders (3:1), and every tone of the theme (its text\n * on the surface, its solid text on the solid background and its hover), and chart colors (3:1\n * against the surface, and categorical colors distinct with color vision deficiencies). Pairs where a color comes\n * from the default theme and is not set by the theme are skipped, except the surface, which falls\n * back to the default one. Values that refer to the theme's palette or tokens with `var()` are\n * checked as what they refer to; colors Panneau can't read are listed as unchecked.\n */\nexport function checkTheme(theme: Theme): ThemeReport {\n const checks: ThemeContrastCheck[] = [];\n const colorDifferences: ThemeReport['colorDifferences'] = [];\n const palette = paletteVariables(theme.palette);\n const surfaces = themeSurfaces(theme);\n (['light', 'dark'] as const).forEach((scheme) => {\n const variables = {\n ...palette,\n ...theme.tokens,\n ...(scheme === 'dark' ? theme.dark : undefined),\n };\n const tokens = Object.fromEntries(\n Object.entries(variables).map(([name, value]) => [\n name,\n resolveReference(value, variables),\n ]),\n );\n const surface = tokens['panneau-color-surface'];\n const surfaceColor = surface !== undefined ? parseColor(surface) : DEFAULT_SURFACES[scheme];\n const add = (\n name: string,\n foreground: string,\n background: string,\n minimum: number,\n backgroundColor: Rgb | null = parseColor(background),\n ) => {\n const foregroundColor = parseColor(foreground);\n checks.push({\n scheme,\n name,\n foreground,\n background,\n ratio:\n foregroundColor !== null && backgroundColor !== null\n ? Math.round(contrastRatio(foregroundColor, backgroundColor) * 100) / 100\n : null,\n minimum,\n });\n };\n TOKEN_PAIRS.forEach(([name, foreground, background, minimum]) => {\n const foregroundValue = tokens[foreground];\n const backgroundValue = tokens[background];\n if (foregroundValue !== undefined && backgroundValue !== undefined) {\n add(`tokens: ${name}`, foregroundValue, backgroundValue, minimum);\n }\n });\n // Chart colors are graphics: 3:1 against the surface, and distinct from each other.\n const surfaceValue = surface ?? (scheme === 'light' ? '#ffffff' : '#18181b');\n CHART_TOKENS.forEach((token) => {\n const value = tokens[token];\n if (value !== undefined) {\n add(\n `chart: ${token.replace('panneau-chart-', '')} on surface`,\n value,\n surfaceValue,\n MIN_NON_TEXT_RATIO,\n surfaceColor,\n );\n }\n });\n const categorical = CHART_TOKENS.filter((token) => token.includes('categorical')).flatMap(\n (token) => (tokens[token] !== undefined ? [tokens[token]] : []),\n );\n colorDifferences.push(\n ...checkColorDifferences(categorical).map((check) => ({ ...check, scheme })),\n );\n Object.entries(theme.tones ?? {}).forEach(([tone, toneInput]) => {\n const input =\n typeof toneInput === 'string' ? resolveReference(toneInput, palette) : toneInput;\n const values = resolveTone(input, surfaces)[scheme];\n add(\n `tone ${tone}: text on surface`,\n values.text,\n surface ?? (scheme === 'light' ? '#ffffff' : '#18181b'),\n MIN_CONTRAST_RATIO,\n surfaceColor,\n );\n // The subtle background of a tone is often mixed with the surface in CSS: compute it\n // against the surface of the scheme when it can't be read directly.\n const color = typeof input === 'string' ? parseColor(input) : null;\n const subtleColor =\n parseColor(values.subtle) ??\n (color !== null && surfaceColor !== null\n ? mixColors(surfaceColor, color, SUBTLE_AMOUNTS[scheme])\n : null);\n add(\n `tone ${tone}: text on subtle`,\n values.text,\n values.subtle,\n MIN_CONTRAST_RATIO,\n subtleColor,\n );\n add(\n `tone ${tone}: solid text on solid`,\n values.solidText,\n values.solid,\n MIN_CONTRAST_RATIO,\n );\n add(\n `tone ${tone}: solid text on hover`,\n values.solidText,\n values.solidHover,\n MIN_CONTRAST_RATIO,\n );\n });\n });\n return {\n checks,\n failures: checks.filter((check) => check.ratio !== null && check.ratio < check.minimum),\n unchecked: checks.filter((check) => check.ratio === null),\n colorDifferences,\n };\n}\n"],"mappings":";;;;;;AAgCA,IAAM,mBAAkD;CACpD,OAAO;CACP,MAAM;EAAE,GAAG;EAAI,GAAG;EAAI,GAAG;CAAG;AAChC;;AAGA,IAAM,eAAe;CACjB,GAAG,MAAM,KAAK,EAAE,QAAQ,EAAE,IAAI,GAAG,UAAU,6BAA6B,QAAQ,GAAG;CACnF;CACA;CACA;AAAkC;;AAItC,IAAM,qBAAqB;AAE3B,IAAM,cAAyF;CAC3F;EAAC;EAAc;EAAsB;EAAoB;CAAkB;CAC3E;EAAC;EAAmB;EAAsB;EAAyB;CAAkB;CACrF;EACI;EACA;EACA;EACA;CAAkB;CAEtB;EAAC;EAAmB;EAAsB;EAAyB;CAAkB;CACrF;EACI;EACA;EACA;EACA;CAAkB;CAEtB;EACI;EACA;EACA;EACA;CAAkB;AACrB;;;;;;;;;;AAYL,SAAgB,WAAW,OAA2B;CAClD,MAAM,SAA+B,CAAA;CACrC,MAAM,mBAAoD,CAAA;CAC1D,MAAM,UAAU,iBAAiB,MAAM,OAAO;CAC9C,MAAM,WAAW,cAAc,KAAK;CACpC,CAAE,SAAS,MAAM,CAAC,CAAW,SAAS,WAAW;EAC7C,MAAM,YAAY;GACd,GAAG;GACH,GAAG,MAAM;GACT,GAAI,WAAW,SAAS,MAAM,OAAO,KAAA;EACzC;EACA,MAAM,SAAS,OAAO,YAClB,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAC7C,MACA,iBAAiB,OAAO,SAAS,CAAC,CACrC,CACL;EACA,MAAM,UAAU,OAAO;EACvB,MAAM,eAAe,YAAY,KAAA,IAAY,WAAW,OAAO,IAAI,iBAAiB;EACpF,MAAM,OACF,MACA,YACA,YACA,SACA,kBAA8B,WAAW,UAAU,MAClD;GACD,MAAM,kBAAkB,WAAW,UAAU;GAC7C,OAAO,KAAK;IACR;IACA;IACA;IACA;IACA,OACI,oBAAoB,QAAQ,oBAAoB,OAC1C,KAAK,MAAM,cAAc,iBAAiB,eAAe,IAAI,GAAG,IAAI,MACpE;IACV;GACJ,CAAC;EACL;EACA,YAAY,SAAS,CAAC,MAAM,YAAY,YAAY,aAAa;GAC7D,MAAM,kBAAkB,OAAO;GAC/B,MAAM,kBAAkB,OAAO;GAC/B,IAAI,oBAAoB,KAAA,KAAa,oBAAoB,KAAA,GACrD,IAAI,WAAW,QAAQ,iBAAiB,iBAAiB,OAAO;EAExE,CAAC;EAED,MAAM,eAAe,YAAY,WAAW,UAAU,YAAY;EAClE,aAAa,SAAS,UAAU;GAC5B,MAAM,QAAQ,OAAO;GACrB,IAAI,UAAU,KAAA,GACV,IACI,UAAU,MAAM,QAAQ,kBAAkB,EAAE,EAAC,cAC7C,OACA,cACA,oBACA,YACJ;EAER,CAAC;EACD,MAAM,cAAc,aAAa,QAAQ,UAAU,MAAM,SAAS,aAAa,CAAC,CAAC,CAAC,SAC7E,UAAW,OAAO,WAAW,KAAA,IAAY,CAAC,OAAO,MAAM,IAAI,CAAA,CAChE;EACA,iBAAiB,KACb,GAAG,sBAAsB,WAAW,CAAC,CAAC,KAAK,WAAW;GAAE,GAAG;GAAO;EAAO,EAAE,CAC/E;EACA,OAAO,QAAQ,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,eAAe;GAC7D,MAAM,QACF,OAAO,cAAc,WAAW,iBAAiB,WAAW,OAAO,IAAI;GAC3E,MAAM,SAAS,YAAY,OAAO,QAAQ,CAAC,CAAC;GAC5C,IACI,QAAQ,KAAI,oBACZ,OAAO,MACP,YAAY,WAAW,UAAU,YAAY,YAC7C,oBACA,YACJ;GAGA,MAAM,QAAQ,OAAO,UAAU,WAAW,WAAW,KAAK,IAAI;GAC9D,MAAM,cACF,WAAW,OAAO,MAAM,MACvB,UAAU,QAAQ,iBAAiB,OAC9B,UAAU,cAAc,OAAO,eAAe,OAAO,IACrD;GACV,IACI,QAAQ,KAAI,mBACZ,OAAO,MACP,OAAO,QACP,oBACA,WACJ;GACA,IACI,QAAQ,KAAI,wBACZ,OAAO,WACP,OAAO,OACP,kBACJ;GACA,IACI,QAAQ,KAAI,wBACZ,OAAO,WACP,OAAO,YACP,kBACJ;EACJ,CAAC;CACL,CAAC;CACD,OAAO;EACH;EACA,UAAU,OAAO,QAAQ,UAAU,MAAM,UAAU,QAAQ,MAAM,QAAQ,MAAM,OAAO;EACtF,WAAW,OAAO,QAAQ,UAAU,MAAM,UAAU,IAAI;EACxD;CACJ;AACJ"}
@@ -0,0 +1,14 @@
1
+ import { type ThemeReport } from './checkTheme';
2
+ import type { Theme } from './theme';
3
+ /** Loads a module from its URL: `import()`, replaced in tests. */
4
+ export type ModuleImporter = (url: string) => Promise<Record<string, unknown>>;
5
+ /** Reads a theme from a JSON file or from an export of a module. */
6
+ export declare function loadTheme(path: string, exportName?: string, load?: ModuleImporter): Promise<Theme>;
7
+ /** A report as text, one line per check that failed or that Panneau couldn't read. */
8
+ export declare function formatThemeReport(report: ThemeReport, label?: string): string;
9
+ /** Runs `panneau-check-theme` with its arguments, and returns the exit code. */
10
+ export declare function runCheckTheme(args: readonly string[], output?: {
11
+ log: (text: string) => void;
12
+ error: (text: string) => void;
13
+ }, load?: ModuleImporter): Promise<number>;
14
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/theme/cli.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAkBrC,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAI/E,oEAAoE;AACpE,wBAAsB,SAAS,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,SAAY,EACtB,IAAI,GAAE,cAA6B,GACpC,OAAO,CAAC,KAAK,CAAC,CAchB;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,SAAU,GAAG,MAAM,CAwB9E;AAED,gFAAgF;AAChF,wBAAsB,aAAa,CAC/B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,GAAE;IAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAY,EAChF,IAAI,GAAE,cAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
@@ -0,0 +1,61 @@
1
+ import { checkTheme } from "./checkTheme.js";
2
+ import { readFile } from "node:fs/promises";
3
+ import { extname, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ //#region src/theme/cli.ts
6
+ var USAGE = `Usage: panneau-check-theme <file> [--export <name>] [--json]
7
+
8
+ Checks the contrast of a theme, and the differences between its chart colors with color vision
9
+ deficiencies. <file> is a JSON file (the Theme schema of @panneau/protocol), or a module whose
10
+ default export, or the export named by --export, is a theme. Exits with 1 when a check fails.`;
11
+ function isTheme(value) {
12
+ return typeof value === "object" && value !== null && "tokens" in value && typeof value.tokens === "object" && value.tokens !== null;
13
+ }
14
+ var importModule = (url) => import(url);
15
+ /** Reads a theme from a JSON file or from an export of a module. */
16
+ async function loadTheme(path, exportName = "default", load = importModule) {
17
+ const file = resolve(path);
18
+ const value = extname(file) === ".json" ? JSON.parse(await readFile(file, "utf8")) : (await load(pathToFileURL(file).href))[exportName];
19
+ if (!isTheme(value)) throw new Error(extname(file) === ".json" ? `${path} is not a theme: it has no tokens.` : `The export "${exportName}" of ${path} is not a theme.`);
20
+ return value;
21
+ }
22
+ /** A report as text, one line per check that failed or that Panneau couldn't read. */
23
+ function formatThemeReport(report, label = "Theme") {
24
+ const lines = [`${label}: ${report.checks.length} contrast checks, ${report.failures.length} failed, ${report.unchecked.length} unchecked, ${report.colorDifferences.length} similar chart colors.`];
25
+ report.failures.forEach((check) => {
26
+ lines.push(` fail ${check.scheme.padEnd(5)} ${check.name}: ${check.foreground} on ${check.background}, ${String(check.ratio)}:1 < ${String(check.minimum)}:1`);
27
+ });
28
+ report.colorDifferences.forEach((check) => {
29
+ lines.push(` similar ${check.scheme.padEnd(5)} chart colors ${check.first} and ${check.second} with ${check.vision}: ${String(check.difference)} < ${String(check.minimum)}`);
30
+ });
31
+ report.unchecked.forEach((check) => {
32
+ lines.push(` unchecked ${check.scheme.padEnd(5)} ${check.name}: ${check.foreground} on ${check.background} (check it in a browser)`);
33
+ });
34
+ return lines.join("\n");
35
+ }
36
+ /** Runs `panneau-check-theme` with its arguments, and returns the exit code. */
37
+ async function runCheckTheme(args, output = console, load = importModule) {
38
+ const json = args.includes("--json");
39
+ const exportIndex = args.indexOf("--export");
40
+ const exportName = exportIndex >= 0 ? args[exportIndex + 1] : void 0;
41
+ const files = args.filter((arg, index) => !arg.startsWith("--") && (exportIndex < 0 || index !== exportIndex + 1));
42
+ const [file] = files;
43
+ if (file === void 0 || files.length > 1 || args.includes("--help")) {
44
+ output.error(USAGE);
45
+ return args.includes("--help") ? 0 : 2;
46
+ }
47
+ let theme;
48
+ try {
49
+ theme = await loadTheme(file, exportName, load);
50
+ } catch (error) {
51
+ output.error(error instanceof Error ? error.message : String(error));
52
+ return 2;
53
+ }
54
+ const report = checkTheme(theme);
55
+ output.log(json ? JSON.stringify(report, null, 2) : formatThemeReport(report, theme.label));
56
+ return report.failures.length > 0 || report.colorDifferences.length > 0 ? 1 : 0;
57
+ }
58
+ //#endregion
59
+ export { formatThemeReport, loadTheme, runCheckTheme };
60
+
61
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../../src/theme/cli.ts"],"sourcesContent":["// The `panneau-check-theme` command, for Node only: it is not part of the exports.\n/// <reference types=\"node\" />\nimport { readFile } from 'node:fs/promises';\nimport { extname, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { type ThemeReport, checkTheme } from './checkTheme';\nimport type { Theme } from './theme';\n\nconst USAGE = `Usage: panneau-check-theme <file> [--export <name>] [--json]\n\nChecks the contrast of a theme, and the differences between its chart colors with color vision\ndeficiencies. <file> is a JSON file (the Theme schema of @panneau/protocol), or a module whose\ndefault export, or the export named by --export, is a theme. Exits with 1 when a check fails.`;\n\nfunction isTheme(value: unknown): value is Theme {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'tokens' in value &&\n typeof value.tokens === 'object' &&\n value.tokens !== null\n );\n}\n\n/** Loads a module from its URL: `import()`, replaced in tests. */\nexport type ModuleImporter = (url: string) => Promise<Record<string, unknown>>;\n\nconst importModule: ModuleImporter = (url) => import(url) as Promise<Record<string, unknown>>;\n\n/** Reads a theme from a JSON file or from an export of a module. */\nexport async function loadTheme(\n path: string,\n exportName = 'default',\n load: ModuleImporter = importModule,\n): Promise<Theme> {\n const file = resolve(path);\n const value: unknown =\n extname(file) === '.json'\n ? JSON.parse(await readFile(file, 'utf8'))\n : (await load(pathToFileURL(file).href))[exportName];\n if (!isTheme(value)) {\n throw new Error(\n extname(file) === '.json'\n ? `${path} is not a theme: it has no tokens.`\n : `The export \"${exportName}\" of ${path} is not a theme.`,\n );\n }\n return value;\n}\n\n/** A report as text, one line per check that failed or that Panneau couldn't read. */\nexport function formatThemeReport(report: ThemeReport, label = 'Theme'): string {\n const lines = [\n `${label}: ${report.checks.length} contrast checks, ${report.failures.length} failed, ` +\n `${report.unchecked.length} unchecked, ${report.colorDifferences.length} similar chart colors.`,\n ];\n report.failures.forEach((check) => {\n lines.push(\n ` fail ${check.scheme.padEnd(5)} ${check.name}: ${check.foreground} on ` +\n `${check.background}, ${String(check.ratio)}:1 < ${String(check.minimum)}:1`,\n );\n });\n report.colorDifferences.forEach((check) => {\n lines.push(\n ` similar ${check.scheme.padEnd(5)} chart colors ${check.first} and ` +\n `${check.second} with ${check.vision}: ${String(check.difference)} < ${String(check.minimum)}`,\n );\n });\n report.unchecked.forEach((check) => {\n lines.push(\n ` unchecked ${check.scheme.padEnd(5)} ${check.name}: ${check.foreground} on ` +\n `${check.background} (check it in a browser)`,\n );\n });\n return lines.join('\\n');\n}\n\n/** Runs `panneau-check-theme` with its arguments, and returns the exit code. */\nexport async function runCheckTheme(\n args: readonly string[],\n output: { log: (text: string) => void; error: (text: string) => void } = console,\n load: ModuleImporter = importModule,\n): Promise<number> {\n const json = args.includes('--json');\n const exportIndex = args.indexOf('--export');\n const exportName = exportIndex >= 0 ? args[exportIndex + 1] : undefined;\n const files = args.filter(\n (arg, index) => !arg.startsWith('--') && (exportIndex < 0 || index !== exportIndex + 1),\n );\n const [file] = files;\n if (file === undefined || files.length > 1 || args.includes('--help')) {\n output.error(USAGE);\n return args.includes('--help') ? 0 : 2;\n }\n let theme: Theme;\n try {\n theme = await loadTheme(file, exportName, load);\n } catch (error) {\n output.error(error instanceof Error ? error.message : String(error));\n return 2;\n }\n const report = checkTheme(theme);\n output.log(json ? JSON.stringify(report, null, 2) : formatThemeReport(report, theme.label));\n return report.failures.length > 0 || report.colorDifferences.length > 0 ? 1 : 0;\n}\n"],"mappings":";;;;;AASA,IAAM,QAAQ;;;;;AAMd,SAAS,QAAQ,OAAgC;CAC7C,OACI,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,OAAO,MAAM,WAAW,YACxB,MAAM,WAAW;AAEzB;AAKA,IAAM,gBAAgC,QAAQ,OAAO;;AAGrD,eAAsB,UAClB,MACA,aAAa,WACb,OAAuB,cACT;CACd,MAAM,OAAO,QAAQ,IAAI;CACzB,MAAM,QACF,QAAQ,IAAI,MAAM,UACZ,KAAK,MAAM,MAAM,SAAS,MAAM,MAAM,CAAC,KACtC,MAAM,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,EAAA,CAAG;CACjD,IAAI,CAAC,QAAQ,KAAK,GACd,MAAM,IAAI,MACN,QAAQ,IAAI,MAAM,UACZ,GAAG,KAAI,sCACP,eAAe,WAAU,OAAQ,KAAI,iBAC/C;CAEJ,OAAO;AACX;;AAGA,SAAgB,kBAAkB,QAAqB,QAAQ,SAAiB;CAC5E,MAAM,QAAQ,CACV,GAAG,MAAK,IAAK,OAAO,OAAO,OAAM,oBAAqB,OAAO,SAAS,OAAM,WACrE,OAAO,UAAU,OAAM,cAAe,OAAO,iBAAiB,OAAM,uBAAwB;CAEvG,OAAO,SAAS,SAAS,UAAU;EAC/B,MAAM,KACF,gBAAgB,MAAM,OAAO,OAAO,CAAC,EAAC,IAAK,MAAM,KAAI,IAAK,MAAM,WAAU,MACnE,MAAM,WAAU,IAAK,OAAO,MAAM,KAAK,EAAC,OAAQ,OAAO,MAAM,OAAO,EAAC,GAChF;CACJ,CAAC;CACD,OAAO,iBAAiB,SAAS,UAAU;EACvC,MAAM,KACF,gBAAgB,MAAM,OAAO,OAAO,CAAC,EAAC,iBAAkB,MAAM,MAAK,OAC5D,MAAM,OAAM,QAAS,MAAM,OAAM,IAAK,OAAO,MAAM,UAAU,EAAC,KAAM,OAAO,MAAM,OAAO,GACnG;CACJ,CAAC;CACD,OAAO,UAAU,SAAS,UAAU;EAChC,MAAM,KACF,gBAAgB,MAAM,OAAO,OAAO,CAAC,EAAC,IAAK,MAAM,KAAI,IAAK,MAAM,WAAU,MACnE,MAAM,WAAU,yBAC3B;CACJ,CAAC;CACD,OAAO,MAAM,KAAK,IAAI;AAC1B;;AAGA,eAAsB,cAClB,MACA,SAAyE,SACzE,OAAuB,cACR;CACf,MAAM,OAAO,KAAK,SAAS,QAAQ;CACnC,MAAM,cAAc,KAAK,QAAQ,UAAU;CAC3C,MAAM,aAAa,eAAe,IAAI,KAAK,cAAc,KAAK,KAAA;CAC9D,MAAM,QAAQ,KAAK,QACd,KAAK,UAAU,CAAC,IAAI,WAAW,IAAI,MAAM,cAAc,KAAK,UAAU,cAAc,EACzF;CACA,MAAM,CAAC,QAAQ;CACf,IAAI,SAAS,KAAA,KAAa,MAAM,SAAS,KAAK,KAAK,SAAS,QAAQ,GAAG;EACnE,OAAO,MAAM,KAAK;EAClB,OAAO,KAAK,SAAS,QAAQ,IAAI,IAAI;CACzC;CACA,IAAI;CACJ,IAAI;EACA,QAAQ,MAAM,UAAU,MAAM,YAAY,IAAI;CAClD,SAAS,OAAO;EACZ,OAAO,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EACnE,OAAO;CACX;CACA,MAAM,SAAS,WAAW,KAAK;CAC/B,OAAO,IAAI,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,kBAAkB,QAAQ,MAAM,KAAK,CAAC;CAC1F,OAAO,OAAO,SAAS,SAAS,KAAK,OAAO,iBAAiB,SAAS,IAAI,IAAI;AAClF"}
@@ -0,0 +1,30 @@
1
+ /** An sRGB color, with channels from 0 to 255. */
2
+ export interface Rgb {
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ }
7
+ /**
8
+ * Reads a hex (`#05f`, `#0055ff`) or `rgb()` color. Returns `null` for anything else (a CSS
9
+ * variable, a named color, `color-mix()`), which Panneau can't compute with.
10
+ */
11
+ export declare function parseColor(value: string): Rgb | null;
12
+ /** A color as `#rrggbb`. */
13
+ export declare function toHex({ r, g, b }: Rgb): string;
14
+ /** Mixes two colors in sRGB, like `color-mix(in srgb, a, b <amount>)`: 0 gives `a`, 1 gives `b`. */
15
+ export declare function mixColors(a: Rgb, b: Rgb, amount: number): Rgb;
16
+ /** Relative luminance, as defined by WCAG 2. */
17
+ export declare function luminance({ r, g, b }: Rgb): number;
18
+ /** Contrast ratio between two colors, from 1 to 21 (WCAG 2). */
19
+ export declare function contrastRatio(a: Rgb, b: Rgb): number;
20
+ export declare const WHITE: Rgb;
21
+ export declare const BLACK: Rgb;
22
+ /**
23
+ * The closest color to `color` that reaches `ratio` against `background`, by mixing it with black
24
+ * or white (whichever moves away from the background), like Bootstrap's shade and tint. Returns
25
+ * the color itself when it already passes.
26
+ */
27
+ export declare function ensureContrast(color: Rgb, background: Rgb, ratio: number): Rgb;
28
+ /** White or black, whichever reads better on `background`, like Bootstrap's `color-contrast()`. */
29
+ export declare function readableTextColor(background: Rgb): Rgb;
30
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/theme/color.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,WAAW,GAAG;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACb;AAMD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAoBpD;AAED,4BAA4B;AAC5B,wBAAgB,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,MAAM,CAI9C;AAED,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAM7D;AAOD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,MAAM,CAElD;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAGpD;AAED,eAAO,MAAM,KAAK,EAAE,GAAgC,CAAC;AACrD,eAAO,MAAM,KAAK,EAAE,GAA0B,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAY9E;AAED,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG,CAEtD"}
@@ -0,0 +1,94 @@
1
+ //#region src/theme/color.ts
2
+ var HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
3
+ var RGB = /^rgba?\(\s*(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)\s*(?:[,/]\s*[\d.]+%?\s*)?\)$/i;
4
+ /**
5
+ * Reads a hex (`#05f`, `#0055ff`) or `rgb()` color. Returns `null` for anything else (a CSS
6
+ * variable, a named color, `color-mix()`), which Panneau can't compute with.
7
+ */
8
+ function parseColor(value) {
9
+ const color = value.trim();
10
+ const hex = HEX.exec(color);
11
+ if (hex?.[1] !== void 0) {
12
+ const digits = hex[1].length === 3 ? [...hex[1]].map((digit) => digit + digit).join("") : hex[1];
13
+ return {
14
+ r: Number.parseInt(digits.slice(0, 2), 16),
15
+ g: Number.parseInt(digits.slice(2, 4), 16),
16
+ b: Number.parseInt(digits.slice(4, 6), 16)
17
+ };
18
+ }
19
+ const rgb = RGB.exec(color);
20
+ if (rgb !== null) {
21
+ const [r, g, b] = [
22
+ rgb[1],
23
+ rgb[2],
24
+ rgb[3]
25
+ ].map((channel) => Math.min(255, Math.max(0, Number(channel))));
26
+ return {
27
+ r: r ?? 0,
28
+ g: g ?? 0,
29
+ b: b ?? 0
30
+ };
31
+ }
32
+ return null;
33
+ }
34
+ /** A color as `#rrggbb`. */
35
+ function toHex({ r, g, b }) {
36
+ return `#${[
37
+ r,
38
+ g,
39
+ b
40
+ ].map((channel) => Math.round(channel).toString(16).padStart(2, "0")).join("")}`;
41
+ }
42
+ /** Mixes two colors in sRGB, like `color-mix(in srgb, a, b <amount>)`: 0 gives `a`, 1 gives `b`. */
43
+ function mixColors(a, b, amount) {
44
+ return {
45
+ r: a.r + (b.r - a.r) * amount,
46
+ g: a.g + (b.g - a.g) * amount,
47
+ b: a.b + (b.b - a.b) * amount
48
+ };
49
+ }
50
+ function linear(channel) {
51
+ const value = channel / 255;
52
+ return value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4;
53
+ }
54
+ /** Relative luminance, as defined by WCAG 2. */
55
+ function luminance({ r, g, b }) {
56
+ return .2126 * linear(r) + .7152 * linear(g) + .0722 * linear(b);
57
+ }
58
+ /** Contrast ratio between two colors, from 1 to 21 (WCAG 2). */
59
+ function contrastRatio(a, b) {
60
+ const [light, dark] = [luminance(a), luminance(b)].sort((x, y) => y - x);
61
+ return (light + .05) / (dark + .05);
62
+ }
63
+ var WHITE = {
64
+ r: 255,
65
+ g: 255,
66
+ b: 255
67
+ };
68
+ var BLACK = {
69
+ r: 0,
70
+ g: 0,
71
+ b: 0
72
+ };
73
+ /**
74
+ * The closest color to `color` that reaches `ratio` against `background`, by mixing it with black
75
+ * or white (whichever moves away from the background), like Bootstrap's shade and tint. Returns
76
+ * the color itself when it already passes.
77
+ */
78
+ function ensureContrast(color, background, ratio) {
79
+ if (contrastRatio(color, background) >= ratio) return color;
80
+ const target = luminance(background) > .18 ? BLACK : WHITE;
81
+ for (let step = 1; step <= 20; step += 1) {
82
+ const mixed = mixColors(color, target, step / 20);
83
+ if (contrastRatio(mixed, background) >= ratio) return mixed;
84
+ }
85
+ return target;
86
+ }
87
+ /** White or black, whichever reads better on `background`, like Bootstrap's `color-contrast()`. */
88
+ function readableTextColor(background) {
89
+ return contrastRatio(WHITE, background) >= contrastRatio(BLACK, background) ? WHITE : BLACK;
90
+ }
91
+ //#endregion
92
+ export { BLACK, WHITE, contrastRatio, ensureContrast, luminance, mixColors, parseColor, readableTextColor, toHex };
93
+
94
+ //# sourceMappingURL=color.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.js","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/** An sRGB color, with channels from 0 to 255. */\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\nconst HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;\nconst RGB =\n /^rgba?\\(\\s*(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)\\s*(?:[,/]\\s*[\\d.]+%?\\s*)?\\)$/i;\n\n/**\n * Reads a hex (`#05f`, `#0055ff`) or `rgb()` color. Returns `null` for anything else (a CSS\n * variable, a named color, `color-mix()`), which Panneau can't compute with.\n */\nexport function parseColor(value: string): Rgb | null {\n const color = value.trim();\n const hex = HEX.exec(color);\n if (hex?.[1] !== undefined) {\n const digits =\n hex[1].length === 3 ? [...hex[1]].map((digit) => digit + digit).join('') : hex[1];\n return {\n r: Number.parseInt(digits.slice(0, 2), 16),\n g: Number.parseInt(digits.slice(2, 4), 16),\n b: Number.parseInt(digits.slice(4, 6), 16),\n };\n }\n const rgb = RGB.exec(color);\n if (rgb !== null) {\n const [r, g, b] = [rgb[1], rgb[2], rgb[3]].map((channel) =>\n Math.min(255, Math.max(0, Number(channel))),\n );\n return { r: r ?? 0, g: g ?? 0, b: b ?? 0 };\n }\n return null;\n}\n\n/** A color as `#rrggbb`. */\nexport function toHex({ r, g, b }: Rgb): string {\n return `#${[r, g, b]\n .map((channel) => Math.round(channel).toString(16).padStart(2, '0'))\n .join('')}`;\n}\n\n/** Mixes two colors in sRGB, like `color-mix(in srgb, a, b <amount>)`: 0 gives `a`, 1 gives `b`. */\nexport function mixColors(a: Rgb, b: Rgb, amount: number): Rgb {\n return {\n r: a.r + (b.r - a.r) * amount,\n g: a.g + (b.g - a.g) * amount,\n b: a.b + (b.b - a.b) * amount,\n };\n}\n\nfunction linear(channel: number): number {\n const value = channel / 255;\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;\n}\n\n/** Relative luminance, as defined by WCAG 2. */\nexport function luminance({ r, g, b }: Rgb): number {\n return 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);\n}\n\n/** Contrast ratio between two colors, from 1 to 21 (WCAG 2). */\nexport function contrastRatio(a: Rgb, b: Rgb): number {\n const [light, dark] = [luminance(a), luminance(b)].sort((x, y) => y - x) as [number, number];\n return (light + 0.05) / (dark + 0.05);\n}\n\nexport const WHITE: Rgb = { r: 255, g: 255, b: 255 };\nexport const BLACK: Rgb = { r: 0, g: 0, b: 0 };\n\n/**\n * The closest color to `color` that reaches `ratio` against `background`, by mixing it with black\n * or white (whichever moves away from the background), like Bootstrap's shade and tint. Returns\n * the color itself when it already passes.\n */\nexport function ensureContrast(color: Rgb, background: Rgb, ratio: number): Rgb {\n if (contrastRatio(color, background) >= ratio) {\n return color;\n }\n const target = luminance(background) > 0.18 ? BLACK : WHITE;\n for (let step = 1; step <= 20; step += 1) {\n const mixed = mixColors(color, target, step / 20);\n if (contrastRatio(mixed, background) >= ratio) {\n return mixed;\n }\n }\n return target;\n}\n\n/** White or black, whichever reads better on `background`, like Bootstrap's `color-contrast()`. */\nexport function readableTextColor(background: Rgb): Rgb {\n return contrastRatio(WHITE, background) >= contrastRatio(BLACK, background) ? WHITE : BLACK;\n}\n"],"mappings":";AAOA,IAAM,MAAM;AACZ,IAAM,MACF;;;;;AAMJ,SAAgB,WAAW,OAA2B;CAClD,MAAM,QAAQ,MAAM,KAAK;CACzB,MAAM,MAAM,IAAI,KAAK,KAAK;CAC1B,IAAI,MAAM,OAAO,KAAA,GAAW;EACxB,MAAM,SACF,IAAI,EAAE,CAAC,WAAW,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,UAAU,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,IAAI,IAAI;EACnF,OAAO;GACH,GAAG,OAAO,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE;GACzC,GAAG,OAAO,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE;GACzC,GAAG,OAAO,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE;EAC7C;CACJ;CACA,MAAM,MAAM,IAAI,KAAK,KAAK;CAC1B,IAAI,QAAQ,MAAM;EACd,MAAM,CAAC,GAAG,GAAG,KAAK;GAAC,IAAI;GAAI,IAAI;GAAI,IAAI;EAAE,CAAC,CAAC,KAAK,YAC5C,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,OAAO,OAAO,CAAC,CAAC,CAC9C;EACA,OAAO;GAAE,GAAG,KAAK;GAAG,GAAG,KAAK;GAAG,GAAG,KAAK;EAAE;CAC7C;CACA,OAAO;AACX;;AAGA,SAAgB,MAAM,EAAE,GAAG,GAAG,KAAkB;CAC5C,OAAO,IAAI;EAAC;EAAG;EAAG;CAAC,CAAC,CACf,KAAK,YAAY,KAAK,MAAM,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CACnE,KAAK,EAAE;AAChB;;AAGA,SAAgB,UAAU,GAAQ,GAAQ,QAAqB;CAC3D,OAAO;EACH,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;EACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;EACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;CAC3B;AACJ;AAEA,SAAS,OAAO,SAAyB;CACrC,MAAM,QAAQ,UAAU;CACxB,OAAO,SAAS,SAAU,QAAQ,UAAU,QAAQ,QAAS,UAAU;AAC3E;;AAGA,SAAgB,UAAU,EAAE,GAAG,GAAG,KAAkB;CAChD,OAAO,QAAS,OAAO,CAAC,IAAI,QAAS,OAAO,CAAC,IAAI,QAAS,OAAO,CAAC;AACtE;;AAGA,SAAgB,cAAc,GAAQ,GAAgB;CAClD,MAAM,CAAC,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CACvE,QAAQ,QAAQ,QAAS,OAAO;AACpC;AAEA,IAAa,QAAa;CAAE,GAAG;CAAK,GAAG;CAAK,GAAG;AAAI;AACnD,IAAa,QAAa;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;AAAE;;;;;;AAO7C,SAAgB,eAAe,OAAY,YAAiB,OAAoB;CAC5E,IAAI,cAAc,OAAO,UAAU,KAAK,OACpC,OAAO;CAEX,MAAM,SAAS,UAAU,UAAU,IAAI,MAAO,QAAQ;CACtD,KAAK,IAAI,OAAO,GAAG,QAAQ,IAAI,QAAQ,GAAG;EACtC,MAAM,QAAQ,UAAU,OAAO,QAAQ,OAAO,EAAE;EAChD,IAAI,cAAc,OAAO,UAAU,KAAK,OACpC,OAAO;CAEf;CACA,OAAO;AACX;;AAGA,SAAgB,kBAAkB,YAAsB;CACpD,OAAO,cAAc,OAAO,UAAU,KAAK,cAAc,OAAO,UAAU,IAAI,QAAQ;AAC1F"}