@clhaas/palette-kit 0.1.7 → 0.3.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 (293) hide show
  1. package/.codex/skills/color-pipeline-implementer/SKILL.md +23 -0
  2. package/.codex/skills/commit-message-crafter/SKILL.md +63 -0
  3. package/.codex/skills/commit-message-crafter/references/benchmarks.md +20 -0
  4. package/.codex/skills/contrast-solver-helper/SKILL.md +20 -0
  5. package/.codex/skills/exporters-builder/SKILL.md +20 -0
  6. package/.codex/skills/markdownlint-writer/SKILL.md +32 -0
  7. package/.codex/skills/phase-implementation-runbook/SKILL.md +92 -0
  8. package/.codex/skills/type-contract-auditor/SKILL.md +21 -0
  9. package/.github/skills/review-guide/SKILL.md +23 -0
  10. package/.github/skills/review-guide/references/review-guide-v0.3.md +629 -0
  11. package/.markdownlint.json +4 -0
  12. package/AGENTS.md +16 -0
  13. package/CHANGELOG.md +34 -0
  14. package/README.md +79 -169
  15. package/biome.json +43 -0
  16. package/dist/cli/args.d.ts +12 -0
  17. package/dist/cli/args.js +56 -0
  18. package/dist/cli/args.test.js +22 -0
  19. package/dist/cli/codegen/__snapshots__/tokens.test.js.snap +87 -0
  20. package/dist/cli/codegen/tokens.d.ts +12 -0
  21. package/dist/cli/codegen/tokens.js +139 -0
  22. package/dist/cli/codegen/tokens.test.d.ts +1 -0
  23. package/dist/cli/codegen/tokens.test.js +51 -0
  24. package/dist/cli/config.d.ts +40 -0
  25. package/dist/cli/config.js +34 -0
  26. package/dist/cli/validate.d.ts +2 -0
  27. package/dist/cli/validate.js +33 -0
  28. package/dist/cli/validate.test.d.ts +1 -0
  29. package/dist/cli/validate.test.js +40 -0
  30. package/dist/cli.js +138 -140
  31. package/dist/contrast/apca.d.ts +2 -2
  32. package/dist/contrast/apca.js +14 -4
  33. package/dist/contrast/apca.test.d.ts +1 -0
  34. package/dist/contrast/apca.test.js +16 -0
  35. package/dist/contrast/index.d.ts +4 -0
  36. package/dist/contrast/index.js +4 -0
  37. package/dist/contrast/scoring.d.ts +4 -0
  38. package/dist/contrast/scoring.js +31 -0
  39. package/dist/contrast/scoring.test.d.ts +1 -0
  40. package/dist/contrast/scoring.test.js +148 -0
  41. package/dist/contrast/solver.d.ts +13 -0
  42. package/dist/contrast/solver.js +170 -0
  43. package/dist/contrast/solver.test.d.ts +1 -0
  44. package/dist/contrast/solver.test.js +75 -0
  45. package/dist/contrast/types.d.ts +17 -0
  46. package/dist/contrast/types.js +1 -0
  47. package/dist/contrast/utils.d.ts +4 -0
  48. package/dist/contrast/utils.js +18 -0
  49. package/dist/contrast/wcag2.d.ts +3 -0
  50. package/dist/contrast/wcag2.js +19 -0
  51. package/dist/contrast/wcag2.test.d.ts +1 -0
  52. package/dist/contrast/wcag2.test.js +17 -0
  53. package/dist/core/createTheme.d.ts +35 -0
  54. package/dist/core/createTheme.js +24 -0
  55. package/dist/core/dx-helpers.test.d.ts +1 -0
  56. package/dist/core/dx-helpers.test.js +61 -0
  57. package/dist/core/index.d.ts +2 -0
  58. package/dist/core/index.js +2 -0
  59. package/dist/core/onSolid.test.d.ts +1 -0
  60. package/dist/core/onSolid.test.js +118 -0
  61. package/dist/core/qa.v1.test.d.ts +1 -0
  62. package/dist/core/qa.v1.test.js +112 -0
  63. package/dist/core/resolve.d.ts +3 -0
  64. package/dist/core/resolve.js +8 -0
  65. package/dist/core/resolve.test.d.ts +1 -0
  66. package/dist/core/resolve.test.js +89 -0
  67. package/dist/core/resolveMany.d.ts +8 -0
  68. package/dist/core/resolveMany.js +17 -0
  69. package/dist/core/tokenRegistry.d.ts +23 -0
  70. package/dist/core/tokenRegistry.js +83 -0
  71. package/dist/core/tokenRegistry.test.d.ts +1 -0
  72. package/dist/core/tokenRegistry.test.js +133 -0
  73. package/dist/engine/applyOperators.d.ts +3 -0
  74. package/dist/engine/applyOperators.js +23 -0
  75. package/dist/engine/context.d.ts +4 -0
  76. package/dist/engine/context.js +1 -0
  77. package/dist/engine/gamut.d.ts +13 -0
  78. package/dist/engine/gamut.js +101 -0
  79. package/dist/engine/gamut.test.d.ts +1 -0
  80. package/dist/engine/gamut.test.js +23 -0
  81. package/dist/engine/generateScale.d.ts +15 -0
  82. package/dist/engine/generateScale.js +29 -0
  83. package/dist/engine/generateScale.test.d.ts +1 -0
  84. package/dist/engine/generateScale.test.js +32 -0
  85. package/dist/engine/index.d.ts +8 -0
  86. package/dist/engine/index.js +4 -0
  87. package/dist/engine/normalize.d.ts +43 -0
  88. package/dist/engine/normalize.js +403 -0
  89. package/dist/engine/normalize.test.d.ts +1 -0
  90. package/dist/engine/normalize.test.js +136 -0
  91. package/dist/engine/onSolid.d.ts +3 -0
  92. package/dist/engine/onSolid.js +110 -0
  93. package/dist/engine/resolveBaseColor.d.ts +25 -0
  94. package/dist/engine/resolveBaseColor.js +127 -0
  95. package/dist/engine/resolveBaseColor.test.d.ts +1 -0
  96. package/dist/engine/resolveBaseColor.test.js +97 -0
  97. package/dist/export/__snapshots__/exportTheme.test.js.snap +74 -0
  98. package/dist/export/exportTheme.d.ts +47 -0
  99. package/dist/export/exportTheme.js +170 -0
  100. package/dist/export/exportTheme.test.d.ts +1 -0
  101. package/dist/export/exportTheme.test.js +118 -0
  102. package/dist/export/index.d.ts +1 -0
  103. package/dist/export/index.js +1 -0
  104. package/dist/export/serializeColor.d.ts +1 -0
  105. package/dist/export/serializeColor.js +1 -0
  106. package/dist/export/serializeColor.test.d.ts +1 -0
  107. package/dist/export/serializeColor.test.js +54 -0
  108. package/dist/export.d.ts +1 -0
  109. package/dist/export.js +1 -0
  110. package/dist/index.d.ts +3 -20
  111. package/dist/index.js +2 -17
  112. package/dist/operators/emphasis.d.ts +3 -0
  113. package/dist/operators/emphasis.js +113 -0
  114. package/dist/operators/emphasis.test.d.ts +1 -0
  115. package/dist/operators/emphasis.test.js +69 -0
  116. package/dist/operators/index.d.ts +3 -0
  117. package/dist/operators/index.js +2 -0
  118. package/dist/operators/state.d.ts +3 -0
  119. package/dist/operators/state.js +102 -0
  120. package/dist/operators/state.test.d.ts +1 -0
  121. package/dist/operators/state.test.js +48 -0
  122. package/dist/operators/types.d.ts +13 -0
  123. package/dist/operators/types.js +1 -0
  124. package/dist/operators/utils.d.ts +16 -0
  125. package/dist/operators/utils.js +23 -0
  126. package/dist/presets/curves.d.ts +28 -0
  127. package/dist/presets/curves.js +145 -0
  128. package/dist/presets/index.d.ts +2 -0
  129. package/dist/presets/index.js +1 -0
  130. package/dist/presets/tokens/index.d.ts +3 -0
  131. package/dist/presets/tokens/index.js +3 -0
  132. package/dist/presets/tokens/minimal-ui.d.ts +6 -0
  133. package/dist/presets/tokens/minimal-ui.js +53 -0
  134. package/dist/presets/tokens/modern-ui.d.ts +5 -0
  135. package/dist/presets/tokens/modern-ui.js +83 -0
  136. package/dist/presets/tokens/presets.test.d.ts +1 -0
  137. package/dist/presets/tokens/presets.test.js +31 -0
  138. package/dist/presets/tokens/radixLike-ui.d.ts +6 -0
  139. package/dist/presets/tokens/radixLike-ui.js +77 -0
  140. package/dist/serialize/index.d.ts +1 -0
  141. package/dist/serialize/index.js +1 -0
  142. package/dist/serialize/normalizeOutput.d.ts +6 -0
  143. package/dist/serialize/normalizeOutput.js +45 -0
  144. package/dist/serialize/serializeColor.d.ts +21 -0
  145. package/dist/serialize/serializeColor.js +178 -0
  146. package/dist/serialize/serializeResolved.test.d.ts +1 -0
  147. package/dist/serialize/serializeResolved.test.js +45 -0
  148. package/dist/serialize.d.ts +1 -0
  149. package/dist/serialize.js +1 -0
  150. package/dist/types/index.d.ts +187 -0
  151. package/dist/types/index.js +1 -0
  152. package/dist/utils/clamp.d.ts +1 -0
  153. package/dist/utils/clamp.js +1 -0
  154. package/dist/utils/index.d.ts +1 -0
  155. package/dist/utils/index.js +1 -0
  156. package/dist/utils/lerp.d.ts +1 -0
  157. package/dist/utils/lerp.js +1 -0
  158. package/dist/utils/parseColor.d.ts +6 -0
  159. package/dist/utils/parseColor.js +67 -0
  160. package/dist/utils/parseColor.test.d.ts +1 -0
  161. package/dist/utils/parseColor.test.js +51 -0
  162. package/dist/utils/smoothstep.d.ts +1 -0
  163. package/dist/utils/smoothstep.js +5 -0
  164. package/package.json +19 -12
  165. package/planning/phase-10-review.md +550 -0
  166. package/planning/phase-7-review.md +411 -0
  167. package/planning/phase-8-review.md +669 -0
  168. package/planning/phase-9-review.md +564 -0
  169. package/planning/roadmap-v0.3.md +284 -0
  170. package/planning/spec-serializer-v0.3.md +324 -0
  171. package/planning/spec-v0.3.md +305 -0
  172. package/src/cli/args.test.ts +28 -0
  173. package/src/cli/args.ts +66 -0
  174. package/src/cli/codegen/__snapshots__/tokens.test.ts.snap +87 -0
  175. package/src/cli/codegen/tokens.test.ts +61 -0
  176. package/src/cli/codegen/tokens.ts +191 -0
  177. package/src/cli/config.ts +71 -0
  178. package/src/cli/validate.test.ts +49 -0
  179. package/src/cli/validate.ts +38 -0
  180. package/src/cli.ts +183 -0
  181. package/src/contrast/apca.test.ts +20 -0
  182. package/src/contrast/apca.ts +26 -0
  183. package/src/contrast/index.ts +4 -0
  184. package/src/contrast/scoring.test.ts +188 -0
  185. package/src/contrast/scoring.ts +48 -0
  186. package/src/contrast/solver.test.ts +147 -0
  187. package/src/contrast/solver.ts +235 -0
  188. package/src/contrast/types.ts +20 -0
  189. package/src/contrast/utils.ts +28 -0
  190. package/src/contrast/wcag2.test.ts +21 -0
  191. package/src/contrast/wcag2.ts +24 -0
  192. package/src/core/createTheme.ts +78 -0
  193. package/src/core/dx-helpers.test.ts +82 -0
  194. package/src/core/index.ts +7 -0
  195. package/src/core/onSolid.test.ts +146 -0
  196. package/src/core/qa.v1.test.ts +149 -0
  197. package/src/core/resolve.test.ts +99 -0
  198. package/src/core/resolve.ts +11 -0
  199. package/src/core/resolveMany.ts +22 -0
  200. package/src/core/tokenRegistry.test.ts +153 -0
  201. package/src/core/tokenRegistry.ts +114 -0
  202. package/src/engine/applyOperators.ts +32 -0
  203. package/src/engine/context.ts +8 -0
  204. package/src/engine/gamut.test.ts +30 -0
  205. package/src/engine/gamut.ts +144 -0
  206. package/src/engine/generateScale.test.ts +46 -0
  207. package/src/engine/generateScale.ts +48 -0
  208. package/src/engine/index.ts +8 -0
  209. package/src/engine/normalize.test.ts +222 -0
  210. package/src/engine/normalize.ts +550 -0
  211. package/src/engine/onSolid.ts +178 -0
  212. package/src/engine/resolveBaseColor.test.ts +117 -0
  213. package/src/engine/resolveBaseColor.ts +203 -0
  214. package/src/export/__snapshots__/exportTheme.test.ts.snap +74 -0
  215. package/src/export/exportTheme.test.ts +144 -0
  216. package/src/export/exportTheme.ts +251 -0
  217. package/src/export/index.ts +1 -0
  218. package/src/export/serializeColor.test.ts +73 -0
  219. package/src/export/serializeColor.ts +1 -0
  220. package/src/export.ts +1 -0
  221. package/src/index.ts +3 -0
  222. package/src/operators/emphasis.test.ts +85 -0
  223. package/src/operators/emphasis.ts +132 -0
  224. package/src/operators/index.ts +3 -0
  225. package/src/operators/state.test.ts +66 -0
  226. package/src/operators/state.ts +122 -0
  227. package/src/operators/types.ts +14 -0
  228. package/src/operators/utils.ts +44 -0
  229. package/src/presets/curves.ts +168 -0
  230. package/src/presets/index.ts +2 -0
  231. package/src/presets/tokens/index.ts +3 -0
  232. package/src/presets/tokens/minimal-ui.ts +55 -0
  233. package/src/presets/tokens/modern-ui.ts +85 -0
  234. package/src/presets/tokens/presets.test.ts +46 -0
  235. package/src/presets/tokens/radixLike-ui.ts +79 -0
  236. package/src/serialize/index.ts +1 -0
  237. package/src/serialize/normalizeOutput.ts +63 -0
  238. package/src/serialize/serializeColor.ts +260 -0
  239. package/src/serialize/serializeResolved.test.ts +57 -0
  240. package/src/serialize.ts +1 -0
  241. package/src/types/index.ts +207 -0
  242. package/src/utils/clamp.ts +2 -0
  243. package/src/utils/index.ts +1 -0
  244. package/src/utils/lerp.ts +1 -0
  245. package/src/utils/parseColor.test.ts +66 -0
  246. package/src/utils/parseColor.ts +87 -0
  247. package/src/utils/smoothstep.ts +6 -0
  248. package/tsconfig.build.json +11 -0
  249. package/tsconfig.json +15 -0
  250. package/dist/alpha/generateAlphaScale.d.ts +0 -5
  251. package/dist/alpha/generateAlphaScale.js +0 -34
  252. package/dist/contrast/onSolid.d.ts +0 -6
  253. package/dist/contrast/onSolid.js +0 -28
  254. package/dist/contrast/solveText.d.ts +0 -2
  255. package/dist/contrast/solveText.js +0 -31
  256. package/dist/createTheme.d.ts +0 -38
  257. package/dist/createTheme.js +0 -137
  258. package/dist/data/radixSeeds.d.ts +0 -3
  259. package/dist/data/radixSeeds.js +0 -34
  260. package/dist/diagnostics/analyzeScale.d.ts +0 -2
  261. package/dist/diagnostics/analyzeScale.js +0 -7
  262. package/dist/diagnostics/analyzeTheme.d.ts +0 -2
  263. package/dist/diagnostics/analyzeTheme.js +0 -35
  264. package/dist/diagnostics/warnings.d.ts +0 -2
  265. package/dist/diagnostics/warnings.js +0 -20
  266. package/dist/engine/curves.d.ts +0 -9
  267. package/dist/engine/curves.js +0 -48
  268. package/dist/engine/oklch.d.ts +0 -8
  269. package/dist/engine/oklch.js +0 -40
  270. package/dist/engine/templates.d.ts +0 -14
  271. package/dist/engine/templates.js +0 -45
  272. package/dist/exporters/selectColorMode.d.ts +0 -2
  273. package/dist/exporters/selectColorMode.js +0 -19
  274. package/dist/exporters/toCssVars.d.ts +0 -13
  275. package/dist/exporters/toCssVars.js +0 -108
  276. package/dist/exporters/toJson.d.ts +0 -3
  277. package/dist/exporters/toJson.js +0 -25
  278. package/dist/exporters/toReactNative.d.ts +0 -54
  279. package/dist/exporters/toReactNative.js +0 -33
  280. package/dist/exporters/toTailwind.d.ts +0 -17
  281. package/dist/exporters/toTailwind.js +0 -111
  282. package/dist/exporters/toTs.d.ts +0 -3
  283. package/dist/exporters/toTs.js +0 -43
  284. package/dist/generateScale.d.ts +0 -48
  285. package/dist/generateScale.js +0 -274
  286. package/dist/overlays/generateOverlayScale.d.ts +0 -2
  287. package/dist/overlays/generateOverlayScale.js +0 -34
  288. package/dist/text/generateTextScale.d.ts +0 -8
  289. package/dist/text/generateTextScale.js +0 -18
  290. package/dist/tokens/presetRadixLikeUi.d.ts +0 -5
  291. package/dist/tokens/presetRadixLikeUi.js +0 -55
  292. package/dist/types.d.ts +0 -69
  293. /package/dist/{types.js → cli/args.test.d.ts} +0 -0
