css-calipers 1.0.0 → 1.1.0-beta.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 (207) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +70 -424
  3. package/dist/cjs/color/formats/display-p3/display-p3.js +25 -0
  4. package/dist/cjs/color/formats/escalate.js +41 -0
  5. package/dist/cjs/color/formats/hex/hex.js +28 -0
  6. package/dist/cjs/color/formats/hexAlpha/hexAlpha.js +25 -0
  7. package/dist/cjs/color/formats/hsl/hsl.js +23 -0
  8. package/dist/cjs/color/formats/hwb/hwb.js +24 -0
  9. package/dist/cjs/color/formats/index.js +36 -0
  10. package/dist/cjs/color/formats/internals.js +93 -0
  11. package/dist/cjs/color/formats/lab/lab.js +24 -0
  12. package/dist/cjs/color/formats/lch/lch.js +23 -0
  13. package/dist/cjs/color/formats/oklab/oklab.js +24 -0
  14. package/dist/cjs/color/formats/oklch/oklch.js +28 -0
  15. package/dist/cjs/color/formats/resolve.js +16 -0
  16. package/dist/cjs/color/formats/rgb/rgb.js +29 -0
  17. package/dist/cjs/color/formats/rgba/rgba.js +27 -0
  18. package/dist/cjs/color/formats/types.js +11 -0
  19. package/dist/cjs/color/index.js +619 -0
  20. package/dist/cjs/color/types.js +2 -0
  21. package/dist/cjs/core.js +11 -8
  22. package/dist/cjs/corpus.js +53 -0
  23. package/dist/cjs/css-values/cssValues.js +78 -0
  24. package/dist/cjs/css-values/index.js +36 -0
  25. package/dist/cjs/css-values/multi.js +290 -0
  26. package/dist/cjs/css-values/propertyMap.js +2 -0
  27. package/dist/cjs/css-values/spec.js +197 -0
  28. package/dist/cjs/default.js +17 -0
  29. package/dist/cjs/factory.js +1 -1
  30. package/dist/cjs/float.js +110 -0
  31. package/dist/cjs/index.js +29 -9
  32. package/dist/cjs/integer.js +114 -0
  33. package/dist/cjs/internal/createCoreApi.js +103 -38
  34. package/dist/cjs/internal/errors.js +13 -14
  35. package/dist/cjs/internal/toPlainDecimal.js +31 -0
  36. package/dist/cjs/measurements.js +41 -0
  37. package/dist/cjs/ratio.js +57 -22
  38. package/dist/cjs/scalar.js +9 -0
  39. package/dist/cjs/units/absolute.js +8 -8
  40. package/dist/cjs/units/angle.js +5 -5
  41. package/dist/cjs/units/container.js +7 -7
  42. package/dist/cjs/units/font-relative.js +13 -13
  43. package/dist/cjs/units/frequency.js +3 -3
  44. package/dist/cjs/units/grid.js +2 -2
  45. package/dist/cjs/units/index.js +9 -9
  46. package/dist/cjs/units/percent.js +4 -4
  47. package/dist/cjs/units/resolution.js +4 -4
  48. package/dist/cjs/units/time.js +3 -3
  49. package/dist/cjs/units/viewport-dynamic.js +7 -7
  50. package/dist/cjs/units/viewport-large.js +7 -7
  51. package/dist/cjs/units/viewport-small.js +7 -7
  52. package/dist/cjs/units/viewport.js +7 -7
  53. package/dist/esm/color/formats/display-p3/display-p3.d.ts +10 -0
  54. package/dist/esm/color/formats/display-p3/display-p3.d.ts.map +1 -0
  55. package/dist/esm/color/formats/display-p3/display-p3.js +22 -0
  56. package/dist/esm/color/formats/escalate.d.ts +10 -0
  57. package/dist/esm/color/formats/escalate.d.ts.map +1 -0
  58. package/dist/esm/color/formats/escalate.js +37 -0
  59. package/dist/esm/color/formats/hex/hex.d.ts +9 -0
  60. package/dist/esm/color/formats/hex/hex.d.ts.map +1 -0
  61. package/dist/esm/color/formats/hex/hex.js +25 -0
  62. package/dist/esm/color/formats/hexAlpha/hexAlpha.d.ts +9 -0
  63. package/dist/esm/color/formats/hexAlpha/hexAlpha.d.ts.map +1 -0
  64. package/dist/esm/color/formats/hexAlpha/hexAlpha.js +22 -0
  65. package/dist/esm/color/formats/hsl/hsl.d.ts +8 -0
  66. package/dist/esm/color/formats/hsl/hsl.d.ts.map +1 -0
  67. package/dist/esm/color/formats/hsl/hsl.js +20 -0
  68. package/dist/esm/color/formats/hwb/hwb.d.ts +9 -0
  69. package/dist/esm/color/formats/hwb/hwb.d.ts.map +1 -0
  70. package/dist/esm/color/formats/hwb/hwb.js +21 -0
  71. package/dist/esm/color/formats/index.d.ts +24 -0
  72. package/dist/esm/color/formats/index.d.ts.map +1 -0
  73. package/dist/esm/color/formats/index.js +32 -0
  74. package/dist/esm/color/formats/internals.d.ts +35 -0
  75. package/dist/esm/color/formats/internals.d.ts.map +1 -0
  76. package/dist/esm/color/formats/internals.js +78 -0
  77. package/dist/esm/color/formats/lab/lab.d.ts +9 -0
  78. package/dist/esm/color/formats/lab/lab.d.ts.map +1 -0
  79. package/dist/esm/color/formats/lab/lab.js +21 -0
  80. package/dist/esm/color/formats/lch/lch.d.ts +8 -0
  81. package/dist/esm/color/formats/lch/lch.d.ts.map +1 -0
  82. package/dist/esm/color/formats/lch/lch.js +20 -0
  83. package/dist/esm/color/formats/oklab/oklab.d.ts +9 -0
  84. package/dist/esm/color/formats/oklab/oklab.d.ts.map +1 -0
  85. package/dist/esm/color/formats/oklab/oklab.js +21 -0
  86. package/dist/esm/color/formats/oklch/oklch.d.ts +13 -0
  87. package/dist/esm/color/formats/oklch/oklch.d.ts.map +1 -0
  88. package/dist/esm/color/formats/oklch/oklch.js +25 -0
  89. package/dist/esm/color/formats/resolve.d.ts +15 -0
  90. package/dist/esm/color/formats/resolve.d.ts.map +1 -0
  91. package/dist/esm/color/formats/resolve.js +12 -0
  92. package/dist/esm/color/formats/rgb/rgb.d.ts +11 -0
  93. package/dist/esm/color/formats/rgb/rgb.d.ts.map +1 -0
  94. package/dist/esm/color/formats/rgb/rgb.js +26 -0
  95. package/dist/esm/color/formats/rgba/rgba.d.ts +9 -0
  96. package/dist/esm/color/formats/rgba/rgba.d.ts.map +1 -0
  97. package/dist/esm/color/formats/rgba/rgba.js +24 -0
  98. package/dist/esm/color/formats/types.d.ts +48 -0
  99. package/dist/esm/color/formats/types.d.ts.map +1 -0
  100. package/dist/esm/color/formats/types.js +7 -0
  101. package/dist/esm/color/index.d.ts +81 -0
  102. package/dist/esm/color/index.d.ts.map +1 -0
  103. package/dist/esm/color/index.js +609 -0
  104. package/dist/esm/color/types.d.ts +294 -0
  105. package/dist/esm/color/types.d.ts.map +1 -0
  106. package/dist/esm/color/types.js +1 -0
  107. package/dist/esm/core.d.ts +76 -270
  108. package/dist/esm/core.d.ts.map +1 -1
  109. package/dist/esm/core.js +5 -7
  110. package/dist/esm/corpus.d.ts +26 -0
  111. package/dist/esm/corpus.d.ts.map +1 -0
  112. package/dist/esm/corpus.js +37 -0
  113. package/dist/esm/css-values/cssValues.d.ts +60 -0
  114. package/dist/esm/css-values/cssValues.d.ts.map +1 -0
  115. package/dist/esm/css-values/cssValues.js +74 -0
  116. package/dist/esm/css-values/index.d.ts +6 -0
  117. package/dist/esm/css-values/index.d.ts.map +1 -0
  118. package/dist/esm/css-values/index.js +12 -0
  119. package/dist/esm/css-values/multi.d.ts +188 -0
  120. package/dist/esm/css-values/multi.d.ts.map +1 -0
  121. package/dist/esm/css-values/multi.js +281 -0
  122. package/dist/esm/css-values/propertyMap.d.ts +39 -0
  123. package/dist/esm/css-values/propertyMap.d.ts.map +1 -0
  124. package/dist/esm/css-values/propertyMap.js +1 -0
  125. package/dist/esm/css-values/spec.d.ts +36 -0
  126. package/dist/esm/css-values/spec.d.ts.map +1 -0
  127. package/dist/esm/css-values/spec.js +194 -0
  128. package/dist/esm/default.d.ts +267 -0
  129. package/dist/esm/default.d.ts.map +1 -0
  130. package/dist/esm/default.js +14 -0
  131. package/dist/esm/factory.d.ts +1 -1
  132. package/dist/esm/factory.d.ts.map +1 -1
  133. package/dist/esm/factory.js +3 -3
  134. package/dist/esm/float.d.ts +35 -0
  135. package/dist/esm/float.d.ts.map +1 -0
  136. package/dist/esm/float.js +104 -0
  137. package/dist/esm/index.d.ts +11 -9
  138. package/dist/esm/index.d.ts.map +1 -1
  139. package/dist/esm/index.js +19 -9
  140. package/dist/esm/integer.d.ts +36 -0
  141. package/dist/esm/integer.d.ts.map +1 -0
  142. package/dist/esm/integer.js +108 -0
  143. package/dist/esm/internal/buildMeasurementCreationError.tests.d.ts +1 -1
  144. package/dist/esm/internal/buildMeasurementCreationError.tests.d.ts.map +1 -1
  145. package/dist/esm/internal/createCoreApi.d.ts +9 -1
  146. package/dist/esm/internal/createCoreApi.d.ts.map +1 -1
  147. package/dist/esm/internal/createCoreApi.js +103 -38
  148. package/dist/esm/internal/createUnitsApi.d.ts +1 -1
  149. package/dist/esm/internal/createUnitsApi.d.ts.map +1 -1
  150. package/dist/esm/internal/errors.d.ts +3 -3
  151. package/dist/esm/internal/errors.d.ts.map +1 -1
  152. package/dist/esm/internal/errors.js +13 -14
  153. package/dist/esm/internal/toPlainDecimal.d.ts +2 -0
  154. package/dist/esm/internal/toPlainDecimal.d.ts.map +1 -0
  155. package/dist/esm/internal/toPlainDecimal.js +27 -0
  156. package/dist/esm/measurements.d.ts +16 -0
  157. package/dist/esm/measurements.d.ts.map +1 -0
  158. package/dist/esm/measurements.js +25 -0
  159. package/dist/esm/ratio.d.ts +14 -5
  160. package/dist/esm/ratio.d.ts.map +1 -1
  161. package/dist/esm/ratio.js +57 -22
  162. package/dist/esm/scalar.d.ts +14 -0
  163. package/dist/esm/scalar.d.ts.map +1 -0
  164. package/dist/esm/scalar.js +5 -0
  165. package/dist/esm/units/absolute.d.ts +1 -1
  166. package/dist/esm/units/absolute.d.ts.map +1 -1
  167. package/dist/esm/units/absolute.js +1 -1
  168. package/dist/esm/units/angle.d.ts +1 -1
  169. package/dist/esm/units/angle.d.ts.map +1 -1
  170. package/dist/esm/units/angle.js +1 -1
  171. package/dist/esm/units/container.d.ts +1 -1
  172. package/dist/esm/units/container.d.ts.map +1 -1
  173. package/dist/esm/units/container.js +1 -1
  174. package/dist/esm/units/font-relative.d.ts +1 -1
  175. package/dist/esm/units/font-relative.d.ts.map +1 -1
  176. package/dist/esm/units/font-relative.js +1 -1
  177. package/dist/esm/units/frequency.d.ts +1 -1
  178. package/dist/esm/units/frequency.d.ts.map +1 -1
  179. package/dist/esm/units/frequency.js +1 -1
  180. package/dist/esm/units/grid.d.ts +1 -1
  181. package/dist/esm/units/grid.d.ts.map +1 -1
  182. package/dist/esm/units/grid.js +1 -1
  183. package/dist/esm/units/index.d.ts +9 -9
  184. package/dist/esm/units/index.d.ts.map +1 -1
  185. package/dist/esm/units/index.js +9 -9
  186. package/dist/esm/units/percent.d.ts +1 -1
  187. package/dist/esm/units/percent.d.ts.map +1 -1
  188. package/dist/esm/units/percent.js +1 -1
  189. package/dist/esm/units/resolution.d.ts +1 -1
  190. package/dist/esm/units/resolution.d.ts.map +1 -1
  191. package/dist/esm/units/resolution.js +1 -1
  192. package/dist/esm/units/time.d.ts +1 -1
  193. package/dist/esm/units/time.d.ts.map +1 -1
  194. package/dist/esm/units/time.js +1 -1
  195. package/dist/esm/units/viewport-dynamic.d.ts +1 -1
  196. package/dist/esm/units/viewport-dynamic.d.ts.map +1 -1
  197. package/dist/esm/units/viewport-dynamic.js +1 -1
  198. package/dist/esm/units/viewport-large.d.ts +1 -1
  199. package/dist/esm/units/viewport-large.d.ts.map +1 -1
  200. package/dist/esm/units/viewport-large.js +1 -1
  201. package/dist/esm/units/viewport-small.d.ts +1 -1
  202. package/dist/esm/units/viewport-small.d.ts.map +1 -1
  203. package/dist/esm/units/viewport-small.js +1 -1
  204. package/dist/esm/units/viewport.d.ts +1 -1
  205. package/dist/esm/units/viewport.d.ts.map +1 -1
  206. package/dist/esm/units/viewport.js +1 -1
  207. package/package.json +54 -12
