@oxyhq/bloom 0.35.8 → 0.37.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 (310) hide show
  1. package/lib/commonjs/frosted-icon-button/FrostedIconButton.js +148 -0
  2. package/lib/commonjs/frosted-icon-button/FrostedIconButton.js.map +1 -0
  3. package/lib/commonjs/frosted-icon-button/FrostedIconButton.web.js +166 -0
  4. package/lib/commonjs/frosted-icon-button/FrostedIconButton.web.js.map +1 -0
  5. package/lib/commonjs/frosted-icon-button/index.js +13 -0
  6. package/lib/commonjs/frosted-icon-button/index.js.map +1 -0
  7. package/lib/commonjs/frosted-icon-button/index.web.js +13 -0
  8. package/lib/commonjs/frosted-icon-button/index.web.js.map +1 -0
  9. package/lib/commonjs/frosted-icon-button/shared.js +112 -0
  10. package/lib/commonjs/frosted-icon-button/shared.js.map +1 -0
  11. package/lib/commonjs/frosted-icon-button/types.js +6 -0
  12. package/lib/commonjs/frosted-icon-button/types.js.map +1 -0
  13. package/lib/commonjs/index.js +10 -1
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/commonjs/index.web.js +46 -37
  16. package/lib/commonjs/index.web.js.map +1 -1
  17. package/lib/commonjs/theme/build-theme.js +31 -43
  18. package/lib/commonjs/theme/build-theme.js.map +1 -1
  19. package/lib/commonjs/theme/color-engine/cam16.js +161 -0
  20. package/lib/commonjs/theme/color-engine/cam16.js.map +1 -0
  21. package/lib/commonjs/theme/color-engine/color-role.js +183 -0
  22. package/lib/commonjs/theme/color-engine/color-role.js.map +1 -0
  23. package/lib/commonjs/theme/color-engine/color-roles.js +416 -0
  24. package/lib/commonjs/theme/color-engine/color-roles.js.map +1 -0
  25. package/lib/commonjs/theme/color-engine/color-utils.js +157 -0
  26. package/lib/commonjs/theme/color-engine/color-utils.js.map +1 -0
  27. package/lib/commonjs/theme/color-engine/contrast-curve.js +32 -0
  28. package/lib/commonjs/theme/color-engine/contrast-curve.js.map +1 -0
  29. package/lib/commonjs/theme/color-engine/contrast.js +70 -0
  30. package/lib/commonjs/theme/color-engine/contrast.js.map +1 -0
  31. package/lib/commonjs/theme/color-engine/dislike-analyzer.js +26 -0
  32. package/lib/commonjs/theme/color-engine/dislike-analyzer.js.map +1 -0
  33. package/lib/commonjs/theme/color-engine/dynamic-scheme.js +70 -0
  34. package/lib/commonjs/theme/color-engine/dynamic-scheme.js.map +1 -0
  35. package/lib/commonjs/theme/color-engine/hct-solver.js +236 -0
  36. package/lib/commonjs/theme/color-engine/hct-solver.js.map +1 -0
  37. package/lib/commonjs/theme/color-engine/hct.js +67 -0
  38. package/lib/commonjs/theme/color-engine/hct.js.map +1 -0
  39. package/lib/commonjs/theme/color-engine/index.js +173 -0
  40. package/lib/commonjs/theme/color-engine/index.js.map +1 -0
  41. package/lib/commonjs/theme/color-engine/lab-point-provider.js +32 -0
  42. package/lib/commonjs/theme/color-engine/lab-point-provider.js.map +1 -0
  43. package/lib/commonjs/theme/color-engine/math-utils.js +77 -0
  44. package/lib/commonjs/theme/color-engine/math-utils.js.map +1 -0
  45. package/lib/commonjs/theme/color-engine/quantizer-map.js +24 -0
  46. package/lib/commonjs/theme/color-engine/quantizer-map.js.map +1 -0
  47. package/lib/commonjs/theme/color-engine/quantizer-wu.js +266 -0
  48. package/lib/commonjs/theme/color-engine/quantizer-wu.js.map +1 -0
  49. package/lib/commonjs/theme/color-engine/scheme-variants.js +102 -0
  50. package/lib/commonjs/theme/color-engine/scheme-variants.js.map +1 -0
  51. package/lib/commonjs/theme/color-engine/score.js +80 -0
  52. package/lib/commonjs/theme/color-engine/score.js.map +1 -0
  53. package/lib/commonjs/theme/color-engine/seed-from-image.js +73 -0
  54. package/lib/commonjs/theme/color-engine/seed-from-image.js.map +1 -0
  55. package/lib/commonjs/theme/color-engine/tonal-palette.js +95 -0
  56. package/lib/commonjs/theme/color-engine/tonal-palette.js.map +1 -0
  57. package/lib/commonjs/theme/color-engine/tone-delta-pair.js +26 -0
  58. package/lib/commonjs/theme/color-engine/tone-delta-pair.js.map +1 -0
  59. package/lib/commonjs/theme/color-engine/variant.js +26 -0
  60. package/lib/commonjs/theme/color-engine/variant.js.map +1 -0
  61. package/lib/commonjs/theme/color-engine/viewing-conditions.js +61 -0
  62. package/lib/commonjs/theme/color-engine/viewing-conditions.js.map +1 -0
  63. package/lib/commonjs/theme/color-presets.js +44 -572
  64. package/lib/commonjs/theme/color-presets.js.map +1 -1
  65. package/lib/commonjs/theme/preset-vars.js +64 -47
  66. package/lib/commonjs/theme/preset-vars.js.map +1 -1
  67. package/lib/module/frosted-icon-button/FrostedIconButton.js +143 -0
  68. package/lib/module/frosted-icon-button/FrostedIconButton.js.map +1 -0
  69. package/lib/module/frosted-icon-button/FrostedIconButton.web.js +161 -0
  70. package/lib/module/frosted-icon-button/FrostedIconButton.web.js.map +1 -0
  71. package/lib/module/frosted-icon-button/index.js +4 -0
  72. package/lib/module/frosted-icon-button/index.js.map +1 -0
  73. package/lib/module/frosted-icon-button/index.web.js +11 -0
  74. package/lib/module/frosted-icon-button/index.web.js.map +1 -0
  75. package/lib/module/frosted-icon-button/shared.js +107 -0
  76. package/lib/module/frosted-icon-button/shared.js.map +1 -0
  77. package/lib/module/frosted-icon-button/types.js +4 -0
  78. package/lib/module/frosted-icon-button/types.js.map +1 -0
  79. package/lib/module/index.js +1 -0
  80. package/lib/module/index.js.map +1 -1
  81. package/lib/module/index.web.js +1 -0
  82. package/lib/module/index.web.js.map +1 -1
  83. package/lib/module/theme/build-theme.js +32 -44
  84. package/lib/module/theme/build-theme.js.map +1 -1
  85. package/lib/module/theme/color-engine/cam16.js +155 -0
  86. package/lib/module/theme/color-engine/cam16.js.map +1 -0
  87. package/lib/module/theme/color-engine/color-role.js +177 -0
  88. package/lib/module/theme/color-engine/color-role.js.map +1 -0
  89. package/lib/module/theme/color-engine/color-roles.js +411 -0
  90. package/lib/module/theme/color-engine/color-roles.js.map +1 -0
  91. package/lib/module/theme/color-engine/color-utils.js +135 -0
  92. package/lib/module/theme/color-engine/color-utils.js.map +1 -0
  93. package/lib/module/theme/color-engine/contrast-curve.js +26 -0
  94. package/lib/module/theme/color-engine/contrast-curve.js.map +1 -0
  95. package/lib/module/theme/color-engine/contrast.js +61 -0
  96. package/lib/module/theme/color-engine/contrast.js.map +1 -0
  97. package/lib/module/theme/color-engine/dislike-analyzer.js +20 -0
  98. package/lib/module/theme/color-engine/dislike-analyzer.js.map +1 -0
  99. package/lib/module/theme/color-engine/dynamic-scheme.js +65 -0
  100. package/lib/module/theme/color-engine/dynamic-scheme.js.map +1 -0
  101. package/lib/module/theme/color-engine/hct-solver.js +231 -0
  102. package/lib/module/theme/color-engine/hct-solver.js.map +1 -0
  103. package/lib/module/theme/color-engine/hct.js +61 -0
  104. package/lib/module/theme/color-engine/hct.js.map +1 -0
  105. package/lib/module/theme/color-engine/index.js +57 -0
  106. package/lib/module/theme/color-engine/index.js.map +1 -0
  107. package/lib/module/theme/color-engine/lab-point-provider.js +25 -0
  108. package/lib/module/theme/color-engine/lab-point-provider.js.map +1 -0
  109. package/lib/module/theme/color-engine/math-utils.js +65 -0
  110. package/lib/module/theme/color-engine/math-utils.js.map +1 -0
  111. package/lib/module/theme/color-engine/quantizer-map.js +20 -0
  112. package/lib/module/theme/color-engine/quantizer-map.js.map +1 -0
  113. package/lib/module/theme/color-engine/quantizer-wu.js +261 -0
  114. package/lib/module/theme/color-engine/quantizer-wu.js.map +1 -0
  115. package/lib/module/theme/color-engine/scheme-variants.js +93 -0
  116. package/lib/module/theme/color-engine/scheme-variants.js.map +1 -0
  117. package/lib/module/theme/color-engine/score.js +75 -0
  118. package/lib/module/theme/color-engine/score.js.map +1 -0
  119. package/lib/module/theme/color-engine/seed-from-image.js +67 -0
  120. package/lib/module/theme/color-engine/seed-from-image.js.map +1 -0
  121. package/lib/module/theme/color-engine/tonal-palette.js +89 -0
  122. package/lib/module/theme/color-engine/tonal-palette.js.map +1 -0
  123. package/lib/module/theme/color-engine/tone-delta-pair.js +21 -0
  124. package/lib/module/theme/color-engine/tone-delta-pair.js.map +1 -0
  125. package/lib/module/theme/color-engine/variant.js +22 -0
  126. package/lib/module/theme/color-engine/variant.js.map +1 -0
  127. package/lib/module/theme/color-engine/viewing-conditions.js +55 -0
  128. package/lib/module/theme/color-engine/viewing-conditions.js.map +1 -0
  129. package/lib/module/theme/color-presets.js +44 -572
  130. package/lib/module/theme/color-presets.js.map +1 -1
  131. package/lib/module/theme/preset-vars.js +64 -46
  132. package/lib/module/theme/preset-vars.js.map +1 -1
  133. package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.d.ts +5 -0
  134. package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.web.d.ts +5 -0
  136. package/lib/typescript/commonjs/frosted-icon-button/FrostedIconButton.web.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/frosted-icon-button/index.d.ts +3 -0
  138. package/lib/typescript/commonjs/frosted-icon-button/index.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/frosted-icon-button/index.web.d.ts +3 -0
  140. package/lib/typescript/commonjs/frosted-icon-button/index.web.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/frosted-icon-button/shared.d.ts +60 -0
  142. package/lib/typescript/commonjs/frosted-icon-button/shared.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/frosted-icon-button/types.d.ts +69 -0
  144. package/lib/typescript/commonjs/frosted-icon-button/types.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/index.d.ts +2 -0
  146. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/index.web.d.ts +2 -0
  148. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/theme/build-theme.d.ts.map +1 -1
  150. package/lib/typescript/commonjs/theme/color-engine/cam16.d.ts +26 -0
  151. package/lib/typescript/commonjs/theme/color-engine/cam16.d.ts.map +1 -0
  152. package/lib/typescript/commonjs/theme/color-engine/color-role.d.ts +36 -0
  153. package/lib/typescript/commonjs/theme/color-engine/color-role.d.ts.map +1 -0
  154. package/lib/typescript/commonjs/theme/color-engine/color-roles.d.ts +58 -0
  155. package/lib/typescript/commonjs/theme/color-engine/color-roles.d.ts.map +1 -0
  156. package/lib/typescript/commonjs/theme/color-engine/color-utils.d.ts +32 -0
  157. package/lib/typescript/commonjs/theme/color-engine/color-utils.d.ts.map +1 -0
  158. package/lib/typescript/commonjs/theme/color-engine/contrast-curve.d.ts +9 -0
  159. package/lib/typescript/commonjs/theme/color-engine/contrast-curve.d.ts.map +1 -0
  160. package/lib/typescript/commonjs/theme/color-engine/contrast.d.ts +12 -0
  161. package/lib/typescript/commonjs/theme/color-engine/contrast.d.ts.map +1 -0
  162. package/lib/typescript/commonjs/theme/color-engine/dislike-analyzer.d.ts +11 -0
  163. package/lib/typescript/commonjs/theme/color-engine/dislike-analyzer.d.ts.map +1 -0
  164. package/lib/typescript/commonjs/theme/color-engine/dynamic-scheme.d.ts +47 -0
  165. package/lib/typescript/commonjs/theme/color-engine/dynamic-scheme.d.ts.map +1 -0
  166. package/lib/typescript/commonjs/theme/color-engine/hct-solver.d.ts +6 -0
  167. package/lib/typescript/commonjs/theme/color-engine/hct-solver.d.ts.map +1 -0
  168. package/lib/typescript/commonjs/theme/color-engine/hct.d.ts +20 -0
  169. package/lib/typescript/commonjs/theme/color-engine/hct.d.ts.map +1 -0
  170. package/lib/typescript/commonjs/theme/color-engine/index.d.ts +44 -0
  171. package/lib/typescript/commonjs/theme/color-engine/index.d.ts.map +1 -0
  172. package/lib/typescript/commonjs/theme/color-engine/lab-point-provider.d.ts +6 -0
  173. package/lib/typescript/commonjs/theme/color-engine/lab-point-provider.d.ts.map +1 -0
  174. package/lib/typescript/commonjs/theme/color-engine/math-utils.d.ts +46 -0
  175. package/lib/typescript/commonjs/theme/color-engine/math-utils.d.ts.map +1 -0
  176. package/lib/typescript/commonjs/theme/color-engine/quantizer-map.d.ts +3 -0
  177. package/lib/typescript/commonjs/theme/color-engine/quantizer-map.d.ts.map +1 -0
  178. package/lib/typescript/commonjs/theme/color-engine/quantizer-wu.d.ts +3 -0
  179. package/lib/typescript/commonjs/theme/color-engine/quantizer-wu.d.ts.map +1 -0
  180. package/lib/typescript/commonjs/theme/color-engine/scheme-variants.d.ts +23 -0
  181. package/lib/typescript/commonjs/theme/color-engine/scheme-variants.d.ts.map +1 -0
  182. package/lib/typescript/commonjs/theme/color-engine/score.d.ts +8 -0
  183. package/lib/typescript/commonjs/theme/color-engine/score.d.ts.map +1 -0
  184. package/lib/typescript/commonjs/theme/color-engine/seed-from-image.d.ts +13 -0
  185. package/lib/typescript/commonjs/theme/color-engine/seed-from-image.d.ts.map +1 -0
  186. package/lib/typescript/commonjs/theme/color-engine/tonal-palette.d.ts +24 -0
  187. package/lib/typescript/commonjs/theme/color-engine/tonal-palette.d.ts.map +1 -0
  188. package/lib/typescript/commonjs/theme/color-engine/tone-delta-pair.d.ts +19 -0
  189. package/lib/typescript/commonjs/theme/color-engine/tone-delta-pair.d.ts.map +1 -0
  190. package/lib/typescript/commonjs/theme/color-engine/variant.d.ts +19 -0
  191. package/lib/typescript/commonjs/theme/color-engine/variant.d.ts.map +1 -0
  192. package/lib/typescript/commonjs/theme/color-engine/viewing-conditions.d.ts +18 -0
  193. package/lib/typescript/commonjs/theme/color-engine/viewing-conditions.d.ts.map +1 -0
  194. package/lib/typescript/commonjs/theme/color-presets.d.ts +25 -20
  195. package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
  196. package/lib/typescript/commonjs/theme/preset-vars.d.ts +16 -12
  197. package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -1
  198. package/lib/typescript/module/frosted-icon-button/FrostedIconButton.d.ts +5 -0
  199. package/lib/typescript/module/frosted-icon-button/FrostedIconButton.d.ts.map +1 -0
  200. package/lib/typescript/module/frosted-icon-button/FrostedIconButton.web.d.ts +5 -0
  201. package/lib/typescript/module/frosted-icon-button/FrostedIconButton.web.d.ts.map +1 -0
  202. package/lib/typescript/module/frosted-icon-button/index.d.ts +3 -0
  203. package/lib/typescript/module/frosted-icon-button/index.d.ts.map +1 -0
  204. package/lib/typescript/module/frosted-icon-button/index.web.d.ts +3 -0
  205. package/lib/typescript/module/frosted-icon-button/index.web.d.ts.map +1 -0
  206. package/lib/typescript/module/frosted-icon-button/shared.d.ts +60 -0
  207. package/lib/typescript/module/frosted-icon-button/shared.d.ts.map +1 -0
  208. package/lib/typescript/module/frosted-icon-button/types.d.ts +69 -0
  209. package/lib/typescript/module/frosted-icon-button/types.d.ts.map +1 -0
  210. package/lib/typescript/module/index.d.ts +2 -0
  211. package/lib/typescript/module/index.d.ts.map +1 -1
  212. package/lib/typescript/module/index.web.d.ts +2 -0
  213. package/lib/typescript/module/index.web.d.ts.map +1 -1
  214. package/lib/typescript/module/theme/build-theme.d.ts.map +1 -1
  215. package/lib/typescript/module/theme/color-engine/cam16.d.ts +26 -0
  216. package/lib/typescript/module/theme/color-engine/cam16.d.ts.map +1 -0
  217. package/lib/typescript/module/theme/color-engine/color-role.d.ts +36 -0
  218. package/lib/typescript/module/theme/color-engine/color-role.d.ts.map +1 -0
  219. package/lib/typescript/module/theme/color-engine/color-roles.d.ts +58 -0
  220. package/lib/typescript/module/theme/color-engine/color-roles.d.ts.map +1 -0
  221. package/lib/typescript/module/theme/color-engine/color-utils.d.ts +32 -0
  222. package/lib/typescript/module/theme/color-engine/color-utils.d.ts.map +1 -0
  223. package/lib/typescript/module/theme/color-engine/contrast-curve.d.ts +9 -0
  224. package/lib/typescript/module/theme/color-engine/contrast-curve.d.ts.map +1 -0
  225. package/lib/typescript/module/theme/color-engine/contrast.d.ts +12 -0
  226. package/lib/typescript/module/theme/color-engine/contrast.d.ts.map +1 -0
  227. package/lib/typescript/module/theme/color-engine/dislike-analyzer.d.ts +11 -0
  228. package/lib/typescript/module/theme/color-engine/dislike-analyzer.d.ts.map +1 -0
  229. package/lib/typescript/module/theme/color-engine/dynamic-scheme.d.ts +47 -0
  230. package/lib/typescript/module/theme/color-engine/dynamic-scheme.d.ts.map +1 -0
  231. package/lib/typescript/module/theme/color-engine/hct-solver.d.ts +6 -0
  232. package/lib/typescript/module/theme/color-engine/hct-solver.d.ts.map +1 -0
  233. package/lib/typescript/module/theme/color-engine/hct.d.ts +20 -0
  234. package/lib/typescript/module/theme/color-engine/hct.d.ts.map +1 -0
  235. package/lib/typescript/module/theme/color-engine/index.d.ts +44 -0
  236. package/lib/typescript/module/theme/color-engine/index.d.ts.map +1 -0
  237. package/lib/typescript/module/theme/color-engine/lab-point-provider.d.ts +6 -0
  238. package/lib/typescript/module/theme/color-engine/lab-point-provider.d.ts.map +1 -0
  239. package/lib/typescript/module/theme/color-engine/math-utils.d.ts +46 -0
  240. package/lib/typescript/module/theme/color-engine/math-utils.d.ts.map +1 -0
  241. package/lib/typescript/module/theme/color-engine/quantizer-map.d.ts +3 -0
  242. package/lib/typescript/module/theme/color-engine/quantizer-map.d.ts.map +1 -0
  243. package/lib/typescript/module/theme/color-engine/quantizer-wu.d.ts +3 -0
  244. package/lib/typescript/module/theme/color-engine/quantizer-wu.d.ts.map +1 -0
  245. package/lib/typescript/module/theme/color-engine/scheme-variants.d.ts +23 -0
  246. package/lib/typescript/module/theme/color-engine/scheme-variants.d.ts.map +1 -0
  247. package/lib/typescript/module/theme/color-engine/score.d.ts +8 -0
  248. package/lib/typescript/module/theme/color-engine/score.d.ts.map +1 -0
  249. package/lib/typescript/module/theme/color-engine/seed-from-image.d.ts +13 -0
  250. package/lib/typescript/module/theme/color-engine/seed-from-image.d.ts.map +1 -0
  251. package/lib/typescript/module/theme/color-engine/tonal-palette.d.ts +24 -0
  252. package/lib/typescript/module/theme/color-engine/tonal-palette.d.ts.map +1 -0
  253. package/lib/typescript/module/theme/color-engine/tone-delta-pair.d.ts +19 -0
  254. package/lib/typescript/module/theme/color-engine/tone-delta-pair.d.ts.map +1 -0
  255. package/lib/typescript/module/theme/color-engine/variant.d.ts +19 -0
  256. package/lib/typescript/module/theme/color-engine/variant.d.ts.map +1 -0
  257. package/lib/typescript/module/theme/color-engine/viewing-conditions.d.ts +18 -0
  258. package/lib/typescript/module/theme/color-engine/viewing-conditions.d.ts.map +1 -0
  259. package/lib/typescript/module/theme/color-presets.d.ts +25 -20
  260. package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
  261. package/lib/typescript/module/theme/preset-vars.d.ts +16 -12
  262. package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -1
  263. package/package.json +17 -1
  264. package/src/__tests__/BloomThemeProvider.test.tsx +9 -4
  265. package/src/__tests__/FrostedIconButton.test.tsx +137 -0
  266. package/src/__tests__/FrostedIconButton.web.test.tsx +246 -0
  267. package/src/__tests__/native-root-vars.test.ts +8 -4
  268. package/src/__tests__/preset-vars.test.ts +66 -37
  269. package/src/__tests__/theme.test.ts +20 -12
  270. package/src/frosted-icon-button/FrostedIconButton.stories.tsx +65 -0
  271. package/src/frosted-icon-button/FrostedIconButton.tsx +156 -0
  272. package/src/frosted-icon-button/FrostedIconButton.web.tsx +207 -0
  273. package/src/frosted-icon-button/index.ts +2 -0
  274. package/src/frosted-icon-button/index.web.ts +9 -0
  275. package/src/frosted-icon-button/shared.ts +118 -0
  276. package/src/frosted-icon-button/types.ts +81 -0
  277. package/src/index.ts +2 -0
  278. package/src/index.web.ts +2 -0
  279. package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +509 -509
  280. package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +613 -613
  281. package/src/theme/__tests__/color-engine-golden.test.ts +81 -0
  282. package/src/theme/__tests__/fixtures/color-engine-golden.json +1 -0
  283. package/src/theme/__tests__/golden-tokens.test.ts +6 -7
  284. package/src/theme/__tests__/runtime-contract.test.ts +8 -8
  285. package/src/theme/__tests__/theme-colors-parity.test.ts +44 -88
  286. package/src/theme/build-theme.ts +32 -44
  287. package/src/theme/color-engine/cam16.ts +179 -0
  288. package/src/theme/color-engine/color-role.ts +217 -0
  289. package/src/theme/color-engine/color-roles.ts +435 -0
  290. package/src/theme/color-engine/color-utils.ts +157 -0
  291. package/src/theme/color-engine/contrast-curve.ts +25 -0
  292. package/src/theme/color-engine/contrast.ts +59 -0
  293. package/src/theme/color-engine/dislike-analyzer.ts +19 -0
  294. package/src/theme/color-engine/dynamic-scheme.ts +90 -0
  295. package/src/theme/color-engine/hct-solver.ts +263 -0
  296. package/src/theme/color-engine/hct.ts +69 -0
  297. package/src/theme/color-engine/index.ts +73 -0
  298. package/src/theme/color-engine/lab-point-provider.ts +26 -0
  299. package/src/theme/color-engine/math-utils.ts +69 -0
  300. package/src/theme/color-engine/quantizer-map.ts +17 -0
  301. package/src/theme/color-engine/quantizer-wu.ts +320 -0
  302. package/src/theme/color-engine/scheme-variants.ts +113 -0
  303. package/src/theme/color-engine/score.ts +82 -0
  304. package/src/theme/color-engine/seed-from-image.ts +64 -0
  305. package/src/theme/color-engine/tonal-palette.ts +94 -0
  306. package/src/theme/color-engine/tone-delta-pair.ts +21 -0
  307. package/src/theme/color-engine/variant.ts +18 -0
  308. package/src/theme/color-engine/viewing-conditions.ts +77 -0
  309. package/src/theme/color-presets.ts +54 -631
  310. package/src/theme/preset-vars.ts +65 -49
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Golden parity test for the Bloom colour engine.
3
+ *
4
+ * The engine is a dependency-free reimplementation of the Material Color
5
+ * Utilities algorithm. These fixtures were generated ONCE from the reference
6
+ * library and frozen; this test asserts Bloom's engine reproduces every role,
7
+ * for 5 seeds × 4 variants × light/dark, byte-for-byte. If the engine ever
8
+ * drifts from the reference algorithm, this fails — no runtime dependency on the
9
+ * reference is needed to keep the port honest.
10
+ */
11
+ import golden from './fixtures/color-engine-golden.json';
12
+ import {
13
+ argbFromHex,
14
+ generateRoleColors,
15
+ seedHexFromImagePixels,
16
+ type RoleName,
17
+ type SchemeVariant,
18
+ } from '../color-engine';
19
+
20
+ const SEED_HEX: Record<string, string> = {
21
+ oxy: '#C46EDE',
22
+ blue: '#1D9BF0',
23
+ green: '#10B981',
24
+ pink: '#EC4899',
25
+ faircoin: '#9FFB50',
26
+ };
27
+
28
+ /** `rgb(r g b)` (the engine's output form) → `#rrggbb` (the fixture form). */
29
+ function rgbToHex(rgb: string): string {
30
+ const match = rgb.match(/rgb\((\d+) (\d+) (\d+)\)/);
31
+ if (!match) {
32
+ throw new Error(`Expected an "rgb(r g b)" string, got: ${rgb}`);
33
+ }
34
+ return `#${[1, 2, 3].map((i) => Number(match[i]).toString(16).padStart(2, '0')).join('')}`;
35
+ }
36
+
37
+ const fixtures = golden as Record<string, Record<string, string>>;
38
+
39
+ describe('Bloom colour engine — golden parity with Material Color Utilities', () => {
40
+ for (const [combo, roles] of Object.entries(fixtures)) {
41
+ it(`reproduces every role for ${combo}`, () => {
42
+ const [seed, variant, mode] = combo.split('|');
43
+ if (seed === undefined || variant === undefined || mode === undefined) {
44
+ throw new Error(`malformed golden combo: ${combo}`);
45
+ }
46
+ const seedHex = SEED_HEX[seed];
47
+ if (seedHex === undefined) throw new Error(`no seed hex for preset "${seed}"`);
48
+ const generated = generateRoleColors({
49
+ seed: seedHex,
50
+ variant: variant as SchemeVariant,
51
+ isDark: mode === 'dark',
52
+ });
53
+ for (const [role, expectedHex] of Object.entries(roles)) {
54
+ expect(rgbToHex(generated[role as RoleName])).toBe(expectedHex);
55
+ }
56
+ });
57
+ }
58
+ });
59
+
60
+ /** Build a flat ARGB pixel array from `[hex, count]` pairs. */
61
+ function image(colors: Array<[string, number]>): number[] {
62
+ const pixels: number[] = [];
63
+ for (const [hex, count] of colors) {
64
+ const argb = argbFromHex(hex);
65
+ for (let i = 0; i < count; i++) pixels.push(argb);
66
+ }
67
+ return pixels;
68
+ }
69
+
70
+ describe('seed extraction from an image (deterministic)', () => {
71
+ const cases: Array<[string, Array<[string, number]>, string]> = [
72
+ ['a purple-dominant photo', [['#C46EDE', 900], ['#1D9BF0', 80], ['#ffffff', 400], ['#101014', 300]], '#c46ede'],
73
+ ['a pink/green photo', [['#EC6094', 600], ['#10B981', 500], ['#F59E0B', 200], ['#000000', 700]], '#ec6094'],
74
+ ['a teal photo', [['#005c67', 1000], ['#14b8a6', 300], ['#eeeeee', 600]], '#005c67'],
75
+ ];
76
+ for (const [label, colors, expectedSeed] of cases) {
77
+ it(`picks the dominant chromatic colour from ${label}`, () => {
78
+ expect(seedHexFromImagePixels(image(colors))).toBe(expectedSeed);
79
+ });
80
+ }
81
+ });
@@ -0,0 +1 @@
1
+ {"oxy|vibrant|light":{"background":"#fff7fb","onBackground":"#211922","surface":"#fff7fb","surfaceDim":"#e4d6e3","surfaceBright":"#fff7fb","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#ffeffd","surfaceContainer":"#f9e9f7","surfaceContainerHigh":"#f3e4f1","surfaceContainerHighest":"#eddeeb","onSurface":"#211922","surfaceVariant":"#efddee","onSurfaceVariant":"#4f4350","inverseSurface":"#362d37","inverseOnSurface":"#fcecfa","outline":"#817381","outlineVariant":"#d2c1d2","shadow":"#000000","scrim":"#000000","surfaceTint":"#9c00c8","primary":"#9c00c8","onPrimary":"#ffffff","primaryContainer":"#fbd7ff","onPrimaryContainer":"#77009a","inversePrimary":"#f0b0ff","secondary":"#75546f","onSecondary":"#ffffff","secondaryContainer":"#ffd7f5","onSecondaryContainer":"#5b3d57","tertiary":"#834e6b","onTertiary":"#ffffff","tertiaryContainer":"#ffd8ea","onTertiaryContainer":"#683653","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"oxy|vibrant|dark":{"background":"#18111a","onBackground":"#eddeeb","surface":"#18111a","surfaceDim":"#18111a","surfaceBright":"#3f3640","surfaceContainerLowest":"#130c14","surfaceContainerLow":"#211922","surfaceContainer":"#251d26","surfaceContainerHigh":"#302731","surfaceContainerHighest":"#3b323c","onSurface":"#eddeeb","surfaceVariant":"#4f4350","onSurfaceVariant":"#d2c1d2","inverseSurface":"#eddeeb","inverseOnSurface":"#362d37","outline":"#9b8c9b","outlineVariant":"#4f4350","shadow":"#000000","scrim":"#000000","surfaceTint":"#f0b0ff","primary":"#f0b0ff","onPrimary":"#54006d","primaryContainer":"#77009a","onPrimaryContainer":"#fbd7ff","inversePrimary":"#9c00c8","secondary":"#e3bada","onSecondary":"#43273f","secondaryContainer":"#5b3d57","onSecondaryContainer":"#ffd7f5","tertiary":"#f6b3d5","onTertiary":"#4e203c","tertiaryContainer":"#683653","onTertiaryContainer":"#ffd8ea","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"oxy|expressive|light":{"background":"#fff7f9","onBackground":"#22191f","surface":"#fff7f9","surfaceDim":"#e6d6de","surfaceBright":"#fff7f9","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#fff0f7","surfaceContainer":"#fae9f2","surfaceContainerHigh":"#f4e4ec","surfaceContainerHighest":"#eedee7","onSurface":"#22191f","surfaceVariant":"#f3dcea","onSurfaceVariant":"#52424d","inverseSurface":"#372e34","inverseOnSurface":"#fdecf5","outline":"#84727e","outlineVariant":"#d6c0ce","shadow":"#000000","scrim":"#000000","surfaceTint":"#00696f","primary":"#00696f","onPrimary":"#ffffff","primaryContainer":"#90f2f9","onPrimaryContainer":"#004f53","inversePrimary":"#72d6dd","secondary":"#805159","onSecondary":"#ffffff","secondaryContainer":"#ffd9de","onSecondaryContainer":"#653b42","tertiary":"#775a1a","onTertiary":"#ffffff","tertiaryContainer":"#ffdea5","onTertiaryContainer":"#5d4202","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"oxy|expressive|dark":{"background":"#191117","onBackground":"#eedee7","surface":"#191117","surfaceDim":"#191117","surfaceBright":"#40363d","surfaceContainerLowest":"#130c11","surfaceContainerLow":"#22191f","surfaceContainer":"#261d23","surfaceContainerHigh":"#31272e","surfaceContainerHighest":"#3c3238","onSurface":"#eedee7","surfaceVariant":"#52424d","onSurfaceVariant":"#d6c0ce","inverseSurface":"#eedee7","inverseOnSurface":"#372e34","outline":"#9f8b98","outlineVariant":"#52424d","shadow":"#000000","scrim":"#000000","surfaceTint":"#72d6dd","primary":"#72d6dd","onPrimary":"#00373a","primaryContainer":"#004f53","onPrimaryContainer":"#90f2f9","inversePrimary":"#00696f","secondary":"#f2b7c0","onSecondary":"#4b252c","secondaryContainer":"#653b42","onSecondaryContainer":"#ffd9de","tertiary":"#e9c177","onTertiary":"#412d00","tertiaryContainer":"#5d4202","onTertiaryContainer":"#ffdea5","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"oxy|tonalSpot|light":{"background":"#fff7fb","onBackground":"#1f1a1f","surface":"#fff7fb","surfaceDim":"#e1d7de","surfaceBright":"#fff7fb","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#fbf1f8","surfaceContainer":"#f5ebf2","surfaceContainerHigh":"#f0e5ed","surfaceContainerHighest":"#eae0e7","onSurface":"#1f1a1f","surfaceVariant":"#ebdfe9","onSurfaceVariant":"#4c444c","inverseSurface":"#342f34","inverseOnSurface":"#f8eef5","outline":"#7e747d","outlineVariant":"#cfc3cd","shadow":"#000000","scrim":"#000000","surfaceTint":"#784f83","primary":"#784f83","onPrimary":"#ffffff","primaryContainer":"#fbd7ff","onPrimaryContainer":"#5e3869","inversePrimary":"#e7b6f1","secondary":"#6a596c","onSecondary":"#ffffff","secondaryContainer":"#f2dcf3","onSecondaryContainer":"#514154","tertiary":"#82524e","onTertiary":"#ffffff","tertiaryContainer":"#ffdad7","onTertiaryContainer":"#663b37","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"oxy|tonalSpot|dark":{"background":"#161217","onBackground":"#eae0e7","surface":"#161217","surfaceDim":"#161217","surfaceBright":"#3d373d","surfaceContainerLowest":"#110d12","surfaceContainerLow":"#1f1a1f","surfaceContainer":"#231e23","surfaceContainerHigh":"#2e282e","surfaceContainerHighest":"#393339","onSurface":"#eae0e7","surfaceVariant":"#4c444c","onSurfaceVariant":"#cfc3cd","inverseSurface":"#eae0e7","inverseOnSurface":"#342f34","outline":"#988e97","outlineVariant":"#4c444c","shadow":"#000000","scrim":"#000000","surfaceTint":"#e7b6f1","primary":"#e7b6f1","onPrimary":"#462151","primaryContainer":"#5e3869","onPrimaryContainer":"#fbd7ff","inversePrimary":"#784f83","secondary":"#d5c0d6","onSecondary":"#3a2b3d","secondaryContainer":"#514154","onSecondaryContainer":"#f2dcf3","tertiary":"#f5b7b2","onTertiary":"#4c2522","tertiaryContainer":"#663b37","onTertiaryContainer":"#ffdad7","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"oxy|neutral|light":{"background":"#fef8f9","onBackground":"#1d1b1c","surface":"#fef8f9","surfaceDim":"#ded9da","surfaceBright":"#fef8f9","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f8f2f4","surfaceContainer":"#f2ecee","surfaceContainerHigh":"#ede7e8","surfaceContainerHighest":"#e7e1e3","onSurface":"#1d1b1c","surfaceVariant":"#e7e1e3","onSurfaceVariant":"#494647","inverseSurface":"#323031","inverseOnSurface":"#f5eff1","outline":"#7a7678","outlineVariant":"#cac5c7","shadow":"#000000","scrim":"#000000","surfaceTint":"#675a68","primary":"#675a68","onPrimary":"#ffffff","primaryContainer":"#efddee","onPrimaryContainer":"#4f4350","inversePrimary":"#d2c1d2","secondary":"#645c64","onSecondary":"#ffffff","secondaryContainer":"#ebdfe9","onSecondaryContainer":"#4c444c","tertiary":"#6a596c","onTertiary":"#ffffff","tertiaryContainer":"#f2dcf3","onTertiaryContainer":"#514154","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"oxy|neutral|dark":{"background":"#151314","onBackground":"#e7e1e3","surface":"#151314","surfaceDim":"#151314","surfaceBright":"#3b383a","surfaceContainerLowest":"#0f0e0f","surfaceContainerLow":"#1d1b1c","surfaceContainer":"#211f20","surfaceContainerHigh":"#2b292b","surfaceContainerHighest":"#363435","onSurface":"#e7e1e3","surfaceVariant":"#494647","onSurfaceVariant":"#cac5c7","inverseSurface":"#e7e1e3","inverseOnSurface":"#323031","outline":"#949091","outlineVariant":"#494647","shadow":"#000000","scrim":"#000000","surfaceTint":"#d2c1d2","primary":"#d2c1d2","onPrimary":"#382d39","primaryContainer":"#4f4350","onPrimaryContainer":"#efddee","inversePrimary":"#675a68","secondary":"#cfc3cd","onSecondary":"#352e36","secondaryContainer":"#4c444c","onSecondaryContainer":"#ebdfe9","tertiary":"#d5c0d6","onTertiary":"#3a2b3d","tertiaryContainer":"#514154","onTertiaryContainer":"#f2dcf3","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"blue|vibrant|light":{"background":"#f7f9ff","onBackground":"#151c24","surface":"#f7f9ff","surfaceDim":"#d4dae5","surfaceBright":"#f7f9ff","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#edf4ff","surfaceContainer":"#e8eef9","surfaceContainerHigh":"#e2e9f4","surfaceContainerHighest":"#dce3ee","onSurface":"#151c24","surfaceVariant":"#dae3f1","onSurfaceVariant":"#3e4852","inverseSurface":"#2a3139","inverseOnSurface":"#eaf1fc","outline":"#6f7884","outlineVariant":"#bec7d4","shadow":"#000000","scrim":"#000000","surfaceTint":"#00629d","primary":"#00629d","onPrimary":"#ffffff","primaryContainer":"#cfe5ff","onPrimaryContainer":"#004a78","inversePrimary":"#99cbff","secondary":"#515e7d","onSecondary":"#ffffff","secondaryContainer":"#d9e2ff","onSecondaryContainer":"#3a4664","tertiary":"#595a8b","onTertiary":"#ffffff","tertiaryContainer":"#e1e0ff","onTertiaryContainer":"#414271","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"blue|vibrant|dark":{"background":"#0d141b","onBackground":"#dce3ee","surface":"#0d141b","surfaceDim":"#0d141b","surfaceBright":"#333a42","surfaceContainerLowest":"#080f16","surfaceContainerLow":"#151c24","surfaceContainer":"#192028","surfaceContainerHigh":"#242b33","surfaceContainerHighest":"#2e353e","onSurface":"#dce3ee","surfaceVariant":"#3e4852","onSurfaceVariant":"#bec7d4","inverseSurface":"#dce3ee","inverseOnSurface":"#2a3139","outline":"#88929e","outlineVariant":"#3e4852","shadow":"#000000","scrim":"#000000","surfaceTint":"#99cbff","primary":"#99cbff","onPrimary":"#003354","primaryContainer":"#004a78","onPrimaryContainer":"#cfe5ff","inversePrimary":"#00629d","secondary":"#b9c6ea","onSecondary":"#23304d","secondaryContainer":"#3a4664","onSecondaryContainer":"#d9e2ff","tertiary":"#c1c2f9","onTertiary":"#2a2b59","tertiaryContainer":"#414271","onTertiaryContainer":"#e1e0ff","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"blue|expressive|light":{"background":"#f9f9ff","onBackground":"#191c22","surface":"#f9f9ff","surfaceDim":"#d8d9e3","surfaceBright":"#f9f9ff","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f2f3fd","surfaceContainer":"#ecedf7","surfaceContainerHigh":"#e6e8f2","surfaceContainerHighest":"#e0e2ec","onSurface":"#191c22","surfaceVariant":"#dee2f2","onSurfaceVariant":"#414753","inverseSurface":"#2d3038","inverseOnSurface":"#eff0fa","outline":"#727785","outlineVariant":"#c1c6d6","shadow":"#000000","scrim":"#000000","surfaceTint":"#4c6722","primary":"#4c6722","onPrimary":"#ffffff","primaryContainer":"#cdee99","onPrimaryContainer":"#354e0b","inversePrimary":"#b1d180","secondary":"#77536c","onSecondary":"#ffffff","secondaryContainer":"#ffd7f0","onSecondaryContainer":"#5d3c54","tertiary":"#475e8a","onTertiary":"#ffffff","tertiaryContainer":"#d7e2ff","onTertiaryContainer":"#2f4671","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"blue|expressive|dark":{"background":"#10131a","onBackground":"#e0e2ec","surface":"#10131a","surfaceDim":"#10131a","surfaceBright":"#363941","surfaceContainerLowest":"#0b0e15","surfaceContainerLow":"#191c22","surfaceContainer":"#1d2027","surfaceContainerHigh":"#272a31","surfaceContainerHighest":"#32353c","onSurface":"#e0e2ec","surfaceVariant":"#414753","onSurfaceVariant":"#c1c6d6","inverseSurface":"#e0e2ec","inverseOnSurface":"#2d3038","outline":"#8b909f","outlineVariant":"#414753","shadow":"#000000","scrim":"#000000","surfaceTint":"#b1d180","primary":"#b1d180","onPrimary":"#213600","primaryContainer":"#354e0b","onPrimaryContainer":"#cdee99","inversePrimary":"#4c6722","secondary":"#e6bad7","onSecondary":"#45263d","secondaryContainer":"#5d3c54","onSecondaryContainer":"#ffd7f0","tertiary":"#b0c7f8","onTertiary":"#173059","tertiaryContainer":"#2f4671","onTertiaryContainer":"#d7e2ff","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"blue|tonalSpot|light":{"background":"#f7f9ff","onBackground":"#181c20","surface":"#f7f9ff","surfaceDim":"#d8dae0","surfaceBright":"#f7f9ff","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f2f3f9","surfaceContainer":"#eceef4","surfaceContainerHigh":"#e6e8ee","surfaceContainerHighest":"#e0e2e8","onSurface":"#181c20","surfaceVariant":"#dee3eb","onSurfaceVariant":"#42474e","inverseSurface":"#2d3135","inverseOnSurface":"#eff1f6","outline":"#72777f","outlineVariant":"#c2c7cf","shadow":"#000000","scrim":"#000000","surfaceTint":"#31628d","primary":"#31628d","onPrimary":"#ffffff","primaryContainer":"#cfe5ff","onPrimaryContainer":"#124a73","inversePrimary":"#9ccbfb","secondary":"#526070","onSecondary":"#ffffff","secondaryContainer":"#d5e4f7","onSecondaryContainer":"#3a4857","tertiary":"#695779","onTertiary":"#ffffff","tertiaryContainer":"#f0dbff","onTertiaryContainer":"#504060","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"blue|tonalSpot|dark":{"background":"#101418","onBackground":"#e0e2e8","surface":"#101418","surfaceDim":"#101418","surfaceBright":"#36393e","surfaceContainerLowest":"#0b0e12","surfaceContainerLow":"#181c20","surfaceContainer":"#1d2024","surfaceContainerHigh":"#272a2f","surfaceContainerHighest":"#32353a","onSurface":"#e0e2e8","surfaceVariant":"#42474e","onSurfaceVariant":"#c2c7cf","inverseSurface":"#e0e2e8","inverseOnSurface":"#2d3135","outline":"#8c9199","outlineVariant":"#42474e","shadow":"#000000","scrim":"#000000","surfaceTint":"#9ccbfb","primary":"#9ccbfb","onPrimary":"#003354","primaryContainer":"#124a73","onPrimaryContainer":"#cfe5ff","inversePrimary":"#31628d","secondary":"#b9c8da","onSecondary":"#243240","secondaryContainer":"#3a4857","onSecondaryContainer":"#d5e4f7","tertiary":"#d4bee6","onTertiary":"#392a49","tertiaryContainer":"#504060","onTertiaryContainer":"#f0dbff","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"blue|neutral|light":{"background":"#fbf9fa","onBackground":"#1b1c1d","surface":"#fbf9fa","surfaceDim":"#dbd9db","surfaceBright":"#fbf9fa","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f5f3f4","surfaceContainer":"#efedee","surfaceContainerHigh":"#eae7e9","surfaceContainerHighest":"#e4e2e3","onSurface":"#1b1c1d","surfaceVariant":"#e4e2e3","onSurfaceVariant":"#474748","inverseSurface":"#303031","inverseOnSurface":"#f2f0f1","outline":"#777778","outlineVariant":"#c8c6c7","shadow":"#000000","scrim":"#000000","surfaceTint":"#565f6b","primary":"#565f6b","onPrimary":"#ffffff","primaryContainer":"#dae3f1","onPrimaryContainer":"#3e4852","inversePrimary":"#bec7d4","secondary":"#5a5f66","onSecondary":"#ffffff","secondaryContainer":"#dee3eb","onSecondaryContainer":"#42474e","tertiary":"#526070","onTertiary":"#ffffff","tertiaryContainer":"#d5e4f7","onTertiaryContainer":"#3a4857","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"blue|neutral|dark":{"background":"#131314","onBackground":"#e4e2e3","surface":"#131314","surfaceDim":"#131314","surfaceBright":"#39393a","surfaceContainerLowest":"#0e0e0f","surfaceContainerLow":"#1b1c1d","surfaceContainer":"#1f2021","surfaceContainerHigh":"#2a2a2b","surfaceContainerHighest":"#343536","onSurface":"#e4e2e3","surfaceVariant":"#474748","onSurfaceVariant":"#c8c6c7","inverseSurface":"#e4e2e3","inverseOnSurface":"#303031","outline":"#919091","outlineVariant":"#474748","shadow":"#000000","scrim":"#000000","surfaceTint":"#bec7d4","primary":"#bec7d4","onPrimary":"#28313b","primaryContainer":"#3e4852","onPrimaryContainer":"#dae3f1","inversePrimary":"#565f6b","secondary":"#c2c7cf","onSecondary":"#2c3137","secondaryContainer":"#42474e","onSecondaryContainer":"#dee3eb","tertiary":"#b9c8da","onTertiary":"#243240","tertiaryContainer":"#3a4857","onTertiaryContainer":"#d5e4f7","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"green|vibrant|light":{"background":"#f0fdf2","onBackground":"#131e18","surface":"#f0fdf2","surfaceDim":"#d0ddd3","surfaceBright":"#f0fdf2","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#eaf7ed","surfaceContainer":"#e4f1e7","surfaceContainerHigh":"#deebe1","surfaceContainerHighest":"#d9e6dc","onSurface":"#131e18","surfaceVariant":"#d6e7db","onSurfaceVariant":"#3b4a41","inverseSurface":"#28332c","inverseOnSurface":"#e7f4ea","outline":"#6b7b71","outlineVariant":"#bacbbf","shadow":"#000000","scrim":"#000000","surfaceTint":"#006c49","primary":"#006c49","onPrimary":"#ffffff","primaryContainer":"#47ffb8","onPrimaryContainer":"#005236","inversePrimary":"#00e29d","secondary":"#3b665c","onSecondary":"#ffffff","secondaryContainer":"#bdecdf","onSecondaryContainer":"#224e45","tertiary":"#1a6967","onTertiary":"#ffffff","tertiaryContainer":"#a8efed","onTertiaryContainer":"#00504e","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"green|vibrant|dark":{"background":"#0b1610","onBackground":"#d9e6dc","surface":"#0b1610","surfaceDim":"#0b1610","surfaceBright":"#303c35","surfaceContainerLowest":"#06100b","surfaceContainerLow":"#131e18","surfaceContainer":"#17221c","surfaceContainerHigh":"#212c26","surfaceContainerHighest":"#2c3731","onSurface":"#d9e6dc","surfaceVariant":"#3b4a41","onSurfaceVariant":"#bacbbf","inverseSurface":"#d9e6dc","inverseOnSurface":"#28332c","outline":"#84958a","outlineVariant":"#3b4a41","shadow":"#000000","scrim":"#000000","surfaceTint":"#00e29d","primary":"#00e29d","onPrimary":"#003824","primaryContainer":"#005236","onPrimaryContainer":"#47ffb8","inversePrimary":"#006c49","secondary":"#a2d0c3","onSecondary":"#05372e","secondaryContainer":"#224e45","onSecondaryContainer":"#bdecdf","tertiary":"#8cd3d0","onTertiary":"#003736","tertiaryContainer":"#00504e","onTertiaryContainer":"#a8efed","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"green|expressive|light":{"background":"#f1fcf7","onBackground":"#141d1b","surface":"#f1fcf7","surfaceDim":"#d2dcd8","surfaceBright":"#f1fcf7","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#ecf6f1","surfaceContainer":"#e6f0ec","surfaceContainerHigh":"#e0eae6","surfaceContainerHighest":"#dbe5e0","onSurface":"#141d1b","surfaceVariant":"#d4e7e0","onSurfaceVariant":"#3a4a45","inverseSurface":"#29322f","inverseOnSurface":"#e9f3ee","outline":"#697b75","outlineVariant":"#b8cbc4","shadow":"#000000","scrim":"#000000","surfaceTint":"#914b26","primary":"#914b26","onPrimary":"#ffffff","primaryContainer":"#ffdbcb","onPrimaryContainer":"#743510","inversePrimary":"#ffb692","secondary":"#39656c","onSecondary":"#ffffff","secondaryContainer":"#bdeaf3","onSecondaryContainer":"#1f4d54","tertiary":"#1e695f","onTertiary":"#ffffff","tertiaryContainer":"#a9f0e3","onTertiaryContainer":"#005048","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"green|expressive|dark":{"background":"#0c1513","onBackground":"#dbe5e0","surface":"#0c1513","surfaceDim":"#0c1513","surfaceBright":"#323b38","surfaceContainerLowest":"#07100e","surfaceContainerLow":"#141d1b","surfaceContainer":"#18211f","surfaceContainerHigh":"#232c29","surfaceContainerHighest":"#2d3734","onSurface":"#dbe5e0","surfaceVariant":"#3a4a45","onSurfaceVariant":"#b8cbc4","inverseSurface":"#dbe5e0","inverseOnSurface":"#29322f","outline":"#83958f","outlineVariant":"#3a4a45","shadow":"#000000","scrim":"#000000","surfaceTint":"#ffb692","primary":"#ffb692","onPrimary":"#562000","primaryContainer":"#743510","onPrimaryContainer":"#ffdbcb","inversePrimary":"#914b26","secondary":"#a1ced6","onSecondary":"#00363d","secondaryContainer":"#1f4d54","onSecondaryContainer":"#bdeaf3","tertiary":"#8dd4c7","onTertiary":"#003731","tertiaryContainer":"#005048","onTertiaryContainer":"#a9f0e3","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"green|tonalSpot|light":{"background":"#f5fbf5","onBackground":"#171d19","surface":"#f5fbf5","surfaceDim":"#d6dbd6","surfaceBright":"#f5fbf5","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f0f5ef","surfaceContainer":"#eaefe9","surfaceContainerHigh":"#e4eae4","surfaceContainerHighest":"#dee4de","onSurface":"#171d19","surfaceVariant":"#dce5dd","onSurfaceVariant":"#404943","inverseSurface":"#2c322e","inverseOnSurface":"#edf2ec","outline":"#707973","outlineVariant":"#c0c9c1","shadow":"#000000","scrim":"#000000","surfaceTint":"#226a4c","primary":"#226a4c","onPrimary":"#ffffff","primaryContainer":"#aaf2cc","onPrimaryContainer":"#005236","inversePrimary":"#8ed5b0","secondary":"#4d6357","onSecondary":"#ffffff","secondaryContainer":"#d0e8d8","onSecondaryContainer":"#364b40","tertiary":"#3d6473","onTertiary":"#ffffff","tertiaryContainer":"#c0e9fb","onTertiaryContainer":"#244c5a","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"green|tonalSpot|dark":{"background":"#0f1511","onBackground":"#dee4de","surface":"#0f1511","surfaceDim":"#0f1511","surfaceBright":"#353b37","surfaceContainerLowest":"#0a0f0c","surfaceContainerLow":"#171d19","surfaceContainer":"#1b211d","surfaceContainerHigh":"#262b28","surfaceContainerHighest":"#303632","onSurface":"#dee4de","surfaceVariant":"#404943","onSurfaceVariant":"#c0c9c1","inverseSurface":"#dee4de","inverseOnSurface":"#2c322e","outline":"#8a938c","outlineVariant":"#404943","shadow":"#000000","scrim":"#000000","surfaceTint":"#8ed5b0","primary":"#8ed5b0","onPrimary":"#003824","primaryContainer":"#005236","onPrimaryContainer":"#aaf2cc","inversePrimary":"#226a4c","secondary":"#b4ccbd","onSecondary":"#20352a","secondaryContainer":"#364b40","onSecondaryContainer":"#d0e8d8","tertiary":"#a5cdde","onTertiary":"#063543","tertiaryContainer":"#244c5a","onTertiaryContainer":"#c0e9fb","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"green|neutral|light":{"background":"#fbf9f7","onBackground":"#1b1c1b","surface":"#fbf9f7","surfaceDim":"#dbdad8","surfaceBright":"#fbf9f7","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f5f3f1","surfaceContainer":"#efeeeb","surfaceContainerHigh":"#e9e8e6","surfaceContainerHighest":"#e3e2e0","onSurface":"#1b1c1b","surfaceVariant":"#e3e2e0","onSurfaceVariant":"#464745","inverseSurface":"#30312f","inverseOnSurface":"#f2f0ee","outline":"#777775","outlineVariant":"#c7c6c4","shadow":"#000000","scrim":"#000000","surfaceTint":"#536259","primary":"#536259","onPrimary":"#ffffff","primaryContainer":"#d6e7db","onPrimaryContainer":"#3b4a41","inversePrimary":"#bacbbf","secondary":"#58605a","onSecondary":"#ffffff","secondaryContainer":"#dce5dd","onSecondaryContainer":"#404943","tertiary":"#4d6357","onTertiary":"#ffffff","tertiaryContainer":"#d0e8d8","onTertiaryContainer":"#364b40","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"green|neutral|dark":{"background":"#121413","onBackground":"#e3e2e0","surface":"#121413","surfaceDim":"#121413","surfaceBright":"#383938","surfaceContainerLowest":"#0d0e0d","surfaceContainerLow":"#1b1c1b","surfaceContainer":"#1f201f","surfaceContainerHigh":"#292a29","surfaceContainerHighest":"#343534","onSurface":"#e3e2e0","surfaceVariant":"#464745","onSurfaceVariant":"#c7c6c4","inverseSurface":"#e3e2e0","inverseOnSurface":"#30312f","outline":"#91918f","outlineVariant":"#464745","shadow":"#000000","scrim":"#000000","surfaceTint":"#bacbbf","primary":"#bacbbf","onPrimary":"#25342b","primaryContainer":"#3b4a41","onPrimaryContainer":"#d6e7db","inversePrimary":"#536259","secondary":"#c0c9c1","onSecondary":"#2a322d","secondaryContainer":"#404943","onSecondaryContainer":"#dce5dd","tertiary":"#b4ccbd","onTertiary":"#20352a","tertiaryContainer":"#364b40","onTertiaryContainer":"#d0e8d8","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"pink|vibrant|light":{"background":"#fff8f8","onBackground":"#25181d","surface":"#fff8f8","surfaceDim":"#ecd4db","surfaceBright":"#fff8f8","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#fff0f3","surfaceContainer":"#ffe8ee","surfaceContainerHigh":"#fbe2e9","surfaceContainerHighest":"#f5dce3","onSurface":"#25181d","surfaceVariant":"#f8dbe3","onSurfaceVariant":"#554148","inverseSurface":"#3b2c31","inverseOnSurface":"#ffecf1","outline":"#887178","outlineVariant":"#dbbfc8","shadow":"#000000","scrim":"#000000","surfaceTint":"#b7006e","primary":"#b7006e","onPrimary":"#ffffff","primaryContainer":"#ffd9e4","onPrimaryContainer":"#8c0053","inversePrimary":"#ffb0cd","secondary":"#805159","onSecondary":"#ffffff","secondaryContainer":"#ffd9de","onSecondaryContainer":"#653b42","tertiary":"#8b4d4a","onTertiary":"#ffffff","tertiaryContainer":"#ffdad7","onTertiaryContainer":"#6f3634","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"pink|vibrant|dark":{"background":"#1c1014","onBackground":"#f5dce3","surface":"#1c1014","surfaceDim":"#1c1014","surfaceBright":"#45353a","surfaceContainerLowest":"#170b0f","surfaceContainerLow":"#25181d","surfaceContainer":"#291c21","surfaceContainerHigh":"#35262b","surfaceContainerHighest":"#403136","onSurface":"#f5dce3","surfaceVariant":"#554148","onSurfaceVariant":"#dbbfc8","inverseSurface":"#f5dce3","inverseOnSurface":"#3b2c31","outline":"#a38a92","outlineVariant":"#554148","shadow":"#000000","scrim":"#000000","surfaceTint":"#ffb0cd","primary":"#ffb0cd","onPrimary":"#640039","primaryContainer":"#8c0053","onPrimaryContainer":"#ffd9e4","inversePrimary":"#b7006e","secondary":"#f2b7c0","onSecondary":"#4b252c","secondaryContainer":"#653b42","onSecondaryContainer":"#ffd9de","tertiary":"#ffb3af","onTertiary":"#53201f","tertiaryContainer":"#6f3634","onTertiaryContainer":"#ffdad7","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"pink|expressive|light":{"background":"#fff8f7","onBackground":"#24181a","surface":"#fff8f7","surfaceDim":"#ebd5d6","surfaceBright":"#fff8f7","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#fff0f0","surfaceContainer":"#ffe9ea","surfaceContainerHigh":"#f9e3e4","surfaceContainerHighest":"#f4ddde","onSurface":"#24181a","surfaceVariant":"#fbdbdd","onSurfaceVariant":"#574143","inverseSurface":"#3a2d2e","inverseOnSurface":"#ffeced","outline":"#8b7173","outlineVariant":"#debfc1","shadow":"#000000","scrim":"#000000","surfaceTint":"#00658b","primary":"#00658b","onPrimary":"#ffffff","primaryContainer":"#c5e7ff","onPrimaryContainer":"#004c6a","inversePrimary":"#86cffa","secondary":"#815340","onSecondary":"#ffffff","secondaryContainer":"#ffdbcd","onSecondaryContainer":"#663c2b","tertiary":"#5d6226","onTertiary":"#ffffff","tertiaryContainer":"#e2e89d","onTertiaryContainer":"#454a0f","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"pink|expressive|dark":{"background":"#1b1012","onBackground":"#f4ddde","surface":"#1b1012","surfaceDim":"#1b1012","surfaceBright":"#443637","surfaceContainerLowest":"#160b0d","surfaceContainerLow":"#24181a","surfaceContainer":"#291c1e","surfaceContainerHigh":"#342728","surfaceContainerHighest":"#3f3133","onSurface":"#f4ddde","surfaceVariant":"#574143","onSurfaceVariant":"#debfc1","inverseSurface":"#f4ddde","inverseOnSurface":"#3a2d2e","outline":"#a68a8c","outlineVariant":"#574143","shadow":"#000000","scrim":"#000000","surfaceTint":"#86cffa","primary":"#86cffa","onPrimary":"#00344a","primaryContainer":"#004c6a","onPrimaryContainer":"#c5e7ff","inversePrimary":"#00658b","secondary":"#f5b9a1","onSecondary":"#4b2717","secondaryContainer":"#663c2b","onSecondaryContainer":"#ffdbcd","tertiary":"#c6cc83","onTertiary":"#2f3300","tertiaryContainer":"#454a0f","onTertiaryContainer":"#e2e89d","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"pink|tonalSpot|light":{"background":"#fff8f8","onBackground":"#21191c","surface":"#fff8f8","surfaceDim":"#e6d6da","surfaceBright":"#fff8f8","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#fff0f3","surfaceContainer":"#faeaed","surfaceContainerHigh":"#f4e4e8","surfaceContainerHighest":"#eedfe2","onSurface":"#21191c","surfaceVariant":"#f2dee3","onSurfaceVariant":"#514347","inverseSurface":"#372e31","inverseOnSurface":"#fdedf0","outline":"#837378","outlineVariant":"#d5c2c7","shadow":"#000000","scrim":"#000000","surfaceTint":"#8a4a65","primary":"#8a4a65","onPrimary":"#ffffff","primaryContainer":"#ffd9e4","onPrimaryContainer":"#6e334d","inversePrimary":"#ffb0cd","secondary":"#735760","onSecondary":"#ffffff","secondaryContainer":"#ffd9e4","onSecondaryContainer":"#5a3f49","tertiary":"#7e5537","onTertiary":"#ffffff","tertiaryContainer":"#ffdcc4","onTertiaryContainer":"#633e22","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"pink|tonalSpot|dark":{"background":"#191114","onBackground":"#eedfe2","surface":"#191114","surfaceDim":"#191114","surfaceBright":"#40373a","surfaceContainerLowest":"#130c0f","surfaceContainerLow":"#21191c","surfaceContainer":"#261d20","surfaceContainerHigh":"#30282a","surfaceContainerHighest":"#3c3235","onSurface":"#eedfe2","surfaceVariant":"#514347","onSurfaceVariant":"#d5c2c7","inverseSurface":"#eedfe2","inverseOnSurface":"#372e31","outline":"#9d8c91","outlineVariant":"#514347","shadow":"#000000","scrim":"#000000","surfaceTint":"#ffb0cd","primary":"#ffb0cd","onPrimary":"#531d36","primaryContainer":"#6e334d","onPrimaryContainer":"#ffd9e4","inversePrimary":"#8a4a65","secondary":"#e1bdc9","onSecondary":"#412932","secondaryContainer":"#5a3f49","onSecondaryContainer":"#ffd9e4","tertiary":"#f1bc96","onTertiary":"#49290e","tertiaryContainer":"#633e22","onTertiaryContainer":"#ffdcc4","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"pink|neutral|light":{"background":"#fff8f8","onBackground":"#1e1b1b","surface":"#fff8f8","surfaceDim":"#e0d8d9","surfaceBright":"#fff8f8","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#faf2f2","surfaceContainer":"#f4ecec","surfaceContainerHigh":"#eee6e7","surfaceContainerHighest":"#e8e1e1","onSurface":"#1e1b1b","surfaceVariant":"#e8e1e1","onSurfaceVariant":"#4a4646","inverseSurface":"#332f30","inverseOnSurface":"#f7efef","outline":"#7b7676","outlineVariant":"#ccc5c5","shadow":"#000000","scrim":"#000000","surfaceTint":"#6e5960","primary":"#6e5960","onPrimary":"#ffffff","primaryContainer":"#f8dbe3","onPrimaryContainer":"#554148","inversePrimary":"#dbbfc8","secondary":"#695b5f","onSecondary":"#ffffff","secondaryContainer":"#f2dee3","onSecondaryContainer":"#514347","tertiary":"#735760","onTertiary":"#ffffff","tertiaryContainer":"#ffd9e4","onTertiaryContainer":"#5a3f49","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"pink|neutral|dark":{"background":"#151313","onBackground":"#e8e1e1","surface":"#151313","surfaceDim":"#151313","surfaceBright":"#3c3839","surfaceContainerLowest":"#100e0e","surfaceContainerLow":"#1e1b1b","surfaceContainer":"#221f1f","surfaceContainerHigh":"#2c292a","surfaceContainerHighest":"#373434","onSurface":"#e8e1e1","surfaceVariant":"#4a4646","onSurfaceVariant":"#ccc5c5","inverseSurface":"#e8e1e1","inverseOnSurface":"#332f30","outline":"#958f90","outlineVariant":"#4a4646","shadow":"#000000","scrim":"#000000","surfaceTint":"#dbbfc8","primary":"#dbbfc8","onPrimary":"#3d2b32","primaryContainer":"#554148","onPrimaryContainer":"#f8dbe3","inversePrimary":"#6e5960","secondary":"#d5c2c7","onSecondary":"#392d31","secondaryContainer":"#514347","onSecondaryContainer":"#f2dee3","tertiary":"#e1bdc9","onTertiary":"#412932","tertiaryContainer":"#5a3f49","onTertiaryContainer":"#ffd9e4","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"faircoin|vibrant|light":{"background":"#f6fce9","onBackground":"#181d12","surface":"#f6fce9","surfaceDim":"#d6dcca","surfaceBright":"#f6fce9","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f0f6e3","surfaceContainer":"#eaf0de","surfaceContainerHigh":"#e4ebd8","surfaceContainerHighest":"#dfe5d3","onSurface":"#181d12","surfaceVariant":"#dde6cf","onSurfaceVariant":"#414939","inverseSurface":"#2c3226","inverseOnSurface":"#edf3e1","outline":"#727a67","outlineVariant":"#c1cab4","shadow":"#000000","scrim":"#000000","surfaceTint":"#376b00","primary":"#376b00","onPrimary":"#ffffff","primaryContainer":"#8afe00","onPrimaryContainer":"#285000","inversePrimary":"#78df00","secondary":"#486549","onSecondary":"#ffffff","secondaryContainer":"#c9ebc7","onSecondaryContainer":"#314d33","tertiary":"#2f694e","onTertiary":"#ffffff","tertiaryContainer":"#b2f0ce","onTertiaryContainer":"#125138","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"faircoin|vibrant|dark":{"background":"#10150a","onBackground":"#dfe5d3","surface":"#10150a","surfaceDim":"#10150a","surfaceBright":"#353b2e","surfaceContainerLowest":"#0a1006","surfaceContainerLow":"#181d12","surfaceContainer":"#1c2116","surfaceContainerHigh":"#262c20","surfaceContainerHighest":"#31372a","onSurface":"#dfe5d3","surfaceVariant":"#414939","onSurfaceVariant":"#c1cab4","inverseSurface":"#dfe5d3","inverseOnSurface":"#2c3226","outline":"#8b9480","outlineVariant":"#414939","shadow":"#000000","scrim":"#000000","surfaceTint":"#78df00","primary":"#78df00","onPrimary":"#1a3700","primaryContainer":"#285000","onPrimaryContainer":"#8afe00","inversePrimary":"#376b00","secondary":"#aecfac","onSecondary":"#1a361e","secondaryContainer":"#314d33","onSecondaryContainer":"#c9ebc7","tertiary":"#97d4b3","onTertiary":"#003824","tertiaryContainer":"#125138","onTertiaryContainer":"#b2f0ce","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"faircoin|expressive|light":{"background":"#f4fcf0","onBackground":"#171d16","surface":"#f4fcf0","surfaceDim":"#d5dcd1","surfaceBright":"#f4fcf0","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#eff6ea","surfaceContainer":"#e9f0e4","surfaceContainerHigh":"#e3eadf","surfaceContainerHighest":"#dee5d9","onSurface":"#171d16","surfaceVariant":"#d9e6d5","onSurfaceVariant":"#3e4a3d","inverseSurface":"#2b322b","inverseOnSurface":"#ecf3e7","outline":"#6e7a6c","outlineVariant":"#bdcaba","shadow":"#000000","scrim":"#000000","surfaceTint":"#94474d","primary":"#94474d","onPrimary":"#ffffff","primaryContainer":"#ffdada","onPrimaryContainer":"#763037","inversePrimary":"#ffb2b6","secondary":"#45664c","onSecondary":"#ffffff","secondaryContainer":"#c7eccb","onSecondaryContainer":"#2e4e36","tertiary":"#22695b","onTertiary":"#ffffff","tertiaryContainer":"#abf0de","onTertiaryContainer":"#005144","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"faircoin|expressive|dark":{"background":"#0f150e","onBackground":"#dee5d9","surface":"#0f150e","surfaceDim":"#0f150e","surfaceBright":"#343b33","surfaceContainerLowest":"#091009","surfaceContainerLow":"#171d16","surfaceContainer":"#1b211a","surfaceContainerHigh":"#252c24","surfaceContainerHighest":"#30372f","onSurface":"#dee5d9","surfaceVariant":"#3e4a3d","onSurfaceVariant":"#bdcaba","inverseSurface":"#dee5d9","inverseOnSurface":"#2b322b","outline":"#879485","outlineVariant":"#3e4a3d","shadow":"#000000","scrim":"#000000","surfaceTint":"#ffb2b6","primary":"#ffb2b6","onPrimary":"#591a22","primaryContainer":"#763037","onPrimaryContainer":"#ffdada","inversePrimary":"#94474d","secondary":"#abd0b0","onSecondary":"#173721","secondaryContainer":"#2e4e36","onSecondaryContainer":"#c7eccb","tertiary":"#8fd4c2","onTertiary":"#00382e","tertiaryContainer":"#005144","onTertiaryContainer":"#abf0de","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"faircoin|tonalSpot|light":{"background":"#f9faef","onBackground":"#1a1d16","surface":"#f9faef","surfaceDim":"#d9dbd0","surfaceBright":"#f9faef","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f3f5ea","surfaceContainer":"#edefe4","surfaceContainerHigh":"#e7e9de","surfaceContainerHighest":"#e2e3d9","onSurface":"#1a1d16","surfaceVariant":"#e0e4d6","onSurfaceVariant":"#44483e","inverseSurface":"#2e312a","inverseOnSurface":"#f0f2e7","outline":"#74796d","outlineVariant":"#c4c8ba","shadow":"#000000","scrim":"#000000","surfaceTint":"#48672e","primary":"#48672e","onPrimary":"#ffffff","primaryContainer":"#c9eea7","onPrimaryContainer":"#314e19","inversePrimary":"#aed18d","secondary":"#56624a","onSecondary":"#ffffff","secondaryContainer":"#dae7c9","onSecondaryContainer":"#3f4a34","tertiary":"#386664","onTertiary":"#ffffff","tertiaryContainer":"#bbece9","onTertiaryContainer":"#1e4e4d","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"faircoin|tonalSpot|dark":{"background":"#11140e","onBackground":"#e2e3d9","surface":"#11140e","surfaceDim":"#11140e","surfaceBright":"#373a33","surfaceContainerLowest":"#0c0f09","surfaceContainerLow":"#1a1d16","surfaceContainer":"#1e211a","surfaceContainerHigh":"#282b24","surfaceContainerHighest":"#33362e","onSurface":"#e2e3d9","surfaceVariant":"#44483e","onSurfaceVariant":"#c4c8ba","inverseSurface":"#e2e3d9","inverseOnSurface":"#2e312a","outline":"#8e9286","outlineVariant":"#44483e","shadow":"#000000","scrim":"#000000","surfaceTint":"#aed18d","primary":"#aed18d","onPrimary":"#1c3704","primaryContainer":"#314e19","onPrimaryContainer":"#c9eea7","inversePrimary":"#48672e","secondary":"#becbae","onSecondary":"#29341f","secondaryContainer":"#3f4a34","onSecondaryContainer":"#dae7c9","tertiary":"#a0cfcd","onTertiary":"#003736","tertiaryContainer":"#1e4e4d","onTertiaryContainer":"#bbece9","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"},"faircoin|neutral|light":{"background":"#fbf9f5","onBackground":"#1b1c1a","surface":"#fbf9f5","surfaceDim":"#dcdad6","surfaceBright":"#fbf9f5","surfaceContainerLowest":"#ffffff","surfaceContainerLow":"#f6f3ef","surfaceContainer":"#f0edea","surfaceContainerHigh":"#eae8e4","surfaceContainerHighest":"#e4e2de","onSurface":"#1b1c1a","surfaceVariant":"#e4e2de","onSurfaceVariant":"#474744","inverseSurface":"#30302e","inverseOnSurface":"#f3f0ed","outline":"#787774","outlineVariant":"#c8c6c3","shadow":"#000000","scrim":"#000000","surfaceTint":"#596150","primary":"#596150","onPrimary":"#ffffff","primaryContainer":"#dde6cf","onPrimaryContainer":"#414939","inversePrimary":"#c1cab4","secondary":"#5b6055","onSecondary":"#ffffff","secondaryContainer":"#e0e4d6","onSecondaryContainer":"#44483e","tertiary":"#56624a","onTertiary":"#ffffff","tertiaryContainer":"#dae7c9","onTertiaryContainer":"#3f4a34","error":"#ba1a1a","onError":"#ffffff","errorContainer":"#ffdad6","onErrorContainer":"#93000a"},"faircoin|neutral|dark":{"background":"#131412","onBackground":"#e4e2de","surface":"#131412","surfaceDim":"#131412","surfaceBright":"#393937","surfaceContainerLowest":"#0e0e0d","surfaceContainerLow":"#1b1c1a","surfaceContainer":"#1f201e","surfaceContainerHigh":"#2a2a28","surfaceContainerHighest":"#353533","onSurface":"#e4e2de","surfaceVariant":"#474744","onSurfaceVariant":"#c8c6c3","inverseSurface":"#e4e2de","inverseOnSurface":"#30302e","outline":"#92918d","outlineVariant":"#474744","shadow":"#000000","scrim":"#000000","surfaceTint":"#c1cab4","primary":"#c1cab4","onPrimary":"#2b3324","primaryContainer":"#414939","onPrimaryContainer":"#dde6cf","inversePrimary":"#596150","secondary":"#c4c8ba","onSecondary":"#2d3228","secondaryContainer":"#44483e","onSecondaryContainer":"#e0e4d6","tertiary":"#becbae","onTertiary":"#29341f","tertiaryContainer":"#3f4a34","onTertiaryContainer":"#dae7c9","error":"#ffb4ab","onError":"#690005","errorContainer":"#93000a","onErrorContainer":"#ffdad6"}}
@@ -2,13 +2,12 @@ import goldenResolvedTokens from './__fixtures__/golden-resolved-tokens.json';
2
2
  import { APP_COLOR_NAMES } from '../color-presets';
3
3
  import { getResolvedTokens } from '../token-registry';
4
4
 
5
- // `golden-resolved-tokens.json` is the FROZEN parity oracle: the exact resolved
6
- // rgb for every preset×mode×token that the legacy resolved-color pipeline
7
- // produced (captured once, each resolved-color key remapped to its base `--X`).
8
- // It is IMMUTABLE never regenerate it to chase new output. These tests prove
9
- // the canonical `getResolvedTokens` pipeline reproduces that palette EXACTLY
10
- // (byte-for-byte rgb), so the refactor to a single rgb token pipeline does not
11
- // shift a single color.
5
+ // `golden-resolved-tokens.json` is the frozen palette oracle: the exact resolved
6
+ // rgb for every preset×mode×token. It was DELIBERATELY REBASED when the palette
7
+ // moved from hand-authored HSL triples to the colour engine (Phase 5) — the
8
+ // palette change is the deliverable. It is frozen going FORWARD: it now guards
9
+ // against accidental drift of the engine-backed output, so these tests still
10
+ // prove `getResolvedTokens` reproduces the committed palette byte-for-byte.
12
11
  const fixture = goldenResolvedTokens as Record<string, Record<string, string>>;
13
12
 
14
13
  describe('golden token parity (frozen oracle)', () => {
@@ -44,18 +44,18 @@ describe('web var(--primary) resolves to a real color (not a bare triple)', () =
44
44
  });
45
45
  });
46
46
 
47
- it('restored legacy alias invariants hold', () => {
48
- // Pre-0.8.0 semantics, restored in 0.8.1: `secondary` mirrors `primary`,
49
- // `card` and `primaryLight` are the page surface, `primaryDark` is the page
50
- // background. All read straight from the same resolved rgb token map, so the
51
- // alias values are byte-identical to their source tokens.
47
+ it('engine-backed alias invariants hold', () => {
48
+ // Phase 5 contract: `secondary` is a REAL contrast color (engine secondary
49
+ // role, no longer a mirror of primary), `card` is the lightest surface (engine
50
+ // `surfaceContainerLowest`, surfaced as `--card`). The legacy `primaryLight`
51
+ // (page surface tint) and `primaryDark` (page background) aliases are retained.
52
52
  for (const preset of ['oxy', 'blue'] as const) {
53
53
  for (const mode of ['light', 'dark'] as const) {
54
54
  const { colors } = buildTheme(preset, mode);
55
55
  const t = getResolvedTokens(preset, mode);
56
- expect(colors.secondary).toBe(colors.primary);
57
- expect(colors.secondary).toBe(t['--primary']);
58
- expect(colors.card).toBe(t['--surface']);
56
+ expect(colors.secondary).not.toBe(colors.primary);
57
+ expect(colors.secondary).toBe(t['--secondary']);
58
+ expect(colors.card).toBe(t['--card']);
59
59
  expect(colors.primaryLight).toBe(t['--surface']);
60
60
  expect(colors.primaryDark).toBe(t['--background']);
61
61
  }
@@ -5,18 +5,16 @@
5
5
  import normalizeColor from '@react-native/normalize-colors';
6
6
 
7
7
  import { buildTheme } from '../build-theme';
8
- import type { AppColorName } from '../color-presets';
8
+ import { getPresetVars } from '../preset-vars';
9
+ import { APP_COLOR_NAMES, type AppColorName } from '../color-presets';
9
10
  import type { ThemeColors } from '../types';
10
11
 
11
- // Parity guard for the 0.8.1 restoration of the pre-0.8.0 `theme.colors`
12
- // derived/alias values. 0.8.0 changed several derived/alias mappings, visibly
13
- // altering dark (and light) mode for consuming components. 0.8.1 restores the
14
- // EXACT pre-0.8.0 colors while keeping the single rgb pipeline (the old logic
15
- // emitted `hsl(h,s%,l%)` strings; the new logic emits the `rgb(...)` of the same
16
- // color). These oracles are the VERBATIM pre-0.8.0 outputs (commit 00a4cf3:
17
- // `git show 00a4cf3:src/theme/build-theme.ts`). We assert visual equality via
18
- // ΔE on the parsed sRGB — `hsl(...)` and the matching `rgb(...)` parse to the
19
- // same bytes under the RN/RNW/browser `normalizeColor`, so ΔE is 0.
12
+ // Phase 5 rebased the palette from hand-authored HSL triples to the colour
13
+ // engine. The pre-0.8.0 `theme.colors` parity oracles (which froze
14
+ // `secondary === primary` and `card === surface`) are intentionally OBSOLETE
15
+ // those two aliases are the very bugs this phase fixes. This suite now guards the
16
+ // NEW engine-backed contract: every value parses, and the two fixes hold for
17
+ // every preset in both modes.
20
18
 
21
19
  type Rgb = { r: number; g: number; b: number };
22
20
 
@@ -29,87 +27,45 @@ function parse(color: string): Rgb {
29
27
  return { r: (n >>> 24) & 0xff, g: (n >>> 16) & 0xff, b: (n >>> 8) & 0xff };
30
28
  }
31
29
 
32
- function deltaE(a: Rgb, b: Rgb): number {
33
- return Math.sqrt((a.r - b.r) ** 2 + (a.g - b.g) ** 2 + (a.b - b.b) ** 2) / Math.sqrt(3);
34
- }
30
+ const lightness = ({ r, g, b }: Rgb): number => r + g + b;
35
31
 
36
- /**
37
- * The pre-0.8.0 `theme.colors` for a (preset, mode) pair, expressed in the EXACT
38
- * source forms commit 00a4cf3 produced — token-backed values as `hsl(H, S%, L%)`
39
- * (from the preset's raw triples) and computed tints via `hsl(hue, s%, l%)`.
40
- *
41
- * We only assert the fields the restoration touches plus the load-bearing
42
- * aliases (primaryLight/primaryDark/secondary/card). Status colors and
43
- * shadow/overlay were never changed, so they are intentionally omitted.
44
- */
45
- type Oracle = Partial<Record<keyof ThemeColors, string>>;
32
+ describe.each(APP_COLOR_NAMES)('engine-backed theme.colors (%s)', (preset: AppColorName) => {
33
+ it.each(['light', 'dark'] as const)('%s: every color value parses', (mode) => {
34
+ const { colors } = buildTheme(preset, mode);
35
+ for (const value of Object.values(colors)) {
36
+ expect(normalizeColor(value)).not.toBeNull();
37
+ }
38
+ });
46
39
 
47
- const ORACLES: Array<{ preset: AppColorName; mode: 'light' | 'dark'; old: Oracle }> = [
48
- {
49
- // blue: primary 205 87% 53%, surface dark 205 20% 18%, background dark 205 50% 5%, destructive hue 0.
50
- preset: 'blue',
51
- mode: 'dark',
52
- old: {
53
- secondary: 'hsl(205, 87%, 53%)', // == primary
54
- primary: 'hsl(205, 87%, 53%)',
55
- card: 'hsl(205, 20%, 18%)', // == surface
56
- primaryLight: 'hsl(205, 20%, 18%)', // == surface
57
- primaryDark: 'hsl(205, 50%, 5%)', // == background
58
- primarySubtle: 'hsl(205, 50%, 10%)',
59
- primarySubtleForeground: 'hsl(205, 70%, 65%)',
60
- negative: 'hsl(0, 84%, 45%)',
61
- negativeForeground: '#FFFFFF',
62
- negativeSubtle: 'hsl(0, 50%, 10%)',
63
- negativeSubtleForeground: 'hsl(0, 70%, 65%)',
64
- contrast50: 'hsl(205, 15%, 12%)',
65
- },
66
- },
67
- {
68
- // oxy: primary 277 66% 56%, surface dark 277 20% 18%, background dark 277 50% 5%, destructive hue 0.
69
- preset: 'oxy',
70
- mode: 'dark',
71
- old: {
72
- secondary: 'hsl(277, 66%, 56%)',
73
- primary: 'hsl(277, 66%, 56%)',
74
- card: 'hsl(277, 20%, 18%)',
75
- primaryLight: 'hsl(277, 20%, 18%)',
76
- primaryDark: 'hsl(277, 50%, 5%)',
77
- primarySubtle: 'hsl(277, 50%, 10%)',
78
- primarySubtleForeground: 'hsl(277, 70%, 65%)',
79
- negative: 'hsl(0, 84%, 45%)',
80
- negativeForeground: '#FFFFFF',
81
- negativeSubtle: 'hsl(0, 50%, 10%)',
82
- negativeSubtleForeground: 'hsl(0, 70%, 65%)',
83
- contrast50: 'hsl(277, 15%, 12%)',
84
- },
85
- },
86
- {
87
- // blue light: surface 205 58% 94%, background 205 55% 96%.
88
- preset: 'blue',
89
- mode: 'light',
90
- old: {
91
- secondary: 'hsl(205, 87%, 53%)',
92
- primary: 'hsl(205, 87%, 53%)',
93
- card: 'hsl(205, 58%, 94%)',
94
- primaryLight: 'hsl(205, 58%, 94%)',
95
- primaryDark: 'hsl(205, 55%, 96%)',
96
- primarySubtle: 'hsl(205, 70%, 93%)',
97
- primarySubtleForeground: 'hsl(205, 90%, 25%)',
98
- negative: 'hsl(0, 84%, 45%)',
99
- negativeForeground: '#FFFFFF',
100
- negativeSubtle: 'hsl(0, 90%, 95%)',
101
- negativeSubtleForeground: 'hsl(0, 80%, 40%)',
102
- contrast50: 'hsl(205, 10%, 93%)',
103
- },
104
- },
105
- ];
40
+ it('secondary is a real contrast color, not a mirror of primary', () => {
41
+ for (const mode of ['light', 'dark'] as const) {
42
+ const { colors } = buildTheme(preset, mode);
43
+ expect(colors.secondary).not.toBe(colors.primary);
44
+ }
45
+ });
46
+
47
+ it('card is lighter than background in light, darker (or equal) in dark', () => {
48
+ const light = buildTheme(preset, 'light').colors;
49
+ expect(lightness(parse(light.card))).toBeGreaterThan(lightness(parse(light.background)));
106
50
 
107
- describe.each(ORACLES)('theme.colors parity with pre-0.8.0 ($preset/$mode)', ({ preset, mode, old }) => {
108
- const { colors } = buildTheme(preset, mode);
51
+ const dark = buildTheme(preset, 'dark').colors;
52
+ expect(lightness(parse(dark.card))).toBeLessThanOrEqual(lightness(parse(dark.background)));
53
+ });
109
54
 
110
- it.each(Object.entries(old))('%s matches the pre-0.8.0 color (ΔE 1)', (key, oldColor) => {
111
- const newColor = colors[key as keyof ThemeColors];
112
- const dE = deltaE(parse(oldColor), parse(newColor));
113
- expect(dE).toBeLessThanOrEqual(1);
55
+ it('token-backed fields read straight from the resolved token map', () => {
56
+ for (const mode of ['light', 'dark'] as const) {
57
+ const { colors } = buildTheme(preset, mode);
58
+ const t = getPresetVars(preset, mode);
59
+ const field: Array<[keyof ThemeColors, string]> = [
60
+ ['primary', '--primary'],
61
+ ['secondary', '--secondary'],
62
+ ['card', '--card'],
63
+ ['background', '--background'],
64
+ ['border', '--border'],
65
+ ];
66
+ for (const [colorKey, tokenKey] of field) {
67
+ expect(colors[colorKey]).toBe(t[tokenKey]);
68
+ }
69
+ }
114
70
  });
115
71
  });
@@ -1,7 +1,8 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { APP_COLOR_PRESETS, type AppColorName } from './color-presets';
3
+ import { generateRoleColors, type RoleColors } from './color-engine';
3
4
  import { getAdaptiveColors } from './adaptive-colors';
4
- import { getResolvedTokens, hslToSrgb } from './token-registry';
5
+ import { getResolvedTokens } from './token-registry';
5
6
  import { THEME_GRADIENTS } from './gradients';
6
7
  import type { Theme, ThemeColors } from './types';
7
8
 
@@ -17,29 +18,16 @@ export const STATUS_COLORS = {
17
18
  } as const;
18
19
 
19
20
  /**
20
- * Extract the integer hue from a shadcn-style raw HSL triple (`'H S% L%'`,
21
- * optionally with an alpha tail). Used to seed the computed brand tints below
22
- * from the preset's primary/destructive hue, exactly as pre-0.8.0.
23
- */
24
- function extractHue(triple: string): number {
25
- const first = (triple.split(/\s+/)[0] ?? '0').replace(/deg$/i, '');
26
- const hue = parseInt(first, 10);
27
- return Number.isFinite(hue) ? hue : 0;
28
- }
29
-
30
- /**
31
- * Build the JS `theme.colors` object from the SAME canonical rgb token source
32
- * the web/native CSS-var writes use (`getResolvedTokens`). JS styles and the
33
- * `var(--x)` document tokens therefore share one rgb pipeline — no second,
34
- * drift-prone HSL conversion lives here.
21
+ * Build the JS `theme.colors` object from the SAME engine-derived colours the
22
+ * web/native CSS-var writes use. Token-backed fields read from the resolved rgb
23
+ * token map (`getResolvedTokens`, which sources from the engine via
24
+ * `getPresetVars`); the brand-tint family (primarySubtle/negative/…) reads the
25
+ * engine's container/error roles directly. JS styles and the `var(--x)` document
26
+ * tokens therefore share one engine-backed pipeline — no second HSL conversion.
35
27
  *
36
- * Token-backed values read straight from the resolved rgb map via `g(...)`.
37
- * The brand tints (primarySubtle/negative/contrast50/...) reproduce the EXACT
38
- * pre-0.8.0 colors computed from the preset's primary/destructive HUE with
39
- * the historical `hsl(h, s%, l%)` math — but are emitted as `rgb(...)` by
40
- * routing the triple through the registry's `hslToSrgb`, so the single-format
41
- * pipeline is preserved. (`hsl(h,s%,l%)` and the `rgb(...)` of the same color
42
- * are byte-identical under the RN/RNW/browser parser.)
28
+ * The two intentional fixes land here too: `card` is now the lightest surface
29
+ * (`--card` engine `surfaceContainerLowest`) and `secondary` is a real
30
+ * contrast colour (engine `secondary` role), not a mirror of `primary`.
43
31
  */
44
32
  function buildColorsFromPreset(
45
33
  preset: AppColorName,
@@ -51,24 +39,23 @@ function buildColorsFromPreset(
51
39
  // Read a resolved `rgb(...)` token by its bare name (no leading `--`).
52
40
  const g = (k: string): string => t[`--${k}`] ?? 'rgb(0 0 0)';
53
41
 
54
- // Convert a computed `H S% L%` triple to the canonical `rgb(...)` via the
55
- // registry's single HSL→sRGB conversion (matches the old `hsl(h,s%,l%)`).
56
- const hslRgb = (h: number, s: number, l: number): string => hslToSrgb(`${h} ${s}% ${l}%`);
57
-
58
- // Brand hues seed the computed tints from the preset's raw HSL triples,
59
- // exactly as the pre-0.8.0 logic did.
60
- const presetTokens = isDark ? APP_COLOR_PRESETS[preset].dark : APP_COLOR_PRESETS[preset].light;
61
- const pHue = extractHue(presetTokens['--primary'] ?? '0 0% 50%');
62
- const dHue = extractHue(presetTokens['--destructive'] ?? '0 0% 0%');
42
+ // The full engine role set for fields that map to roles Bloom's canonical
43
+ // token set does not surface (containers, error family).
44
+ const config = APP_COLOR_PRESETS[preset];
45
+ const r: RoleColors = generateRoleColors({
46
+ seed: config.hex,
47
+ variant: config.variant,
48
+ isDark,
49
+ });
63
50
 
64
51
  return {
65
52
  background: g('background'),
66
53
  backgroundSecondary: g('surface'),
67
- backgroundTertiary: g('muted'),
54
+ backgroundTertiary: g('popover'),
68
55
 
69
56
  text: g('foreground'),
70
57
  textSecondary: g('muted-foreground'),
71
- textTertiary: g('muted-foreground'),
58
+ textTertiary: r.outline,
72
59
 
73
60
  border: g('border'),
74
61
  borderLight: g('input'),
@@ -80,8 +67,8 @@ function buildColorsFromPreset(
80
67
  primaryLight: g('surface'),
81
68
  primaryDark: g('background'),
82
69
 
83
- // `secondary` historically mirrors `primary`. Retained for compatibility.
84
- secondary: g('primary'),
70
+ // The FIX: `secondary` is now a real contrast colour (engine `secondary`).
71
+ secondary: g('secondary'),
85
72
 
86
73
  tint: g('primary'),
87
74
  icon: g('muted-foreground'),
@@ -89,15 +76,16 @@ function buildColorsFromPreset(
89
76
 
90
77
  ...STATUS_COLORS,
91
78
 
92
- primarySubtle: isDark ? hslRgb(pHue, 50, 10) : hslRgb(pHue, 70, 93),
93
- primarySubtleForeground: isDark ? hslRgb(pHue, 70, 65) : hslRgb(pHue, 90, 25),
94
- negative: hslRgb(dHue, 84, 45),
95
- negativeForeground: '#FFFFFF',
96
- negativeSubtle: isDark ? hslRgb(dHue, 50, 10) : hslRgb(dHue, 90, 95),
97
- negativeSubtleForeground: isDark ? hslRgb(dHue, 70, 65) : hslRgb(dHue, 80, 40),
98
- contrast50: isDark ? hslRgb(pHue, 15, 12) : hslRgb(pHue, 10, 93),
79
+ primarySubtle: r.primaryContainer,
80
+ primarySubtleForeground: r.onPrimaryContainer,
81
+ negative: r.error,
82
+ negativeForeground: r.onError,
83
+ negativeSubtle: r.errorContainer,
84
+ negativeSubtleForeground: r.onErrorContainer,
85
+ contrast50: g('muted'),
99
86
 
100
- card: g('surface'),
87
+ // The FIX: `card` is the lightest surface (engine `surfaceContainerLowest`).
88
+ card: g('card'),
101
89
  shadow: isDark ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.1)',
102
90
  overlay: 'rgba(0, 0, 0, 0.5)',
103
91
  };