package/README.md CHANGED
@@ -1,217 +1,127 @@
1
1
  # Palette Kit
2
2
 
3
- Modern palette generator (OKLCH + APCA) with Radix-like steps. The library accepts seeds (initial colors) and produces light/dark scales, semantic tokens, and exporters ready for use.
3
+ Palette Kit is a **runtime-first color engine** for generating OKLCH-based palettes from semantic queries, with optional build-time tooling (serializer, exporters, CLI, codegen).
4
4
 
5
- Status: WIP. The API may change while the MVP is under construction.
6
-
7
- ## Installation
5
+ ## Install
8
6
 
9
7
  ```bash
10
8
  npm install @clhaas/palette-kit
11
9
  ```
12
10
 
13
- ```bash
14
- yarn add @clhaas/palette-kit
15
- ```
16
-
17
- ```bash
18
- pnpm add @clhaas/palette-kit
19
- ```
20
-
21
- ## What the library provides
22
-
23
- - 12-step scale (light/dark) from a seed.
24
- - Semantic tokens for UI (`radix-like-ui` preset).
25
- - Alpha scales per palette slot (chromatic alpha).
26
- - Overlay scales (black/white alpha).
27
- - Deterministic text scales for light/dark backgrounds.
28
- - Exporters for TS, JSON, CSS vars, Tailwind, and React Native.
29
- - Auto anchor selection per mode (light/dark), overridable via `anchorStep`.
30
- - Basic contrast and gamut diagnostics.
31
-
32
- ## Usage example
11
+ ## Runtime quick start
33
12
 