@@ -0,0 +1,619 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.color = exports.createColor = exports.defaultColorConfig = exports.defaultFormatPriority = exports.resolve = exports.defineColorSpace = exports.colorFormats = exports.storeColor = exports.parseColor = void 0;
18
+ const culori_1 = require("culori");
19
+ const formats_1 = require("./formats");
20
+ Object.defineProperty(exports, "colorFormats", { enumerable: true, get: function () { return formats_1.colorFormats; } });
21
+ Object.defineProperty(exports, "defineColorSpace", { enumerable: true, get: function () { return formats_1.defineColorSpace; } });
22
+ const escalate_1 = require("./formats/escalate");
23
+ const internals_1 = require("./formats/internals");
24
+ const resolve_1 = require("./formats/resolve");
25
+ __exportStar(require("./types"), exports);
26
+ /* A resolved color privately carries its store here so it can be re-wrapped by
27
+ * `parseColor` (lib-agnostic: this holds our `Store`, never a culori name). */
28
+ const STORED = Symbol('color.store');
29
+ /* ============================================================================
30
+ * INPUT (Part 1 of the book): parse a `ColorInput` into the canonical store.
31
+ *
32
+ * The store is the boundary between input and the rest of the book. Translatable
33
+ * values become a culori color object; symbolic keywords are kept verbatim and
34
+ * pass through on emit (modifying them throws later, not here). Storage
35
+ * normalization and output formatting are separate, later steps.
36
+ * ==========================================================================*/
37
+ /* The symbolic allowlist, in canonical casing. Runtime mirror of the
38
+ * `Symbolic*` types; `satisfies` keeps each entry a valid keyword. */
39
+ const SYMBOLIC_KEYWORDS = [
40
+ 'currentColor',
41
+ // system colors (current, CSS Color 4)
42
+ 'Canvas',
43
+ 'CanvasText',
44
+ 'LinkText',
45
+ 'VisitedText',
46
+ 'ActiveText',
47
+ 'ButtonFace',
48
+ 'ButtonText',
49
+ 'ButtonBorder',
50
+ 'Field',
51
+ 'FieldText',
52
+ 'Highlight',
53
+ 'HighlightText',
54
+ 'SelectedItem',
55
+ 'SelectedItemText',
56
+ 'Mark',
57
+ 'MarkText',
58
+ 'GrayText',
59
+ 'AccentColor',
60
+ 'AccentColorText',
61
+ // system colors (deprecated, Appendix A - still accepted)
62
+ 'ActiveBorder',
63
+ 'ActiveCaption',
64
+ 'AppWorkspace',
65
+ 'Background',
66
+ 'ButtonHighlight',
67
+ 'ButtonShadow',
68
+ 'CaptionText',
69
+ 'InactiveBorder',
70
+ 'InactiveCaption',
71
+ 'InactiveCaptionText',
72
+ 'InfoBackground',
73
+ 'InfoText',
74
+ 'Menu',
75
+ 'MenuText',
76
+ 'Scrollbar',
77
+ 'ThreeDDarkShadow',
78
+ 'ThreeDFace',
79
+ 'ThreeDHighlight',
80
+ 'ThreeDLightShadow',
81
+ 'ThreeDShadow',
82
+ 'Window',
83
+ 'WindowFrame',
84
+ 'WindowText',
85
+ // CSS-wide cascade keywords
86
+ 'inherit',
87
+ 'initial',
88
+ 'unset',
89
+ 'revert',
90
+ 'revert-layer',
91
+ ];
92
+ /** lower-cased keyword -> canonical casing, for case-insensitive matching. */
93
+ const SYMBOLIC_BY_LOWER = new Map(SYMBOLIC_KEYWORDS.map((keyword) => [
94
+ keyword.toLowerCase(),
95
+ keyword,
96
+ ]));
97
+ const isColorObject = (input) => typeof input === 'object' && input !== null && 'space' in input;
98
+ const isCuloriColor = (input) => typeof input === 'object' && input !== null && 'mode' in input;
99
+ const cloneStore = (store) => store.kind === 'symbolic'
100
+ ? { kind: 'symbolic', keyword: store.keyword }
101
+ : { kind: 'color', color: { ...store.color } };
102
+ /**
103
+ * Adapt a structured `ColorObject` to a culori color object. We accept
104
+ * CSS-authoring ranges (rgb 0-255, hsl/hwb percentages 0-100) and convert them
105
+ * to culori's normalized ranges; lab/lch/oklab/oklch channels are 1:1.
106
+ */
107
+ const colorObjectToCulori = (input) => {
108
+ switch (input.space) {
109
+ case 'rgb': {
110
+ const color = {
111
+ mode: 'rgb',
112
+ r: input.r / 255,
113
+ g: input.g / 255,
114
+ b: input.b / 255,
115
+ };
116
+ if (input.alpha !== undefined)
117
+ color.alpha = input.alpha;
118
+ return color;
119
+ }
120
+ case 'hsl': {
121
+ const color = {
122
+ mode: 'hsl',
123
+ h: input.h,
124
+ s: input.s / 100,
125
+ l: input.l / 100,
126
+ };
127
+ if (input.alpha !== undefined)
128
+ color.alpha = input.alpha;
129
+ return color;
130
+ }
131
+ case 'hwb': {
132
+ const color = {
133
+ mode: 'hwb',
134
+ h: input.h,
135
+ w: input.w / 100,
136
+ b: input.b / 100,
137
+ };
138
+ if (input.alpha !== undefined)
139
+ color.alpha = input.alpha;
140
+ return color;
141
+ }
142
+ case 'lab': {
143
+ const color = {
144
+ mode: 'lab',
145
+ l: input.l,
146
+ a: input.a,
147
+ b: input.b,
148
+ };
149
+ if (input.alpha !== undefined)
150
+ color.alpha = input.alpha;
151
+ return color;
152
+ }
153
+ case 'lch': {
154
+ const color = {
155
+ mode: 'lch',
156
+ l: input.l,
157
+ c: input.c,
158
+ h: input.h,
159
+ };
160
+ if (input.alpha !== undefined)
161
+ color.alpha = input.alpha;
162
+ return color;
163
+ }
164
+ case 'oklab': {
165
+ const color = {
166
+ mode: 'oklab',
167
+ l: input.l,
168
+ a: input.a,
169
+ b: input.b,
170
+ };
171
+ if (input.alpha !== undefined)
172
+ color.alpha = input.alpha;
173
+ return color;
174
+ }
175
+ case 'oklch': {
176
+ const color = {
177
+ mode: 'oklch',
178
+ l: input.l,
179
+ c: input.c,
180
+ h: input.h,
181
+ };
182
+ if (input.alpha !== undefined)
183
+ color.alpha = input.alpha;
184
+ return color;
185
+ }
186
+ }
187
+ };
188
+ /**
189
+ * A plugin-aware string parser: tries the built-in parse FIRST (symbolic keyword,
190
+ * then culori CSS parse), and only if that declines tries each plugin's `parse` in
191
+ * order. Returns a `Store` or `undefined` to decline. Built-in precedence is intact;
192
+ * a plugin only ever claims a string the built-ins reject. The module-level parser is
193
+ * this with no plugins; a factory instance binds its own plugin list.
194
+ */
195
+ const parseString = (input, plugins) => {
196
+ var _a;
197
+ const canonical = SYMBOLIC_BY_LOWER.get(input.toLowerCase());
198
+ if (canonical !== undefined) {
199
+ return { kind: 'symbolic', keyword: canonical };
200
+ }
201
+ const builtIn = (0, culori_1.parse)(input);
202
+ if (builtIn !== undefined) {
203
+ return { kind: 'color', color: builtIn };
204
+ }
205
+ // built-ins declined: offer the string to each plugin's input bridge, in order.
206
+ for (const plugin of plugins) {
207
+ const bridged = (_a = plugin.parse) === null || _a === void 0 ? void 0 : _a.call(plugin, input);
208
+ if (bridged !== undefined) {
209
+ return { kind: 'color', color: bridged };
210
+ }
211
+ }
212
+ return undefined;
213
+ };
214
+ /**
215
+ * Parse any `ColorInput` into the canonical store, optionally consulting custom
216
+ * format plugins for strings the built-ins reject.
217
+ *
218
+ * - string: a symbolic keyword (case-insensitive) -> passthrough store; otherwise
219
+ * parsed as a CSS color; if that declines, each plugin's `parse` is tried; an input
220
+ * no one claims throws.
221
+ * - structured `ColorObject` -> adapted to a culori color.
222
+ * - re-wrap: an existing culori color (internal/engine use) -> reused as-is.
223
+ */
224
+ const parseInput = (input, plugins) => {
225
+ if (typeof input === 'string') {
226
+ const store = parseString(input, plugins);
227
+ if (store === undefined) {
228
+ throw new Error(`color: unparseable color string "${input}"`);
229
+ }
230
+ return store;
231
+ }
232
+ // re-wrap: an existing ResolvedColor carries its store privately.
233
+ if (typeof input === 'object' &&
234
+ input !== null &&
235
+ STORED in input) {
236
+ return cloneStore(input[STORED]);
237
+ }
238
+ if (isColorObject(input)) {
239
+ return { kind: 'color', color: colorObjectToCulori(input) };
240
+ }
241
+ if (isCuloriColor(input)) {
242
+ return { kind: 'color', color: input };
243
+ }
244
+ throw new Error('color: unsupported color input');
245
+ };
246
+ /**
247
+ * Parse any `ColorInput` into the canonical store (built-in formats only). The public,
248
+ * instance-agnostic parser. Factory instances thread their own plugin-aware parser
249
+ * internally; this stays the bare built-in entry point.
250
+ */
251
+ const parseColor = (input) => parseInput(input, []);
252
+ exports.parseColor = parseColor;
253
+ /* ============================================================================
254
+ * STORAGE (Part 2 of the book): normalize the canonical store.
255
+ *
256
+ * Every translatable color is converted to OKLCH, so the rest of the book works
257
+ * from one perceptually-uniform representation: modifications become direct
258
+ * coordinate edits (l/c/h), and outputs convert out of OKLCH. Symbolic keywords
259
+ * have no value to normalize and pass through untouched.
260
+ *
261
+ * This is pure culori math and runs anywhere JS runs - it needs no browser
262
+ * `oklch()` support. Browser compatibility is purely an output-step concern
263
+ * (which format you emit), not a storage concern.
264
+ * ==========================================================================*/
265
+ /** Normalize a parsed store into the canonical OKLCH working space. */
266
+ const storeColor = (store) => {
267
+ if (store.kind === 'symbolic') {
268
+ return store;
269
+ }
270
+ return { kind: 'color', color: (0, internals_1.toOklch)(store.color) };
271
+ };
272
+ exports.storeColor = storeColor;
273
+ // Render a (non-fully-transparent) color in the requested format by dispatching to
274
+ // that format's descriptor. The descriptors own the per-format render; this single
275
+ // dispatch replaced the old per-format switch.
276
+ const serialize = (color, format, cfg) => (0, resolve_1.asDescriptor)(format, formats_1.colorFormats).render(color, cfg);
277
+ const renderColor = (color, format, cfg) => {
278
+ // fully-transparent rendering policy (alpha 0): a keyword, or white/black at 0.
279
+ if ((0, internals_1.alphaOf)(color) === 0) {
280
+ if (cfg.transparent === 'keyword')
281
+ return 'transparent';
282
+ // `preserve`: keep the colour's own RGB at alpha 0 (the truest fade), i.e.
283
+ // render the stored colour as-is, exactly like a non-transparent value.
284
+ if (cfg.transparent === 'preserve') {
285
+ return serialize(color, format, cfg);
286
+ }
287
+ const substitute = cfg.transparent === 'white'
288
+ ? { mode: 'rgb', r: 1, g: 1, b: 1, alpha: 0 }
289
+ : { mode: 'rgb', r: 0, g: 0, b: 0, alpha: 0 };
290
+ return serialize(substitute, format, cfg);
291
+ }
292
+ return serialize(color, format, cfg);
293
+ };
294
+ const render = (store, output, cfg) => {
295
+ // symbolic colors pass through: their keyword emits for any requested format.
296
+ if (store.kind === 'symbolic') {
297
+ return store.keyword;
298
+ }
299
+ // resolve the output (a single format, or a priority list) to one faithful format.
300
+ const format = (0, escalate_1.chooseFormat)(store.color, output);
301
+ return renderColor(store.color, format, cfg);
302
+ };
303
+ const wrapHue = (h) => ((h % 360) + 360) % 360;
304
+ /* Map a colour into the sRGB gamut (a no-op for in-gamut colours). Separable blend
305
+ * modes are sRGB-defined, so a wide-gamut operand is mapped here before blending; an
306
+ * in-gamut colour passes through losslessly. */
307
+ const toSrgbGamut = (0, culori_1.toGamut)('rgb', 'oklch');
308
+ const builtInBinding = {
309
+ parse: exports.parseColor,
310
+ plugins: [],
311
+ };
312
+ const resolveWith = (store, cfg, binding) => {
313
+ // a format selector: same color, new configured output, still finished via .css().
314
+ const withFormat = (output) => resolveWith(store, { ...cfg, output }, binding);
315
+ // a transparency selector: same colour and format, new alpha-0 render policy.
316
+ const withTransparent = (transparent) => resolveWith(store, { ...cfg, transparent }, binding);
317
+ // a modification: a NEW color (re-normalized to OKLCH), same config.
318
+ const withColor = (raw) => resolveWith((0, exports.storeColor)({ kind: 'color', color: raw }), cfg, binding);
319
+ const self = () => resolveWith(store, cfg, binding);
320
+ // modifications are only valid on a translatable color; on a symbolic keyword
321
+ // they are a violation (throw in dev / warn in prod) and leave the color as-is.
322
+ const modifiable = (op) => {
323
+ if (store.kind === 'symbolic') {
324
+ (0, internals_1.violate)(`color: cannot ${op} a symbolic color '${store.keyword}'`, cfg.strictness);
325
+ return undefined;
326
+ }
327
+ return store.color;
328
+ };
329
+ const targetColor = (target) => {
330
+ const resolved = (0, exports.storeColor)(binding.parse(target));
331
+ if (resolved.kind !== 'color') {
332
+ (0, internals_1.violate)('color: cannot mix with a symbolic color', cfg.strictness);
333
+ return undefined;
334
+ }
335
+ return resolved.color;
336
+ };
337
+ const blend = (op, target, ratio, mode, makeBaseSolid) => {
338
+ const c = modifiable(op);
339
+ if (c === undefined)
340
+ return undefined;
341
+ const t = targetColor(target);
342
+ if (t === undefined)
343
+ return undefined;
344
+ const base = makeBaseSolid ? { ...c, alpha: 1 } : c;
345
+ return (0, culori_1.interpolate)([
346
+ base,
347
+ t,
348
+ ], mode)((0, internals_1.clamp01)(ratio));
349
+ };
350
+ // a separable blend: compose `target` onto this colour with a sRGB-defined blend
351
+ // mode. Both operands are gamut-mapped into sRGB (a no-op for in-gamut colours) and
352
+ // blended there; the result is re-normalized to OKLCH by `withColor`.
353
+ const applyBlend = (op, target, mode) => {
354
+ const c = modifiable(op);
355
+ if (c === undefined)
356
+ return undefined;
357
+ const t = targetColor(target);
358
+ if (t === undefined)
359
+ return undefined;
360
+ return (0, culori_1.blend)([
361
+ toSrgbGamut(c),
362
+ toSrgbGamut(t),
363
+ ], mode, 'rgb');
364
+ };
365
+ // adjust this colour's OKLCH lightness toward black/white to reach a WCAG contrast
366
+ // ratio against `target`, moving the least amount needed. Used by `ensureContrast`.
367
+ const liftToContrast = (base, target, ratio) => {
368
+ const at = (l) => (0, culori_1.wcagContrast)({ ...base, l }, target);
369
+ // already sufficient: leave it.
370
+ if (at(base.l) >= ratio)
371
+ return base;
372
+ // pick the endpoint (black L=0 or white L=1) that yields the most contrast.
373
+ const endpoint = at(1) >= at(0) ? 1 : 0;
374
+ // unreachable even at the endpoint: return the best-achievable colour.
375
+ if (at(endpoint) < ratio)
376
+ return { ...base, l: endpoint };
377
+ // contrast is monotonic between base.l and the endpoint: binary-search the
378
+ // smallest move that still meets the ratio. `lo` stays insufficient, `hi`
379
+ // sufficient, regardless of numeric direction.
380
+ let lo = base.l;
381
+ let hi = endpoint;
382
+ for (let i = 0; i < 40; i += 1) {
383
+ const mid = (lo + hi) / 2;
384
+ if (at(mid) >= ratio)
385
+ hi = mid;
386
+ else
387
+ lo = mid;
388
+ }
389
+ return { ...base, l: hi };
390
+ };
391
+ // a format override selector: same colour, new configured output (a single
392
+ // format or a priority list), still finished via .css(). Mirrors `withFormat`
393
+ // and `withTransparent`; the output threads straight into `cfg.output`.
394
+ const formatAs = (formats) => resolveWith(store, { ...cfg, output: formats }, binding);
395
+ const result = {
396
+ css: () => render(store, cfg.output, cfg),
397
+ formatAs,
398
+ rgba: () => withFormat(formats_1.colorFormats.rgba),
399
+ rgb: () => withFormat(formats_1.colorFormats.rgb),
400
+ hex: () => withFormat(formats_1.colorFormats.hex),
401
+ hexAlpha: () => withFormat(formats_1.colorFormats.hexAlpha),
402
+ hsl: () => withFormat(formats_1.colorFormats.hsl),
403
+ hwb: () => withFormat(formats_1.colorFormats.hwb),
404
+ lab: () => withFormat(formats_1.colorFormats.lab),
405
+ lch: () => withFormat(formats_1.colorFormats.lch),
406
+ oklab: () => withFormat(formats_1.colorFormats.oklab),
407
+ oklch: () => withFormat(formats_1.colorFormats.oklch),
408
+ displayP3: () => withFormat(formats_1.colorFormats.displayP3),
409
+ transparentAs: (mode) => withTransparent(mode),
410
+ alpha: ((value) => {
411
+ var _a;
412
+ if (value === undefined) {
413
+ return store.kind === 'color' ? ((_a = store.color.alpha) !== null && _a !== void 0 ? _a : 1) : 1;
414
+ }
415
+ const c = modifiable('set the alpha of');
416
+ return c === undefined
417
+ ? self()
418
+ : withColor({ ...c, alpha: value });
419
+ }),
420
+ darken: (amount = 0.1) => {
421
+ const c = modifiable('darken');
422
+ return c === undefined
423
+ ? self()
424
+ : withColor({ ...c, l: c.l * (1 - (0, internals_1.clamp01)(amount)) });
425
+ },
426
+ lighten: (amount = 0.1) => {
427
+ const c = modifiable('lighten');
428
+ return c === undefined
429
+ ? self()
430
+ : withColor({ ...c, l: c.l + (1 - c.l) * (0, internals_1.clamp01)(amount) });
431
+ },
432
+ brighten: (amount = 0.1) => result.lighten(amount),
433
+ saturate: (amount = 0.1) => {
434
+ const c = modifiable('saturate');
435
+ return c === undefined
436
+ ? self()
437
+ : withColor({ ...c, c: c.c * (1 + Math.max(0, amount)) });
438
+ },
439
+ desaturate: (amount = 0.1) => {
440
+ const c = modifiable('desaturate');
441
+ return c === undefined
442
+ ? self()
443
+ : withColor({ ...c, c: c.c * (1 - (0, internals_1.clamp01)(amount)) });
444
+ },
445
+ hueShift: (value) => {
446
+ var _a;
447
+ const c = modifiable('hue-shift');
448
+ return c === undefined
449
+ ? self()
450
+ : withColor({
451
+ ...c,
452
+ h: wrapHue(((_a = c.h) !== null && _a !== void 0 ? _a : 0) + value.getValue()),
453
+ });
454
+ },
455
+ setLightness: (value) => {
456
+ const c = modifiable('set the lightness of');
457
+ return c === undefined ? self() : withColor({ ...c, l: value });
458
+ },
459
+ setChroma: (value) => {
460
+ const c = modifiable('set the chroma of');
461
+ return c === undefined ? self() : withColor({ ...c, c: value });
462
+ },
463
+ setHue: (value) => {
464
+ const c = modifiable('set the hue of');
465
+ return c === undefined
466
+ ? self()
467
+ : withColor({ ...c, h: wrapHue(value.getValue()) });
468
+ },
469
+ complement: () => {
470
+ var _a;
471
+ const c = modifiable('complement');
472
+ return c === undefined
473
+ ? self()
474
+ : withColor({ ...c, h: wrapHue(((_a = c.h) !== null && _a !== void 0 ? _a : 0) + 180) });
475
+ },
476
+ invert: (amount = 1) => {
477
+ const c = modifiable('invert');
478
+ if (c === undefined)
479
+ return self();
480
+ const a = (0, internals_1.clamp01)(amount);
481
+ // L + ((1 - L) - L) * a; a=1 -> 1 - L, a=0.5 -> 0.5 for any L.
482
+ return withColor({ ...c, l: c.l + (1 - 2 * c.l) * a });
483
+ },
484
+ grayscale: (amount = 1) => {
485
+ const c = modifiable('grayscale');
486
+ return c === undefined
487
+ ? self()
488
+ : withColor({ ...c, c: c.c * (1 - (0, internals_1.clamp01)(amount)) });
489
+ },
490
+ blend: (other, mode) => {
491
+ const blended = applyBlend('blend', other, mode);
492
+ return blended === undefined ? self() : withColor(blended);
493
+ },
494
+ ensureContrast: (other, ratio = 4.5) => {
495
+ const c = modifiable('ensure the contrast of');
496
+ if (c === undefined)
497
+ return self();
498
+ const t = targetColor(other);
499
+ if (t === undefined)
500
+ return self();
501
+ return withColor(liftToContrast(c, t, ratio));
502
+ },
503
+ contrast: (other) => {
504
+ const c = modifiable('measure the contrast of');
505
+ if (c === undefined)
506
+ return Number.NaN;
507
+ const t = targetColor(other);
508
+ if (t === undefined)
509
+ return Number.NaN;
510
+ return (0, culori_1.wcagContrast)(c, t);
511
+ },
512
+ mix: (target, ratio = 0.5, mode = 'oklch') => {
513
+ const mixed = blend('mix', target, ratio, mode, false);
514
+ return mixed === undefined ? self() : withColor(mixed);
515
+ },
516
+ mixSolid: (target, ratio = 0.5, mode = 'oklch') => {
517
+ const mixed = blend('mix', target, ratio, mode, true);
518
+ return mixed === undefined ? self() : withColor(mixed);
519
+ },
520
+ mixWithAlpha: (target, ratio = 0.5, alpha, mode = 'oklch') => {
521
+ var _a;
522
+ const c = modifiable('mix');
523
+ if (c === undefined)
524
+ return self();
525
+ const mixed = blend('mix', target, ratio, mode, true);
526
+ if (mixed === undefined)
527
+ return self();
528
+ return withColor({ ...mixed, alpha: (_a = alpha !== null && alpha !== void 0 ? alpha : c.alpha) !== null && _a !== void 0 ? _a : 1 });
529
+ },
530
+ solid: () => {
531
+ const c = modifiable('solidify');
532
+ return c === undefined ? self() : withColor({ ...c, alpha: 1 });
533
+ },
534
+ clone: () => self(),
535
+ };
536
+ // carry the store privately so this result can be re-wrapped via `color(result)`.
537
+ Object.defineProperty(result, STORED, { value: store });
538
+ // Named custom-format selectors: one lazy getter per registered plugin (e.g.
539
+ // `.zoo`), mirroring the built-in format selectors and the `transparentAs` chain.
540
+ // Lazy (a getter, not an eager property) so a child result is built only on access,
541
+ // avoiding eager construction recursion. Each returns this same colour reconfigured
542
+ // to render through that plugin's descriptor.
543
+ for (const plugin of binding.plugins) {
544
+ Object.defineProperty(result, plugin.format, {
545
+ enumerable: false,
546
+ get: () => withFormat(plugin),
547
+ });
548
+ }
549
+ return result;
550
+ };
551
+ /**
552
+ * Resolve a store + config into an immutable, navigable `ResolvedColor`, using the
553
+ * built-in parser and no custom plugins. The public, instance-agnostic resolver
554
+ * (re-exported as `resolveColor`). Factory instances use `resolveWith` internally to
555
+ * thread their own plugin-aware parser and named selectors.
556
+ */
557
+ const resolve = (store, cfg) => resolveWith(store, cfg, builtInBinding);
558
+ exports.resolve = resolve;
559
+ /**
560
+ * The default output priority: the simplest faithful format first. With no argument
561
+ * `.css()` escalates down this ladder to the first format that holds the color (see
562
+ * `formats/README.md`). Overridable per book via `publishBookColor({ config })`.
563
+ *
564
+ * Pared to one format per distinct output space, most popular first (see
565
+ * `docs/color-format-popularity.md`). Every other built-in format is a redundant
566
+ * cover of one of these three, so it would never win escalation; it stays reachable
567
+ * via a named selector (`.hwb()`, ...) or `.formatAs(colorFormats.x)`.
568
+ * - `hex`: solids (opaque sRGB). ~76% of real-world colour declarations.
569
+ * - `rgba`: translucent sRGB. The dominant alpha form (rgba >> #rrggbbaa, hsla).
570
+ * - `oklch`: the unbounded floor, holds any colour (P3 and beyond), and is the
571
+ * primitive's own storage space, so it is the zero-loss fallback.
572
+ */
573
+ exports.defaultFormatPriority = [
574
+ formats_1.colorFormats.hex,
575
+ formats_1.colorFormats.rgba,
576
+ formats_1.colorFormats.oklch,
577
+ ];
578
+ /** The colour primitive's defaults. */
579
+ exports.defaultColorConfig = {
580
+ output: exports.defaultFormatPriority,
581
+ strictness: 'auto',
582
+ transparent: 'keyword',
583
+ omitOpaqueAlpha: false,
584
+ };
585
+ /**
586
+ * Build a `color()`-shaped function bound to the given custom format plugins. The
587
+ * instance parser tries the built-in parse first, then each plugin's `parse` in order;
588
+ * the plugins are valid `output` descriptors and resolve through `asDescriptor`. The
589
+ * returned result exposes a typed lazy selector per plugin format.
590
+ */
591
+ const createColor = (config) => {
592
+ const plugins = config.formats;
593
+ // the per-instance registry: built-ins, then plugins (a plugin may shadow a built-in
594
+ // name; built-in parse precedence is unaffected, it is consulted before plugins).
595
+ const registry = {
596
+ ...formats_1.colorFormats,
597
+ };
598
+ for (const plugin of plugins) {
599
+ registry[plugin.format] = plugin;
600
+ }
601
+ // the plugin-aware parser threaded through every parse this instance does.
602
+ const instanceParse = (input) => parseInput(input, plugins);
603
+ const binding = { parse: instanceParse, plugins };
604
+ const instance = (input, callConfig) => resolveWith((0, exports.storeColor)(instanceParse(input)), callConfig === undefined
605
+ ? exports.defaultColorConfig
606
+ : { ...exports.defaultColorConfig, ...callConfig }, binding);
607
+ return Object.assign(instance, { formats: registry });
608
+ };
609
+ exports.createColor = createColor;
610
+ /**
611
+ * `color(input, config?)`: the native colour input, alongside `m()`, `r()`, `i()`,
612
+ * `f()`. Parses + normalizes `input` to OKLCH and resolves it with the default
613
+ * config (override per call via `config`). Mirrors how `m()` wraps a measurement.
614
+ *
615
+ * It is `createColor({ formats: [] })` at defaults: the module-level instance is just
616
+ * the factory with no custom plugins, so the default and the factory share one
617
+ * construction path. Typed as the bare `(input, config?) => ResolvedColor` surface.
618
+ */
619
+ exports.color = (0, exports.createColor)({ formats: [] });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/cjs/core.js CHANGED
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.simplifyRatio = exports.reduceRatio = exports.toFloat = exports.ratioToFloat = exports.parseRatio = exports.normalizeRatio = exports.isRatio = exports.r = exports.setErrorConfig = exports.getErrorConfig = exports.assertCondition = exports.assertUnit = exports.hasCssMethod = exports.makeUnitAssert = exports.makeUnitGuard = exports.makeUnitHelperFromDefinition = exports.makeUnitHelper = exports.measurementUnitMetadata = exports.measurementMax = exports.measurementMin = exports.assertMatchingUnits = exports.isMeasurement = exports.m = void 0;
4
- const createCoreApi_1 = require("./internal/createCoreApi");
5
- const errors_1 = require("./internal/errors");
3
+ exports.isInteger = exports.isFloat = exports.i = exports.hardenInteger = exports.hardenFloat = exports.f = exports.toFloat = exports.simplifyRatio = exports.reduceRatio = exports.ratioToFloat = exports.r = exports.parseRatio = exports.normalizeRatio = exports.isRatio = void 0;
4
+ const float_1 = require("./float");
5
+ Object.defineProperty(exports, "f", { enumerable: true, get: function () { return float_1.f; } });
6
+ Object.defineProperty(exports, "hardenFloat", { enumerable: true, get: function () { return float_1.hardenFloat; } });
7
+ Object.defineProperty(exports, "isFloat", { enumerable: true, get: function () { return float_1.isFloat; } });
8
+ const integer_1 = require("./integer");
9
+ Object.defineProperty(exports, "hardenInteger", { enumerable: true, get: function () { return integer_1.hardenInteger; } });
10
+ Object.defineProperty(exports, "i", { enumerable: true, get: function () { return integer_1.i; } });
11
+ Object.defineProperty(exports, "isInteger", { enumerable: true, get: function () { return integer_1.isInteger; } });
6
12
  const ratio_1 = require("./ratio");
