@clhaas/palette-kit 0.3.0 → 0.4.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 (312) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +80 -87
  3. package/dist/contrast/contrast.d.ts +16 -0
  4. package/dist/contrast/contrast.js +102 -0
  5. package/dist/core/intent-registry.d.ts +11 -0
  6. package/dist/core/intent-registry.js +70 -0
  7. package/dist/core/oklch.d.ts +16 -0
  8. package/dist/core/oklch.js +56 -0
  9. package/dist/create-palette-kit.d.ts +9 -0
  10. package/dist/create-palette-kit.js +67 -0
  11. package/dist/engine/context/context.d.ts +13 -0
  12. package/dist/engine/context/context.js +37 -0
  13. package/dist/engine/level/curves.d.ts +17 -0
  14. package/dist/engine/level/curves.js +49 -0
  15. package/dist/engine/level/level.d.ts +4 -0
  16. package/dist/engine/level/level.js +13 -0
  17. package/dist/engine/relation/relation.d.ts +105 -0
  18. package/dist/engine/relation/relation.js +137 -0
  19. package/dist/engine/resolve/resolve.d.ts +36 -0
  20. package/dist/engine/resolve/resolve.js +116 -0
  21. package/dist/engine/state/state.d.ts +46 -0
  22. package/dist/engine/state/state.js +68 -0
  23. package/dist/engine/usage/fill.d.ts +9 -0
  24. package/dist/engine/usage/fill.js +9 -0
  25. package/dist/engine/usage/lines.d.ts +9 -0
  26. package/dist/engine/usage/lines.js +9 -0
  27. package/dist/engine/usage/overlays.d.ts +9 -0
  28. package/dist/engine/usage/overlays.js +9 -0
  29. package/dist/engine/usage/strategy.d.ts +56 -0
  30. package/dist/engine/usage/strategy.js +30 -0
  31. package/dist/engine/usage/visualVocabulary.d.ts +9 -0
  32. package/dist/engine/usage/visualVocabulary.js +9 -0
  33. package/dist/export/serialize.d.ts +14 -0
  34. package/dist/export/serialize.js +89 -0
  35. package/dist/export/types.d.ts +37 -0
  36. package/dist/export/types.js +31 -0
  37. package/dist/index.d.ts +3 -3
  38. package/dist/index.js +2 -2
  39. package/dist/operators/convert.d.ts +32 -0
  40. package/dist/operators/convert.js +80 -0
  41. package/dist/presets/presets.d.ts +95 -0
  42. package/dist/presets/presets.js +308 -0
  43. package/dist/types/index.d.ts +111 -187
  44. package/dist/utils/errors/errors.d.ts +17 -0
  45. package/dist/utils/errors/errors.js +22 -0
  46. package/docs/API.md +167 -0
  47. package/docs/Alpha.md +14 -0
  48. package/docs/Architecture.md +56 -0
  49. package/docs/CLI.md +22 -0
  50. package/docs/Concepts.md +73 -0
  51. package/docs/Config.md +144 -0
  52. package/docs/Diagnostics.md +22 -0
  53. package/docs/Exporters.md +33 -0
  54. package/docs/FAQ.md +59 -0
  55. package/docs/Migration.md +61 -0
  56. package/docs/Overlays.md +33 -0
  57. package/docs/README.md +60 -0
  58. package/docs/Text.md +41 -0
  59. package/docs/Tokens.md +42 -0
  60. package/docs/Usage-JSON.md +39 -0
  61. package/docs/Usage-ReactNative.md +63 -0
  62. package/docs/Usage-Web.md +66 -0
  63. package/docs/Validation.md +97 -0
  64. package/docs/Why.md +37 -0
  65. package/docs/_api-surface.md +53 -0
  66. package/docs/snippets/serialize-oklch.md +9 -0
  67. package/docs/spec.md +98 -0
  68. package/package.json +74 -59
  69. package/.codex/skills/color-pipeline-implementer/SKILL.md +0 -23
  70. package/.codex/skills/commit-message-crafter/SKILL.md +0 -63
  71. package/.codex/skills/commit-message-crafter/references/benchmarks.md +0 -20
  72. package/.codex/skills/contrast-solver-helper/SKILL.md +0 -20
  73. package/.codex/skills/exporters-builder/SKILL.md +0 -20
  74. package/.codex/skills/markdownlint-writer/SKILL.md +0 -32
  75. package/.codex/skills/phase-implementation-runbook/SKILL.md +0 -92
  76. package/.codex/skills/type-contract-auditor/SKILL.md +0 -21
  77. package/.github/skills/review-guide/SKILL.md +0 -23
  78. package/.github/skills/review-guide/references/review-guide-v0.3.md +0 -629
  79. package/.markdownlint.json +0 -4
  80. package/AGENTS.md +0 -16
  81. package/biome.json +0 -43
  82. package/dist/cli/args.d.ts +0 -12
  83. package/dist/cli/args.js +0 -56
  84. package/dist/cli/args.test.d.ts +0 -1
  85. package/dist/cli/args.test.js +0 -22
  86. package/dist/cli/codegen/__snapshots__/tokens.test.js.snap +0 -87
  87. package/dist/cli/codegen/tokens.d.ts +0 -12
  88. package/dist/cli/codegen/tokens.js +0 -139
  89. package/dist/cli/codegen/tokens.test.d.ts +0 -1
  90. package/dist/cli/codegen/tokens.test.js +0 -51
  91. package/dist/cli/config.d.ts +0 -40
  92. package/dist/cli/config.js +0 -34
  93. package/dist/cli/validate.d.ts +0 -2
  94. package/dist/cli/validate.js +0 -33
  95. package/dist/cli/validate.test.d.ts +0 -1
  96. package/dist/cli/validate.test.js +0 -40
  97. package/dist/cli.d.ts +0 -2
  98. package/dist/cli.js +0 -148
  99. package/dist/contrast/apca.d.ts +0 -2
  100. package/dist/contrast/apca.js +0 -15
  101. package/dist/contrast/apca.test.d.ts +0 -1
  102. package/dist/contrast/apca.test.js +0 -16
  103. package/dist/contrast/index.d.ts +0 -4
  104. package/dist/contrast/index.js +0 -4
  105. package/dist/contrast/scoring.d.ts +0 -4
  106. package/dist/contrast/scoring.js +0 -31
  107. package/dist/contrast/scoring.test.d.ts +0 -1
  108. package/dist/contrast/scoring.test.js +0 -148
  109. package/dist/contrast/solver.d.ts +0 -13
  110. package/dist/contrast/solver.js +0 -170
  111. package/dist/contrast/solver.test.d.ts +0 -1
  112. package/dist/contrast/solver.test.js +0 -75
  113. package/dist/contrast/types.d.ts +0 -17
  114. package/dist/contrast/types.js +0 -1
  115. package/dist/contrast/utils.d.ts +0 -4
  116. package/dist/contrast/utils.js +0 -18
  117. package/dist/contrast/wcag2.d.ts +0 -3
  118. package/dist/contrast/wcag2.js +0 -19
  119. package/dist/contrast/wcag2.test.d.ts +0 -1
  120. package/dist/contrast/wcag2.test.js +0 -17
  121. package/dist/core/createTheme.d.ts +0 -35
  122. package/dist/core/createTheme.js +0 -24
  123. package/dist/core/dx-helpers.test.d.ts +0 -1
  124. package/dist/core/dx-helpers.test.js +0 -61
  125. package/dist/core/index.d.ts +0 -2
  126. package/dist/core/index.js +0 -2
  127. package/dist/core/onSolid.test.d.ts +0 -1
  128. package/dist/core/onSolid.test.js +0 -118
  129. package/dist/core/qa.v1.test.d.ts +0 -1
  130. package/dist/core/qa.v1.test.js +0 -112
  131. package/dist/core/resolve.d.ts +0 -3
  132. package/dist/core/resolve.js +0 -8
  133. package/dist/core/resolve.test.d.ts +0 -1
  134. package/dist/core/resolve.test.js +0 -89
  135. package/dist/core/resolveMany.d.ts +0 -8
  136. package/dist/core/resolveMany.js +0 -17
  137. package/dist/core/tokenRegistry.d.ts +0 -23
  138. package/dist/core/tokenRegistry.js +0 -83
  139. package/dist/core/tokenRegistry.test.d.ts +0 -1
  140. package/dist/core/tokenRegistry.test.js +0 -133
  141. package/dist/engine/applyOperators.d.ts +0 -3
  142. package/dist/engine/applyOperators.js +0 -23
  143. package/dist/engine/context.d.ts +0 -4
  144. package/dist/engine/context.js +0 -1
  145. package/dist/engine/gamut.d.ts +0 -13
  146. package/dist/engine/gamut.js +0 -101
  147. package/dist/engine/gamut.test.d.ts +0 -1
  148. package/dist/engine/gamut.test.js +0 -23
  149. package/dist/engine/generateScale.d.ts +0 -15
  150. package/dist/engine/generateScale.js +0 -29
  151. package/dist/engine/generateScale.test.d.ts +0 -1
  152. package/dist/engine/generateScale.test.js +0 -32
  153. package/dist/engine/index.d.ts +0 -8
  154. package/dist/engine/index.js +0 -4
  155. package/dist/engine/normalize.d.ts +0 -43
  156. package/dist/engine/normalize.js +0 -403
  157. package/dist/engine/normalize.test.d.ts +0 -1
  158. package/dist/engine/normalize.test.js +0 -136
  159. package/dist/engine/onSolid.d.ts +0 -3
  160. package/dist/engine/onSolid.js +0 -110
  161. package/dist/engine/resolveBaseColor.d.ts +0 -25
  162. package/dist/engine/resolveBaseColor.js +0 -127
  163. package/dist/engine/resolveBaseColor.test.d.ts +0 -1
  164. package/dist/engine/resolveBaseColor.test.js +0 -97
  165. package/dist/export/__snapshots__/exportTheme.test.js.snap +0 -74
  166. package/dist/export/exportTheme.d.ts +0 -47
  167. package/dist/export/exportTheme.js +0 -170
  168. package/dist/export/exportTheme.test.d.ts +0 -1
  169. package/dist/export/exportTheme.test.js +0 -118
  170. package/dist/export/index.d.ts +0 -1
  171. package/dist/export/index.js +0 -1
  172. package/dist/export/serializeColor.d.ts +0 -1
  173. package/dist/export/serializeColor.js +0 -1
  174. package/dist/export/serializeColor.test.d.ts +0 -1
  175. package/dist/export/serializeColor.test.js +0 -54
  176. package/dist/export.d.ts +0 -1
  177. package/dist/export.js +0 -1
  178. package/dist/operators/emphasis.d.ts +0 -3
  179. package/dist/operators/emphasis.js +0 -113
  180. package/dist/operators/emphasis.test.d.ts +0 -1
  181. package/dist/operators/emphasis.test.js +0 -69
  182. package/dist/operators/index.d.ts +0 -3
  183. package/dist/operators/index.js +0 -2
  184. package/dist/operators/state.d.ts +0 -3
  185. package/dist/operators/state.js +0 -102
  186. package/dist/operators/state.test.d.ts +0 -1
  187. package/dist/operators/state.test.js +0 -48
  188. package/dist/operators/types.d.ts +0 -13
  189. package/dist/operators/types.js +0 -1
  190. package/dist/operators/utils.d.ts +0 -16
  191. package/dist/operators/utils.js +0 -23
  192. package/dist/presets/curves.d.ts +0 -28
  193. package/dist/presets/curves.js +0 -145
  194. package/dist/presets/index.d.ts +0 -2
  195. package/dist/presets/index.js +0 -1
  196. package/dist/presets/tokens/index.d.ts +0 -3
  197. package/dist/presets/tokens/index.js +0 -3
  198. package/dist/presets/tokens/minimal-ui.d.ts +0 -6
  199. package/dist/presets/tokens/minimal-ui.js +0 -53
  200. package/dist/presets/tokens/modern-ui.d.ts +0 -5
  201. package/dist/presets/tokens/modern-ui.js +0 -83
  202. package/dist/presets/tokens/presets.test.d.ts +0 -1
  203. package/dist/presets/tokens/presets.test.js +0 -31
  204. package/dist/presets/tokens/radixLike-ui.d.ts +0 -6
  205. package/dist/presets/tokens/radixLike-ui.js +0 -77
  206. package/dist/serialize/index.d.ts +0 -1
  207. package/dist/serialize/index.js +0 -1
  208. package/dist/serialize/normalizeOutput.d.ts +0 -6
  209. package/dist/serialize/normalizeOutput.js +0 -45
  210. package/dist/serialize/serializeColor.d.ts +0 -21
  211. package/dist/serialize/serializeColor.js +0 -178
  212. package/dist/serialize/serializeResolved.test.d.ts +0 -1
  213. package/dist/serialize/serializeResolved.test.js +0 -45
  214. package/dist/serialize.d.ts +0 -1
  215. package/dist/serialize.js +0 -1
  216. package/dist/utils/clamp.d.ts +0 -1
  217. package/dist/utils/clamp.js +0 -1
  218. package/dist/utils/index.d.ts +0 -1
  219. package/dist/utils/index.js +0 -1
  220. package/dist/utils/lerp.d.ts +0 -1
  221. package/dist/utils/lerp.js +0 -1
  222. package/dist/utils/parseColor.d.ts +0 -6
  223. package/dist/utils/parseColor.js +0 -67
  224. package/dist/utils/parseColor.test.d.ts +0 -1
  225. package/dist/utils/parseColor.test.js +0 -51
  226. package/dist/utils/smoothstep.d.ts +0 -1
  227. package/dist/utils/smoothstep.js +0 -5
  228. package/planning/phase-10-review.md +0 -550
  229. package/planning/phase-7-review.md +0 -411
  230. package/planning/phase-8-review.md +0 -669
  231. package/planning/phase-9-review.md +0 -564
  232. package/planning/roadmap-v0.3.md +0 -284
  233. package/planning/spec-serializer-v0.3.md +0 -324
  234. package/planning/spec-v0.3.md +0 -305
  235. package/src/cli/args.test.ts +0 -28
  236. package/src/cli/args.ts +0 -66
  237. package/src/cli/codegen/__snapshots__/tokens.test.ts.snap +0 -87
  238. package/src/cli/codegen/tokens.test.ts +0 -61
  239. package/src/cli/codegen/tokens.ts +0 -191
  240. package/src/cli/config.ts +0 -71
  241. package/src/cli/validate.test.ts +0 -49
  242. package/src/cli/validate.ts +0 -38
  243. package/src/cli.ts +0 -183
  244. package/src/contrast/apca.test.ts +0 -20
  245. package/src/contrast/apca.ts +0 -26
  246. package/src/contrast/index.ts +0 -4
  247. package/src/contrast/scoring.test.ts +0 -188
  248. package/src/contrast/scoring.ts +0 -48
  249. package/src/contrast/solver.test.ts +0 -147
  250. package/src/contrast/solver.ts +0 -235
  251. package/src/contrast/types.ts +0 -20
  252. package/src/contrast/utils.ts +0 -28
  253. package/src/contrast/wcag2.test.ts +0 -21
  254. package/src/contrast/wcag2.ts +0 -24
  255. package/src/core/createTheme.ts +0 -78
  256. package/src/core/dx-helpers.test.ts +0 -82
  257. package/src/core/index.ts +0 -7
  258. package/src/core/onSolid.test.ts +0 -146
  259. package/src/core/qa.v1.test.ts +0 -149
  260. package/src/core/resolve.test.ts +0 -99
  261. package/src/core/resolve.ts +0 -11
  262. package/src/core/resolveMany.ts +0 -22
  263. package/src/core/tokenRegistry.test.ts +0 -153
  264. package/src/core/tokenRegistry.ts +0 -114
  265. package/src/engine/applyOperators.ts +0 -32
  266. package/src/engine/context.ts +0 -8
  267. package/src/engine/gamut.test.ts +0 -30
  268. package/src/engine/gamut.ts +0 -144
  269. package/src/engine/generateScale.test.ts +0 -46
  270. package/src/engine/generateScale.ts +0 -48
  271. package/src/engine/index.ts +0 -8
  272. package/src/engine/normalize.test.ts +0 -222
  273. package/src/engine/normalize.ts +0 -550
  274. package/src/engine/onSolid.ts +0 -178
  275. package/src/engine/resolveBaseColor.test.ts +0 -117
  276. package/src/engine/resolveBaseColor.ts +0 -203
  277. package/src/export/__snapshots__/exportTheme.test.ts.snap +0 -74
  278. package/src/export/exportTheme.test.ts +0 -144
  279. package/src/export/exportTheme.ts +0 -251
  280. package/src/export/index.ts +0 -1
  281. package/src/export/serializeColor.test.ts +0 -73
  282. package/src/export/serializeColor.ts +0 -1
  283. package/src/export.ts +0 -1
  284. package/src/index.ts +0 -3
  285. package/src/operators/emphasis.test.ts +0 -85
  286. package/src/operators/emphasis.ts +0 -132
  287. package/src/operators/index.ts +0 -3
  288. package/src/operators/state.test.ts +0 -66
  289. package/src/operators/state.ts +0 -122
  290. package/src/operators/types.ts +0 -14
  291. package/src/operators/utils.ts +0 -44
  292. package/src/presets/curves.ts +0 -168
  293. package/src/presets/index.ts +0 -2
  294. package/src/presets/tokens/index.ts +0 -3
  295. package/src/presets/tokens/minimal-ui.ts +0 -55
  296. package/src/presets/tokens/modern-ui.ts +0 -85
  297. package/src/presets/tokens/presets.test.ts +0 -46
  298. package/src/presets/tokens/radixLike-ui.ts +0 -79
  299. package/src/serialize/index.ts +0 -1
  300. package/src/serialize/normalizeOutput.ts +0 -63
  301. package/src/serialize/serializeColor.ts +0 -260
  302. package/src/serialize/serializeResolved.test.ts +0 -57
  303. package/src/serialize.ts +0 -1
  304. package/src/types/index.ts +0 -207
  305. package/src/utils/clamp.ts +0 -2
  306. package/src/utils/index.ts +0 -1
  307. package/src/utils/lerp.ts +0 -1
  308. package/src/utils/parseColor.test.ts +0 -66
  309. package/src/utils/parseColor.ts +0 -87
  310. package/src/utils/smoothstep.ts +0 -6
  311. package/tsconfig.build.json +0 -11
  312. package/tsconfig.json +0 -15