34
13
  ```ts
35
14
  import { createTheme } from "@clhaas/palette-kit";
36
15
 
37
16
  const theme = createTheme({
38
- neutral: { source: "seed", value: "#111827" },
39
- accent: { source: "seed", value: "#3d63dd" },
40
- semantic: {
41
- success: { source: "seed", value: "#16a34a" },
42
- warning: { source: "seed", value: "#f59e0b" },
43
- danger: { source: "seed", value: "#ef4444" },
44
- },
45
- tokens: { preset: "radix-like-ui" },
46
- text: {
47
- darkBase: "#1C1C1E",
48
- lightBase: "#F5F5F7",
17
+ seeds: {
18
+ light: { neutral: "#111827", accent: "#3d63dd" },
19
+ dark: { neutral: "#111827", accent: "#3d63dd" },
49
20
  },
50
- p3: true,
21
+ preset: "modern",
51
22
  });
52
- ```
53
-
54
- ## Quick start
55
23
 
56
- Generate a theme and export CSS variables:
57
-
58
- ```ts
59
- import { createTheme, toCssVars } from "@clhaas/palette-kit";
60
-
61
- const theme = createTheme({
62
- neutral: { source: "seed", value: "#111827" },
63
- accent: { source: "seed", value: "#3d63dd" },
24
+ const bg = theme.resolve({
25
+ role: "bg.app",
26
+ usage: "bg",
27
+ surface: "app",
28
+ context: "light",
64
29
  });
65
30
 
66
- const css = toCssVars(theme, { prefix: "pk" });
67
- ```
68
-
69
- Use tokens in your app:
70
-
71
- ```css
72
- :root {
73
- /* paste the generated CSS vars here */
74
- }
75
-
76
- body {
77
- background: var(--pk-bg-app);
78
- color: var(--pk-text-primary);
79
- }
31
+ const onSolidText = theme.onSolid({
32
+ bgRole: "bg.app",
33
+ usage: "text",
34
+ context: "light",
35
+ contrast: { model: "apca", targetLc: 75 },
36
+ });
80
37
  ```