7
- Object.defineProperty(exports, "r", { enumerable: true, get: function () { return ratio_1.r; } });
8
13
  Object.defineProperty(exports, "isRatio", { enumerable: true, get: function () { return ratio_1.isRatio; } });
9
14
  Object.defineProperty(exports, "normalizeRatio", { enumerable: true, get: function () { return ratio_1.normalizeRatio; } });
10
15
  Object.defineProperty(exports, "parseRatio", { enumerable: true, get: function () { return ratio_1.parseRatio; } });
16
+ Object.defineProperty(exports, "r", { enumerable: true, get: function () { return ratio_1.r; } });
11
17
  Object.defineProperty(exports, "ratioToFloat", { enumerable: true, get: function () { return ratio_1.ratioToFloat; } });
12
- Object.defineProperty(exports, "toFloat", { enumerable: true, get: function () { return ratio_1.toFloat; } });
13
18
  Object.defineProperty(exports, "reduceRatio", { enumerable: true, get: function () { return ratio_1.reduceRatio; } });
14
19
  Object.defineProperty(exports, "simplifyRatio", { enumerable: true, get: function () { return ratio_1.simplifyRatio; } });
15
- const defaultErrorStore = (0, errors_1.createErrorConfigStore)();
16
- const coreApi = (0, createCoreApi_1.createCoreApi)(defaultErrorStore);
17
- exports.m = coreApi.m, exports.isMeasurement = coreApi.isMeasurement, exports.assertMatchingUnits = coreApi.assertMatchingUnits, exports.measurementMin = coreApi.measurementMin, exports.measurementMax = coreApi.measurementMax, exports.measurementUnitMetadata = coreApi.measurementUnitMetadata, exports.makeUnitHelper = coreApi.makeUnitHelper, exports.makeUnitHelperFromDefinition = coreApi.makeUnitHelperFromDefinition, exports.makeUnitGuard = coreApi.makeUnitGuard, exports.makeUnitAssert = coreApi.makeUnitAssert, exports.hasCssMethod = coreApi.hasCssMethod, exports.assertUnit = coreApi.assertUnit, exports.assertCondition = coreApi.assertCondition, exports.getErrorConfig = coreApi.getErrorConfig, exports.setErrorConfig = coreApi.setErrorConfig;
20
+ Object.defineProperty(exports, "toFloat", { enumerable: true, get: function () { return ratio_1.toFloat; } });