@@ -1,54 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { serializeColor } from "./serializeColor.js";
3
- import { serializeColorJson } from "../serialize/serializeColor.js";
4
- import * as gamut from "../engine/gamut.js";
5
- describe("serializeColor", () => {
6
- it("serializes CSS with precision and includeSpaces", () => {
7
- const color = { l: 60.12, c: 0.2345, h: 40.49, alpha: 0.5 };
8
- const result = serializeColor(color, {
9
- preferSpace: "oklch",
10
- includeSpaces: ["srgb", "p3", "oklch"],
11
- gamutMapping: "compressChroma",
12
- precision: { l: 1, c: 2, h: 0, alpha: 2 },
13
- });
14
- expect(result.value).toBe("oklch(60.1% 0.23 40 / 0.5)");
15
- expect(result.oklch).toBe("oklch(60.1% 0.23 40 / 0.5)");
16
- expect(result.srgb).toMatch(/^#[0-9a-f]{8}$/i);
17
- expect(result.p3?.startsWith("color(display-p3 ")).toBe(true);
18
- });
19
- it("serializes JSON with includeSpaces and rounding", () => {
20
- const color = { l: 60.12, c: 0.2345, h: 40.49, alpha: 0.5 };
21
- const result = serializeColorJson(color, {
22
- preferSpace: "p3",
23
- includeSpaces: ["srgb", "oklch"],
24
- gamutMapping: "preferP3ThenCompress",
25
- precision: { l: 1, c: 2, h: 0, alpha: 2 },
26
- });
27
- expect(result.value.startsWith("color(display-p3 ")).toBe(true);
28
- expect(result.oklch).toBeDefined();
29
- expect(result.srgb).toBeDefined();
30
- expect(result.oklch).toBe("oklch(60.1% 0.23 40 / 0.5)");
31
- expect(result.alpha).toBe(0.5);
32
- });
33
- it("clamps alpha to [0..1] in CSS and JSON outputs", () => {
34
- const colorHi = { l: 60, c: 0.2, h: 40, alpha: 2 };
35
- const cssHi = serializeColor(colorHi, { preferSpace: "oklch", precision: { alpha: 2 } });
36
- expect(cssHi.alpha).toBe(1);
37
- expect(cssHi.value).toMatch(/^oklch\(/);
38
- const colorLo = { l: 60, c: 0.2, h: 40, alpha: -1 };
39
- const jsonLo = serializeColorJson(colorLo, { preferSpace: "oklch", precision: { alpha: 2 } });
40
- expect(jsonLo.alpha).toBe(0);
41
- expect(jsonLo.value).toMatch(/\/\s*0(\.0+)?\)/);
42
- expect(jsonLo.oklch).toMatch(/\/\s*0(\.0+)?\)/);
43
- });
44
- it("throws in strict mode when preferred space cannot be serialized", () => {
45
- const spy = vi.spyOn(gamut, "toGamutRgb").mockReturnValue(null);
46
- try {
47
- expect(() => serializeColor({ l: 60, c: 0.2, h: 40, alpha: 1 }, { preferSpace: "srgb", includeSpaces: ["srgb"], strict: true })).toThrow(/Unable to serialize preferred space: srgb/i);
48
- expect(() => serializeColorJson({ l: 60, c: 0.2, h: 40, alpha: 1 }, { preferSpace: "p3", includeSpaces: ["p3"], strict: true })).toThrow(/Unable to serialize preferred space: p3/i);
49
- }
50
- finally {
51
- spy.mockRestore();
52
- }
53
- });
54
- });
package/dist/export.d.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./export/index.js";
package/dist/export.js DELETED
@@ -1 +0,0 @@
1
- export * from "./export/index.js";
@@ -1,3 +0,0 @@
1
- import type { OkLchColor } from "../engine/generateScale.js";
2
- import type { OperatorInput } from "./types.js";
3
- export declare const applyEmphasisOperator: (input: OperatorInput) => OkLchColor;
@@ -1,113 +0,0 @@
1
- import { clamp } from "../utils/clamp.js";
2
- import { getNeutralL, getStepLightness, getSurfaceRange } from "./utils.js";
3
- const EMPHASIS_TUNING = {
4
- app: {
5
- mutedChroma: 0.55,
6
- subtleChroma: 0.75,
7
- strongChroma: 1.1,
8
- mutedNeutralPull: 0.45,
9
- subtleNeutralPull: 0.2,
10
- strongDelta: 2,
11
- },
12
- surface: {
13
- mutedChroma: 0.55,
14
- subtleChroma: 0.75,
15
- strongChroma: 1.1,
16
- mutedNeutralPull: 0.45,
17
- subtleNeutralPull: 0.2,
18
- strongDelta: 2.5,
19
- },
20
- subtle: {
21
- mutedChroma: 0.55,
22
- subtleChroma: 0.75,
23
- strongChroma: 1.1,
24
- mutedNeutralPull: 0.5,
25
- subtleNeutralPull: 0.25,
26
- strongDelta: 3,
27
- },
28
- solid: {
29
- mutedChroma: 0.55,
30
- subtleChroma: 0.75,
31
- strongChroma: 1.1,
32
- mutedNeutralPull: 0.5,
33
- subtleNeutralPull: 0.25,
34
- strongDelta: 4,
35
- },
36
- overlay: {
37
- mutedChroma: 0.55,
38
- subtleChroma: 0.75,
39
- strongChroma: 1.1,
40
- mutedNeutralPull: 0.5,
41
- subtleNeutralPull: 0.25,
42
- strongDelta: 3,
43
- },
44
- data: {
45
- mutedChroma: 0.55,
46
- subtleChroma: 0.75,
47
- strongChroma: 1.1,
48
- mutedNeutralPull: 0.5,
49
- subtleNeutralPull: 0.25,
50
- strongDelta: 4,
51
- },
52
- transparent: {
53
- mutedChroma: 0.55,
54
- subtleChroma: 0.75,
55
- strongChroma: 1.1,
56
- mutedNeutralPull: 0.45,
57
- subtleNeutralPull: 0.2,
58
- strongDelta: 2,
59
- },
60
- };
61
- const INVERTED_LERP_FACTOR = 0.8;
62
- const clampOkLch = (color, maxChroma) => ({
63
- ...color,
64
- l: clamp(color.l, 0, 100),
65
- c: clamp(color.c, 0, maxChroma),
66
- });
67
- export const applyEmphasisOperator = (input) => {
68
- const { emphasis, context, surface, usage } = input;
69
- const tuning = EMPHASIS_TUNING[surface];
70
- const range = getSurfaceRange(input.preset, surface, context);
71
- const neutralL = getNeutralL(input.preset, surface, context);
72
- if (emphasis === "default") {
73
- return input.oklch;
74
- }
75
- const next = { ...input.oklch };
76
- switch (emphasis) {
77
- case "muted":
78
- next.c *= tuning.mutedChroma;
79
- next.l += (neutralL - next.l) * tuning.mutedNeutralPull;
80
- break;
81
- case "subtle":
82
- next.c *= tuning.subtleChroma;
83
- next.l += (neutralL - next.l) * tuning.subtleNeutralPull;
84
- break;
85
- case "strong": {
86
- const diff = next.l - neutralL;
87
- let direction = diff === 0 ? (context === "light" ? -1 : 1) : Math.sign(diff);
88
- if (usage === "text" || usage === "icon") {
89
- direction = context === "light" ? -1 : 1;
90
- }
91
- else if (context === "light" && direction > 0) {
92
- direction = -1;
93
- }
94
- else if (context === "dark" && direction < 0) {
95
- direction = 1;
96
- }
97
- next.c *= tuning.strongChroma;
98
- next.l += direction * tuning.strongDelta;
99
- break;
100
- }
101
- case "inverted": {
102
- if (usage === "text" || usage === "icon") {
103
- const targetStep = input.step === 12 ? 11 : 12;
104
- const targetL = getStepLightness(input.preset, surface, context, targetStep);
105
- next.l += (targetL - next.l) * INVERTED_LERP_FACTOR;
106
- }
107
- break;
108
- }
109
- default:
110
- return input.oklch;
111
- }
112
- return clampOkLch(next, range.cMax);
113
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,69 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { applyEmphasisOperator } from "./emphasis.js";
3
- import { getStepLightness } from "./utils.js";
4
- const base = {
5
- oklch: { l: 62, c: 0.06, h: 215 },
6
- context: "light",
7
- surface: "surface",
8
- usage: "text",
9
- state: "default",
10
- emphasis: "default",
11
- preset: "modern",
12
- step: 11,
13
- };
14
- describe("applyEmphasisOperator", () => {
15
- it("muted and subtle reduce chroma predictably", () => {
16
- const muted = applyEmphasisOperator({ ...base, emphasis: "muted" });
17
- const subtle = applyEmphasisOperator({ ...base, emphasis: "subtle" });
18
- expect(muted.c).toBeLessThan(base.oklch.c);
19
- expect(subtle.c).toBeLessThan(base.oklch.c);
20
- expect(subtle.c).toBeGreaterThan(muted.c);
21
- });
22
- it("strong increases chroma", () => {
23
- const strong = applyEmphasisOperator({ ...base, emphasis: "strong" });
24
- expect(strong.c).toBeGreaterThan(base.oklch.c);
25
- });
26
- it("strong shifts text lightness by context", () => {
27
- const strongLight = applyEmphasisOperator({ ...base, emphasis: "strong", usage: "text" });
28
- const strongDark = applyEmphasisOperator({
29
- ...base,
30
- emphasis: "strong",
31
- usage: "text",
32
- context: "dark",
33
- });
34
- expect(strongLight.l).toBeLessThan(base.oklch.l);
35
- expect(strongDark.l).toBeGreaterThan(base.oklch.l);
36
- });
37
- it("does not change hue", () => {
38
- const muted = applyEmphasisOperator({ ...base, emphasis: "muted" });
39
- expect(muted.h).toBeCloseTo(base.oklch.h, 6);
40
- });
41
- it("inverted leaves backgrounds unchanged", () => {
42
- const invertedBg = applyEmphasisOperator({
43
- ...base,
44
- emphasis: "inverted",
45
- usage: "bg",
46
- });
47
- expect(invertedBg.l).toBeCloseTo(base.oklch.l, 6);
48
- });
49
- it("inverted leaves non-text usage unchanged", () => {
50
- const invertedBorder = applyEmphasisOperator({
51
- ...base,
52
- emphasis: "inverted",
53
- usage: "border",
54
- });
55
- expect(invertedBorder.l).toBeCloseTo(base.oklch.l, 6);
56
- expect(invertedBorder.c).toBeCloseTo(base.oklch.c, 6);
57
- expect(invertedBorder.h).toBeCloseTo(base.oklch.h, 6);
58
- });
59
- it("inverted moves text toward the target step without changing hue", () => {
60
- const targetL = getStepLightness(base.preset, base.surface, base.context, 12);
61
- const inverted = applyEmphasisOperator({
62
- ...base,
63
- emphasis: "inverted",
64
- usage: "text",
65
- });
66
- expect(Math.abs(inverted.l - targetL)).toBeLessThan(Math.abs(base.oklch.l - targetL));
67
- expect(inverted.h).toBeCloseTo(base.oklch.h, 6);
68
- });
69
- });
@@ -1,3 +0,0 @@
1
- export { applyEmphasisOperator } from "./emphasis.js";
2
- export { applyStateOperator } from "./state.js";
3
- export type { OperatorInput } from "./types.js";
@@ -1,2 +0,0 @@
1
- export { applyEmphasisOperator } from "./emphasis.js";
2
- export { applyStateOperator } from "./state.js";
@@ -1,3 +0,0 @@
1
- import type { OkLchColor } from "../engine/generateScale.js";
2
- import type { OperatorInput } from "./types.js";
3
- export declare const applyStateOperator: (input: OperatorInput) => OkLchColor;
@@ -1,102 +0,0 @@
1
- import { clamp } from "../utils/clamp.js";
2
- import { getNeutralL, getSurfaceRange } from "./utils.js";
3
- const STATE_TUNING = {
4
- app: {
5
- hover: 2,
6
- active: 4,
7
- selected: 3,
8
- focus: 2,
9
- disabledChroma: 0.45,
10
- disabledNeutralPull: 0.45,
11
- },
12
- surface: {
13
- hover: 2.5,
14
- active: 4.5,
15
- selected: 3.5,
16
- focus: 2.5,
17
- disabledChroma: 0.45,
18
- disabledNeutralPull: 0.45,
19
- },
20
- subtle: {
21
- hover: 3,
22
- active: 5.5,
23
- selected: 4,
24
- focus: 3,
25
- disabledChroma: 0.45,
26
- disabledNeutralPull: 0.5,
27
- },
28
- solid: {
29
- hover: 4,
30
- active: 7,
31
- selected: 5,
32
- focus: 4,
33
- disabledChroma: 0.4,
34
- disabledNeutralPull: 0.55,
35
- },
36
- overlay: {
37
- hover: 3,
38
- active: 5.5,
39
- selected: 4,
40
- focus: 3,
41
- disabledChroma: 0.45,
42
- disabledNeutralPull: 0.5,
43
- },
44
- data: {
45
- hover: 4,
46
- active: 7,
47
- selected: 5,
48
- focus: 4,
49
- disabledChroma: 0.4,
50
- disabledNeutralPull: 0.55,
51
- },
52
- transparent: {
53
- hover: 2,
54
- active: 4,
55
- selected: 3,
56
- focus: 2,
57
- disabledChroma: 0.45,
58
- disabledNeutralPull: 0.45,
59
- },
60
- };
61
- const FOCUS_BORDER_SCALE = 0.6;
62
- const clampOkLch = (color, maxChroma) => ({
63
- ...color,
64
- l: clamp(color.l, 0, 100),
65
- c: clamp(color.c, 0, maxChroma),
66
- });
67
- export const applyStateOperator = (input) => {
68
- const { state, context, surface, usage } = input;
69
- const tuning = STATE_TUNING[surface];
70
- const range = getSurfaceRange(input.preset, surface, context);
71
- const neutralL = getNeutralL(input.preset, surface, context);
72
- const direction = context === "light" ? -1 : 1;
73
- if (state === "default") {
74
- return input.oklch;
75
- }
76
- if (state === "focus" && usage !== "ring" && usage !== "border") {
77
- return input.oklch;
78
- }
79
- const next = { ...input.oklch };
80
- switch (state) {
81
- case "hover":
82
- next.l += direction * tuning.hover;
83
- break;
84
- case "active":
85
- next.l += direction * tuning.active;
86
- break;
87
- case "selected":
88
- next.l += direction * tuning.selected;
89
- break;
90
- case "focus":
91
- next.l += direction * tuning.focus * (usage === "border" ? FOCUS_BORDER_SCALE : 1);
92
- break;
93
- case "disabled":
94
- next.c *= tuning.disabledChroma;
95
- next.l += (neutralL - next.l) * tuning.disabledNeutralPull;
96
- break;
97
- default:
98
- // Allow unknown/future states to be no-ops to avoid unexpected shifts.
99
- return input.oklch;
100
- }
101
- return clampOkLch(next, range.cMax);
102
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,48 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { applyStateOperator } from "./state.js";
3
- const base = {
4
- oklch: { l: 60, c: 0.12, h: 210 },
5
- context: "light",
6
- surface: "surface",
7
- usage: "bg",
8
- state: "default",
9
- emphasis: "default",
10
- preset: "modern",
11
- step: 9,
12
- };
13
- describe("applyStateOperator", () => {
14
- it("darkens hover in light context", () => {
15
- const result = applyStateOperator({ ...base, state: "hover" });
16
- expect(result.l).toBeLessThan(base.oklch.l);
17
- });
18
- it("lightens hover in dark context", () => {
19
- const result = applyStateOperator({ ...base, context: "dark", state: "hover" });
20
- expect(result.l).toBeGreaterThan(base.oklch.l);
21
- });
22
- it("active is stronger than hover", () => {
23
- const hover = applyStateOperator({ ...base, state: "hover" });
24
- const active = applyStateOperator({ ...base, state: "active" });
25
- expect(Math.abs(active.l - base.oklch.l)).toBeGreaterThan(Math.abs(hover.l - base.oklch.l));
26
- });
27
- it("selected is less aggressive than active", () => {
28
- const selected = applyStateOperator({ ...base, state: "selected" });
29
- const active = applyStateOperator({ ...base, state: "active" });
30
- expect(Math.abs(selected.l - base.oklch.l)).toBeLessThan(Math.abs(active.l - base.oklch.l));
31
- });
32
- it("disabled reduces chroma", () => {
33
- const disabled = applyStateOperator({ ...base, state: "disabled" });
34
- expect(disabled.c).toBeLessThan(base.oklch.c * 0.6);
35
- });
36
- it("returns the original color for default state", () => {
37
- const result = applyStateOperator({ ...base, state: "default" });
38
- expect(result).toEqual(base.oklch);
39
- });
40
- it("focus only affects ring and border usage", () => {
41
- const bgFocus = applyStateOperator({ ...base, usage: "bg", state: "focus" });
42
- const ringFocus = applyStateOperator({ ...base, usage: "ring", state: "focus" });
43
- const borderFocus = applyStateOperator({ ...base, usage: "border", state: "focus" });
44
- expect(bgFocus.l).toBeCloseTo(base.oklch.l, 6);
45
- expect(ringFocus.l).not.toBeCloseTo(base.oklch.l, 6);
46
- expect(Math.abs(borderFocus.l - base.oklch.l)).toBeLessThan(Math.abs(ringFocus.l - base.oklch.l));
47
- });
48
- });
@@ -1,13 +0,0 @@
1
- import type { OkLchColor } from "../engine/generateScale.js";
2
- import type { CurvePresetName } from "../presets/index.js";
3
- import type { ColorEmphasis, ColorState, ColorUsage, SurfaceIntent } from "../types/index.js";
4
- export type OperatorInput = {
5
- oklch: OkLchColor;
6
- context: "light" | "dark";
7
- surface: SurfaceIntent;
8
- usage: ColorUsage;
9
- state: ColorState;
10
- emphasis: ColorEmphasis;
11
- preset?: CurvePresetName;
12
- step: number;
13
- };
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import type { CurvePresetName } from "../presets/index.js";
2
- import type { SurfaceIntent } from "../types/index.js";
3
- export type OperatorContext = "light" | "dark";
4
- export declare const getSurfaceRange: (presetName: CurvePresetName | undefined, surface: SurfaceIntent, context: OperatorContext) => {
5
- l: [number, number];
6
- cMin: number;
7
- cMax: number;
8
- chromaBoost?: number;
9
- } | {
10
- l: [number, number];
11
- cMin: number;
12
- cMax: number;
13
- chromaBoost?: number;
14
- };
15
- export declare const getNeutralL: (presetName: CurvePresetName | undefined, surface: SurfaceIntent, context: OperatorContext) => number;
16
- export declare const getStepLightness: (presetName: CurvePresetName | undefined, surface: SurfaceIntent, context: OperatorContext, step: number) => number;
@@ -1,23 +0,0 @@
1
- import { curvePresets } from "../presets/index.js";
2
- import { clamp } from "../utils/clamp.js";
3
- import { lerp } from "../utils/lerp.js";
4
- // TODO: deduplicate with generateScale constants and helpers.
5
- const STEPS = 12;
6
- const OKLCH_L_MIN = 0;
7
- const OKLCH_L_MAX = 100;
8
- export const getSurfaceRange = (presetName, surface, context) => {
9
- const preset = curvePresets[presetName ?? "modern"];
10
- return preset.surfaces[surface].ranges[context];
11
- };
12
- export const getNeutralL = (presetName, surface, context) => {
13
- const range = getSurfaceRange(presetName, surface, context);
14
- return (range.l[0] + range.l[1]) / 2;
15
- };
16
- export const getStepLightness = (presetName, surface, context, step) => {
17
- const preset = curvePresets[presetName ?? "modern"];
18
- const surfaceCurve = preset.surfaces[surface];
19
- const range = surfaceCurve.ranges[context];
20
- const t = clamp((step - 1) / (STEPS - 1), 0, 1);
21
- const lightnessT = surfaceCurve.l(t);
22
- return clamp(lerp(range.l[0], range.l[1], lightnessT), OKLCH_L_MIN, OKLCH_L_MAX);
23
- };
@@ -1,28 +0,0 @@
1
- import type { SurfaceIntent } from "../types/index.js";
2
- export type CurvePresetName = "modern" | "radixLike";
3
- export type CurveFn = (t: number) => number;
4
- export type SurfaceCurve = {
5
- l: CurveFn;
6
- c: CurveFn;
7
- ranges: {
8
- light: {
9
- l: [number, number];
10
- cMin: number;
11
- cMax: number;
12
- chromaBoost?: number;
13
- };
14
- dark: {
15
- l: [number, number];
16
- cMin: number;
17
- cMax: number;
18
- chromaBoost?: number;
19
- };
20
- };
21
- };
22
- export type CurvePreset = {
23
- name: CurvePresetName;
24
- surfaces: Record<SurfaceIntent, SurfaceCurve>;
25
- };
26
- export declare const modern: CurvePreset;
27
- export declare const radixLike: CurvePreset;
28
- export declare const curvePresets: Record<CurvePresetName, CurvePreset>;
@@ -1,145 +0,0 @@
1
- import { clamp } from "../utils/clamp.js";
2
- import { smoothstep } from "../utils/smoothstep.js";
3
- const normalizeT = (t) => clamp(t, 0, 1);
4
- const lightnessCurve = (t) => smoothstep(normalizeT(t));
5
- const chromaCurve = (t) => Math.sin(Math.PI * normalizeT(t));
6
- export const modern = {
7
- name: "modern",
8
- surfaces: {
9
- // app: fundo do app (separação mínima, chroma baixíssimo)
10
- app: {
11
- l: lightnessCurve,
12
- c: chromaCurve,
13
- ranges: {
14
- light: { l: [90, 99], cMin: 0.004, cMax: 0.05 },
15
- dark: { l: [6, 22], cMin: 0.004, cMax: 0.07 },
16
- },
17
- },
18
- // surface: cards/panels (separação leve do app bg, chroma baixo)
19
- surface: {
20
- l: lightnessCurve,
21
- c: chromaCurve,
22
- ranges: {
23
- light: { l: [84, 97], cMin: 0.008, cMax: 0.08 },
24
- dark: { l: [8, 28], cMin: 0.008, cMax: 0.09 },
25
- },
26
- },
27
- // subtle: tints/hover backgrounds (um pouco mais de chroma, ainda calmo)
28
- subtle: {
29
- l: lightnessCurve,
30
- c: chromaCurve,
31
- ranges: {
32
- light: { l: [78, 95], cMin: 0.012, cMax: 0.1 },
33
- dark: { l: [10, 32], cMin: 0.012, cMax: 0.11 },
34
- },
35
- },
36
- // solid: backgrounds sólidos (chroma mais alto, pensado para onSolid)
37
- solid: {
38
- l: lightnessCurve,
39
- c: chromaCurve,
40
- ranges: {
41
- light: { l: [46, 90], cMin: 0.03, cMax: 0.18, chromaBoost: 1.25 },
42
- dark: { l: [12, 42], cMin: 0.03, cMax: 0.2, chromaBoost: 1.25 },
43
- },
44
- },
45
- // overlay: modal surfaces/scrims (chroma baixo, controle forte de L)
46
- overlay: {
47
- l: lightnessCurve,
48
- c: chromaCurve,
49
- ranges: {
50
- light: { l: [72, 96], cMin: 0.01, cMax: 0.09 },
51
- dark: { l: [14, 40], cMin: 0.01, cMax: 0.1 },
52
- },
53
- },
54
- // data: charts/heatmaps (pode usar chroma maior sem “gritar”)
55
- data: {
56
- l: lightnessCurve,
57
- c: chromaCurve,
58
- ranges: {
59
- light: { l: [38, 86], cMin: 0.04, cMax: 0.22, chromaBoost: 1.3 },
60
- dark: { l: [18, 48], cMin: 0.04, cMax: 0.24, chromaBoost: 1.3 },
61
- },
62
- },
63
- // transparent: base quase neutra (chroma quase zero; usado para ghost)
64
- transparent: {
65
- l: lightnessCurve,
66
- c: chromaCurve,
67
- ranges: {
68
- light: { l: [62, 96], cMin: 0, cMax: 0.06 },
69
- dark: { l: [8, 30], cMin: 0, cMax: 0.07 },
70
- },
71
- },
72
- },
73
- };
74
- export const radixLike = {
75
- name: "radixLike",
76
- surfaces: {
77
- // app: fundo do app (separação mínima, chroma baixíssimo)
78
- app: {
79
- l: lightnessCurve,
80
- c: chromaCurve,
81
- ranges: {
82
- light: { l: [92, 99], cMin: 0.003, cMax: 0.06 },
83
- dark: { l: [4, 18], cMin: 0.003, cMax: 0.08 },
84
- },
85
- },
86
- // surface: cards/panels (separação leve do app bg, chroma baixo)
87
- surface: {
88
- l: lightnessCurve,
89
- c: chromaCurve,
90
- ranges: {
91
- light: { l: [86, 97], cMin: 0.006, cMax: 0.1 },
92
- dark: { l: [6, 24], cMin: 0.006, cMax: 0.11 },
93
- },
94
- },
95
- // subtle: tints/hover backgrounds (um pouco mais de chroma, ainda calmo)
96
- subtle: {
97
- l: lightnessCurve,
98
- c: chromaCurve,
99
- ranges: {
100
- light: { l: [80, 94], cMin: 0.01, cMax: 0.12 },
101
- dark: { l: [8, 30], cMin: 0.01, cMax: 0.13 },
102
- },
103
- },
104
- // solid: backgrounds sólidos (chroma mais alto, pensado para onSolid)
105
- solid: {
106
- l: lightnessCurve,
107
- c: chromaCurve,
108
- ranges: {
109
- light: { l: [50, 88], cMin: 0.035, cMax: 0.2, chromaBoost: 1.25 },
110
- dark: { l: [10, 38], cMin: 0.035, cMax: 0.22, chromaBoost: 1.25 },
111
- },
112
- },
113
- // overlay: modal surfaces/scrims (chroma baixo, controle forte de L)
114
- overlay: {
115
- l: lightnessCurve,
116
- c: chromaCurve,
117
- ranges: {
118
- light: { l: [74, 96], cMin: 0.01, cMax: 0.11 },
119
- dark: { l: [12, 38], cMin: 0.01, cMax: 0.12 },
120
- },
121
- },
122
- // data: charts/heatmaps (pode usar chroma maior sem “gritar”)
123
- data: {
124
- l: lightnessCurve,
125
- c: chromaCurve,
126
- ranges: {
127
- light: { l: [40, 84], cMin: 0.05, cMax: 0.24, chromaBoost: 1.3 },
128
- dark: { l: [16, 46], cMin: 0.05, cMax: 0.26, chromaBoost: 1.3 },
129
- },
130
- },
131
- // transparent: base quase neutra (chroma quase zero; usado para ghost)
132
- transparent: {
133
- l: lightnessCurve,
134
- c: chromaCurve,
135
- ranges: {
136
- light: { l: [60, 94], cMin: 0, cMax: 0.08 },
137
- dark: { l: [6, 28], cMin: 0, cMax: 0.09 },
138
- },
139
- },
140
- },
141
- };
142
- export const curvePresets = {
143
- modern,
144
- radixLike,
145
- };
@@ -1,2 +0,0 @@
1
- export type { CurveFn, CurvePreset, CurvePresetName, SurfaceCurve } from "./curves.js";
2
- export { curvePresets, modern, radixLike } from "./curves.js";
@@ -1 +0,0 @@
1
- export { curvePresets, modern, radixLike } from "./curves.js";
@@ -1,3 +0,0 @@
1
- export { minimalUiTokens } from "./minimal-ui.js";
2
- export { modernUiTokens } from "./modern-ui.js";
3
- export { radixLikeUiTokens } from "./radixLike-ui.js";
@@ -1,3 +0,0 @@
1
- export { minimalUiTokens } from "./minimal-ui.js";
2
- export { modernUiTokens } from "./modern-ui.js";
3
- export { radixLikeUiTokens } from "./radixLike-ui.js";