81
38
 
82
- ## Step-by-step (install -> usage -> types)
83
-
84
- 1) Install:
39
+ ## Serializer (public)
85
40
 
86
- ```bash
87
- npm install @clhaas/palette-kit
88
- ```
41
+ Use the serializer to turn resolved OKLCH into CSS/RN-ready strings:
89
42
 
90
- 2) Create a config file (`palette.config.mjs`):
43
+ ```ts
44
+ import { createTheme } from "@clhaas/palette-kit";
45
+ import { serializeResolved } from "@clhaas/palette-kit/serialize";
91
46
 
92
- ```js
93
- /** @type {import("@clhaas/palette-kit").CreateThemeOptions} */
94
- export default {
95
- neutral: { source: "seed", value: "#111827" },
96
- accent: { source: "seed", value: "#3d63dd" },
97
- semantic: {
98
- success: { source: "seed", value: "#16a34a" },
99
- warning: { source: "seed", value: "#f59e0b" },
100
- danger: { source: "seed", value: "#ef4444" },
101
- },
102
- tokens: { preset: "radix-like-ui" },
103
- alpha: { enabled: true },
104
- text: {
105
- darkBase: "#1C1C1E",
106
- lightBase: "#F5F5F7",
47
+ const theme = createTheme({
48
+ seeds: {
49
+ light: { neutral: "#111827", accent: "#3d63dd" },
50
+ dark: { neutral: "#111827", accent: "#3d63dd" },
107
51
  },
108
- p3: true,
109
- };
110
- ```
52
+ });
111
53
 
112
- 3) Generate a typed theme file (includes token name types):
54
+ const resolved = theme.resolve({ role: "bg.app", usage: "bg", surface: "app" });
55
+ const color = serializeResolved(resolved, { preferSpace: "srgb", srgbFormat: "hex" });
113
56
 
114
- ```bash
115
- npx palette-kit generate --out src/theme.ts
57
+ console.log(color.value);
116
58
  ```
117
59
 
118
- 4) Export CSS vars (for web apps):
119
-
120
- ```ts
121
- import { toCssVars } from "@clhaas/palette-kit";
122
- import { theme } from "./theme";
123
-
124
- const css = toCssVars(theme, { prefix: "pk" });
125
- // write the string into a .css file or inject it at build time
126
- ```
60
+ ## Exporters (public, build-time)
127
61
 
128
- 5) Use the generated types:
62
+ Export deterministic CSS variables and JSON tokens:
129
63
 
130
64
  ```ts
131
- import { theme, ThemeTokenMap, ThemeTokenName } from "./theme";
65
+ import { createTheme } from "@clhaas/palette-kit";
66
+ import { exportThemeCss, exportThemeJson } from "@clhaas/palette-kit/export";
132
67
 
133
- const tokens: ThemeTokenMap = theme.tokens.light;
134
- const tokenName: ThemeTokenName = "bg.app";
135
- ```
68
+ const theme = createTheme({
69
+ seeds: {
70
+ light: { neutral: "#111827", accent: "#3d63dd" },
71
+ dark: { neutral: "#111827", accent: "#3d63dd" },
72
+ },
73
+ });
136
74
 
137
- ## Alpha, overlay, and text examples
75
+ const tokens = {
76
+ "bg.app": { usage: "bg", surface: "app" },
77
+ "text.primary": { usage: "text", surface: "surface" },
78
+ };
138
79
 
139
- ```ts
140
- const accentAlpha = theme.alpha?.accent.light[5];
141
- const overlay = theme.overlay.black[9];
142
- const textOnLight = theme.tokens.light["text.dark.primary"];
143
- const textOnDark = theme.tokens.dark["text.light.primary"];
80
+ const { css } = exportThemeCss(theme, tokens, { includeSpaces: ["oklch", "p3"] });
81
+ const json = exportThemeJson(theme, tokens, { includeSpaces: ["srgb"] });
144
82
  ```
145
83
 
146
- ## Migration note (alpha)
84
+ ## CLI
147
85
 
148
- Alpha scales are now generated per palette slot. If you previously used:
86
+ - `palette-kit init` creates `palette.config.ts`
87
+ - `palette-kit build` writes `dist/palette/` artifacts (`tokens.css`, `tokens.json`, `tokens.ts`, `tokens.d.ts`)
149
88
 
150
- ```ts
151
- theme.alpha?.light[5];
152
- ```
89
+ See `docs/CLI.md` for flags and config details.
153
90
 
154
- Update to:
91
+ ## Docs
155
92
 
156
- ```ts
157
- theme.alpha?.accent.light[5];
158
- ```
159
-
160
- CSS variables were also updated from `--pk-alpha-<step>` to `--pk-alpha-<slot>-<step>`.
161
-
162
- ## React Native + Expo
93
+ - `docs/README.md`
94
+ - `docs/_api-surface.md`
95
+ - `docs/Exporters.md`
96
+ - `docs/CLI.md`
97
+ - `docs/Migration.md`
163
98
 
164
- Use the React Native exporter and `useColorScheme()`:
99
+ ## Compatibility
165
100
 
166
- ```ts
167
- import { useMemo } from "react";
168
- import { useColorScheme } from "react-native";
169
- import { createTheme, toReactNative } from "@clhaas/palette-kit";
101
+ - ESM package (`"type": "module"`).
102
+ - CJS is not supported in v0.3; use ESM or dynamic `import()` in CJS environments.
103
+ - Subpath imports:
104
+ - `@clhaas/palette-kit/serialize`
105
+ - `@clhaas/palette-kit/export`
106
+ - `@clhaas/palette-kit/cli`
107
+ - Tree-shaking: runtime imports (`@clhaas/palette-kit`) do not bundle build-time tools (exporters/CLI).
170
108
 
171
- const theme = createTheme({
172
- neutral: { source: "seed", value: "#111827" },
173
- accent: { source: "seed", value: "#3d63dd" },
174
- p3: true,
175
- });
109
+ ### CommonJS note
176
110
 
177
- export function usePalette() {
178
- const scheme = useColorScheme();
179
- const palette = useMemo(() => toReactNative(theme, { includeP3: true }), []);
180
- return scheme === "dark" ? palette.dark : palette.light;
181
- }
111
+ ```js
112
+ // CJS workaround (dynamic import)
113
+ (async () => {
114
+ const { createTheme } = await import("@clhaas/palette-kit");
115
+ const theme = createTheme({
116
+ seeds: {
117
+ light: { neutral: "#111827", accent: "#3d63dd" },
118
+ dark: { neutral: "#111827", accent: "#3d63dd" },
119
+ },
120
+ });
121
+ console.log(theme);
122
+ })();
182
123
  ```
183
124
 
184
- See `examples/expo` for a full example.
185
-
186
- Note: React Native does not support `color(display-p3 ...)` strings as drop-in colors. The `p3` field is provided as data for platforms that can handle wide color via native APIs.
187
-
188
- ## Principles
189
-
190
- - Tokens by intent, not by color.
191
- - Fixed steps (1-12) for UI consistency.
192
- - OKLCH generation, contrast resolved with APCA.
193
-
194
- ## Docs and plans
195
-
196
- - `docs/README.md`
197
- - `docs/concepts.md`
198
- - `docs/api.md`
199
- - `docs/tokens.md`
200
- - `docs/contrast.md`
201
- - `docs/alpha.md`
202
- - `docs/text.md`
203
- - `docs/overlays.md`
204
- - `docs/Why.md`
205
- - `docs/spec-implementation.md`
206
- - `docs/plan-tests.md`
207
- - `docs/plan-docs.md`
208
-
209
- ## Short roadmap
210
-
211
- 1) Generate scales from seeds (light/dark).
212
- 2) Tokens and basic exporters.
213
- 3) Contrast and alpha scale.
214
-
215
125
  ## License
216
126
 
217
- MIT
127
+ MIT — see `LICENSE`.
package/biome.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
3
+ "formatter": {
4
+ "enabled": true,
5
+ "indentStyle": "space",
6
+ "indentWidth": 2,
7
+ "lineWidth": 100
8
+ },
9
+ "linter": {
10
+ "enabled": true,
11
+ "rules": {
12
+ "recommended": true
13
+ }
14
+ },
15
+ "files": {
16
+ "ignoreUnknown": true,
17
+ "includes": [
18
+ "**/*.js",
19
+ "**/*.ts",
20
+ "**/*.tsx",
21
+ "**/*.css",
22
+ "**/*.scss",
23
+ "**/*.html",
24
+ "**/*.json",
25
+ "**/*.md",
26
+ "!**/supabase/functions",
27
+ "!**/node_modules",
28
+ "!**/dist",
29
+ "!**/build",
30
+ "!**/coverage",
31
+ "!**/ios",
32
+ "!**/android",
33
+ "!**/.git/",
34
+ "!**/.vscode",
35
+ "!**/*.d.ts",
36
+ "!**/*.spec.ts",
37
+ "!**/*.test.ts",
38
+ "!**/.github",
39
+ "!**/examples",
40
+ "!**/tests"
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ export declare class CliUsageError extends Error {
2
+ name: string;
3
+ }
4
+ export declare const HELP_TEXT = "palette-kit <command>\n\nCommands:\n palette-kit init [--force] [--path <dir>]\n palette-kit build [--config <path>] [--outDir <dir>] [--report]\n\nOptions:\n -h, --help Show help\n -v, --version Show version\n";
5
+ export type ParsedArgs = {
6
+ command?: string;
7
+ help: boolean;
8
+ version: boolean;
9
+ flags: Record<string, string | boolean>;
10
+ };
11
+ export declare const COMMANDS: readonly ["init", "build"];
12
+ export declare const parseArgs: (argv: string[]) => ParsedArgs;
@@ -0,0 +1,56 @@
1
+ export class CliUsageError extends Error {
2
+ name = "CliUsageError";
3
+ }
4
+ export const HELP_TEXT = `palette-kit <command>
5
+
6
+ Commands:
7
+ palette-kit init [--force] [--path <dir>]
8
+ palette-kit build [--config <path>] [--outDir <dir>] [--report]
9
+
10
+ Options:
11
+ -h, --help Show help
12
+ -v, --version Show version
13
+ `;
14
+ export const COMMANDS = ["init", "build"];
15
+ export const parseArgs = (argv) => {
16
+ const first = argv[0];
17
+ const command = first && !first.startsWith("-") ? first : undefined;
18
+ const rest = command ? argv.slice(1) : argv;
19
+ const flags = {};
20
+ let i = 0;
21
+ while (i < rest.length) {
22
+ const value = rest[i];
23
+ if (value === undefined)
24
+ break;
25
+ if (value === "-h" || value === "--help") {
26
+ flags.help = true;
27
+ i += 1;
28
+ continue;
29
+ }
30
+ if (value === "-v" || value === "--version") {
31
+ flags.version = true;
32
+ i += 1;
33
+ continue;
34
+ }
35
+ if (value.startsWith("--")) {
36
+ const key = value.slice(2);
37
+ const next = rest[i + 1];
38
+ if (next && !next.startsWith("--")) {
39
+ flags[key] = next;
40
+ i += 2;
41
+ }
42
+ else {
43
+ flags[key] = true;
44
+ i += 1;
45
+ }
46
+ continue;
47
+ }
48
+ throw new CliUsageError(`Unknown argument: ${value}`);
49
+ }
50
+ return {
51
+ command,
52
+ help: Boolean(flags.help),
53
+ version: Boolean(flags.version),
54
+ flags,
55
+ };
56
+ };
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { CliUsageError, parseArgs } from "./args.js";
3
+ describe("cli args", () => {
4
+ it("parses init command", () => {
5
+ const parsed = parseArgs(["init"]);
6
+ expect(parsed.command).toBe("init");
7
+ });
8
+ it("parses build command with flags", () => {
9
+ const parsed = parseArgs(["build", "--config", "custom.ts", "--outDir", "out", "--report"]);
10
+ expect(parsed.command).toBe("build");
11
+ expect(parsed.flags.config).toBe("custom.ts");
12
+ expect(parsed.flags.outDir).toBe("out");
13
+ expect(parsed.flags.report).toBe(true);
14
+ });
15
+ it("recognizes help and version flags", () => {
16
+ expect(parseArgs(["--help"]).help).toBe(true);
17
+ expect(parseArgs(["--version"]).version).toBe(true);
18
+ });
19
+ it("throws on unknown arguments", () => {
20
+ expect(() => parseArgs(["build", "wat"])).toThrow(CliUsageError);
21
+ });
22
+ });
@@ -0,0 +1,87 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`codegen tokens > generates navigable tokens.ts and tokens.d.ts 1`] = `
4
+ "/* This file is generated by palette-kit build (Phase 7). */
5
+
6
+ export const tokens = {
7
+ bg: {
8
+ /**
9
+ * App background.
10
+ * @token bg.app
11
+ * @category bg
12
+ */
13
+ app: "bg.app",
14
+ },
15
+ border: {
16
+ /**
17
+ * Default border color.
18
+ * @token border.default
19
+ * @category border
20
+ */
21
+ "default": "border.default",
22
+ },
23
+ text: {
24
+ /**
25
+ * Primary text on standard surfaces.
26
+ * @token text.primary
27
+ * @category text
28
+ * @states hover
29
+ */
30
+ primary: "text.primary",
31
+ },
32
+ } as const;
33
+
34
+ export const tokenNames = [
35
+ "bg.app",
36
+ "border.default",
37
+ "text.primary"
38
+ ] as const;
39
+
40
+ export type TokenName = (typeof tokenNames)[number];
41
+
42
+ export type ColorRole = TokenName;
43
+ "
44
+ `;
45
+
46
+ exports[`codegen tokens > generates navigable tokens.ts and tokens.d.ts 2`] = `
47
+ "/* This file is generated by palette-kit build (Phase 7). */
48
+
49
+ export declare const tokens: {
50
+ bg: {
51
+ /**
52
+ * App background.
53
+ * @token bg.app
54
+ * @category bg
55
+ */
56
+ app: "bg.app";
57
+ };
58
+ border: {
59
+ /**
60
+ * Default border color.
61
+ * @token border.default
62
+ * @category border
63
+ */
64
+ "default": "border.default";
65
+ };
66
+ text: {
67
+ /**
68
+ * Primary text on standard surfaces.
69
+ * @token text.primary
70
+ * @category text
71
+ * @states hover
72
+ */
73
+ primary: "text.primary";
74
+ };
75
+ };
76
+
77
+ export declare const tokenNames: readonly [
78
+ "bg.app",
79
+ "border.default",
80
+ "text.primary"
81
+ ];
82
+
83
+ export type TokenName = (typeof tokenNames)[number];
84
+
85
+ export type ColorRole = TokenName;
86
+ "
87
+ `;
@@ -0,0 +1,12 @@
1
+ import type { TokenRegistry } from "../../types/index.js";
2
+ export type GeneratedTokens = {
3
+ tokenNames: string[];
4
+ tokensTs: string;
5
+ tokensDts: string;
6
+ };
7
+ /**
8
+ * Generate `dist/palette/tokens.ts` and `dist/palette/tokens.d.ts` sources.
9
+ *
10
+ * Output is deterministic and derived from the token registry.
11
+ */
12
+ export declare const generateTokenArtifacts: (registry: TokenRegistry) => GeneratedTokens;
@@ -0,0 +1,139 @@
1
+ const RESERVED_WORDS = new Set([
2
+ "break",
3
+ "case",
4
+ "catch",
5
+ "class",
6
+ "const",
7
+ "continue",
8
+ "debugger",
9
+ "default",
10
+ "delete",
11
+ "do",
12
+ "else",
13
+ "export",
14
+ "extends",
15
+ "finally",
16
+ "for",
17
+ "function",
18
+ "if",
19
+ "import",
20
+ "in",
21
+ "instanceof",
22
+ "new",
23
+ "return",
24
+ "super",
25
+ "switch",
26
+ "this",
27
+ "throw",
28
+ "try",
29
+ "typeof",
30
+ "var",
31
+ "void",
32
+ "while",
33
+ "with",
34
+ "yield",
35
+ ]);
36
+ const isValidIdentifier = (value) => {
37
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value))
38
+ return false;
39
+ return !RESERVED_WORDS.has(value);
40
+ };
41
+ const toPropertyKey = (key) => (isValidIdentifier(key) ? key : JSON.stringify(key));
42
+ const indentLines = (lines, spaces) => {
43
+ const prefix = " ".repeat(spaces);
44
+ return lines.map((line) => (line.length ? `${prefix}${line}` : line));
45
+ };
46
+ const escapeJsDoc = (value) => value.replace(/\*\//g, "*\\/");
47
+ const buildTokenJsDocLines = (tokenName, token) => {
48
+ const lines = [];
49
+ if (token.description?.trim()) {
50
+ lines.push(escapeJsDoc(token.description.trim()));
51
+ }
52
+ lines.push(`@token ${tokenName}`);
53
+ if (token.category?.trim()) {
54
+ lines.push(`@category ${escapeJsDoc(token.category.trim())}`);
55
+ }
56
+ const states = token.states ? Object.keys(token.states).sort() : [];
57
+ if (states.length > 0) {
58
+ lines.push(`@states ${states.join(", ")}`);
59
+ }
60
+ return ["/**", ...lines.map((line) => ` * ${line}`), " */"];
61
+ };
62
+ const buildTree = (registry) => {
63
+ const root = { children: new Map() };
64
+ const entries = Object.entries(registry.tokens).sort(([a], [b]) => a.localeCompare(b));
65
+ for (const [tokenName, token] of entries) {
66
+ const segments = tokenName.split(".").filter(Boolean);
67
+ if (segments.length === 0)
68
+ continue;
69
+ let node = root;
70
+ for (const segment of segments) {
71
+ let child = node.children.get(segment);
72
+ if (!child) {
73
+ child = { children: new Map() };
74
+ node.children.set(segment, child);
75
+ }
76
+ node = child;
77
+ }
78
+ node.token = token;
79
+ }
80
+ return root;
81
+ };
82
+ const renderTreeTs = (node, tokenNamePrefix) => {
83
+ const lines = ["{"];
84
+ const keys = [...node.children.keys()].sort((a, b) => a.localeCompare(b));
85
+ for (const key of keys) {
86
+ const child = node.children.get(key);
87
+ if (!child)
88
+ continue;
89
+ const fullTokenName = tokenNamePrefix ? `${tokenNamePrefix}.${key}` : key;
90
+ const propKey = toPropertyKey(key);
91
+ if (child.token && child.children.size === 0) {
92
+ lines.push(...buildTokenJsDocLines(fullTokenName, child.token));
93
+ lines.push(`${propKey}: "${fullTokenName}",`);
94
+ continue;
95
+ }
96
+ const childLines = renderTreeTs(child, fullTokenName);
97
+ lines.push(`${propKey}: ${childLines[0]}`);
98
+ lines.push(...indentLines(childLines.slice(1), 2));
99
+ lines[lines.length - 1] = `${lines[lines.length - 1]},`;
100
+ }
101
+ lines.push("}");
102
+ return lines;
103
+ };
104
+ const renderTreeDts = (node, tokenNamePrefix) => {
105
+ const lines = ["{"];
106
+ const keys = [...node.children.keys()].sort((a, b) => a.localeCompare(b));
107
+ for (const key of keys) {
108
+ const child = node.children.get(key);
109
+ if (!child)
110
+ continue;
111
+ const fullTokenName = tokenNamePrefix ? `${tokenNamePrefix}.${key}` : key;
112
+ const propKey = toPropertyKey(key);
113
+ if (child.token && child.children.size === 0) {
114
+ lines.push(...buildTokenJsDocLines(fullTokenName, child.token));
115
+ lines.push(`${propKey}: "${fullTokenName}";`);
116
+ continue;
117
+ }
118
+ const childLines = renderTreeDts(child, fullTokenName);
119
+ lines.push(`${propKey}: ${childLines[0]}`);
120
+ lines.push(...indentLines(childLines.slice(1), 2));
121
+ lines[lines.length - 1] = `${lines[lines.length - 1]};`;
122
+ }
123
+ lines.push("}");
124
+ return lines;
125
+ };
126
+ /**
127
+ * Generate `dist/palette/tokens.ts` and `dist/palette/tokens.d.ts` sources.
128
+ *
129
+ * Output is deterministic and derived from the token registry.
130
+ */
131
+ export const generateTokenArtifacts = (registry) => {
132
+ const tokenNames = Object.keys(registry.tokens).sort((a, b) => a.localeCompare(b));
133
+ const tree = buildTree(registry);
134
+ const tokensObject = renderTreeTs(tree, "");
135
+ const tokensDtsObject = renderTreeDts(tree, "");
136
+ const tokensTs = `/* This file is generated by palette-kit build (Phase 7). */\n\nexport const tokens = ${tokensObject.join("\n")} as const;\n\nexport const tokenNames = ${JSON.stringify(tokenNames, null, 2)} as const;\n\nexport type TokenName = (typeof tokenNames)[number];\n\nexport type ColorRole = TokenName;\n`;
137
+ const tokensDts = `/* This file is generated by palette-kit build (Phase 7). */\n\nexport declare const tokens: ${tokensDtsObject.join("\n")};\n\nexport declare const tokenNames: readonly ${JSON.stringify(tokenNames, null, 2)};\n\nexport type TokenName = (typeof tokenNames)[number];\n\nexport type ColorRole = TokenName;\n`;
138
+ return { tokenNames, tokensTs, tokensDts };
139
+ };
@@ -0,0 +1 @@
1
+ export {};