chromonym 1.4.0 → 1.4.1

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 (2) hide show
  1. package/README.md +40 -32
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -17,10 +17,12 @@
17
17
  [![CI](https://github.com/simiancraft/chromonym/actions/workflows/ci.yml/badge.svg)](https://github.com/simiancraft/chromonym/actions/workflows/ci.yml)
18
18
  [![codecov](https://codecov.io/gh/simiancraft/chromonym/branch/main/graph/badge.svg)](https://codecov.io/gh/simiancraft/chromonym)
19
19
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
20
- [![semantic-release: conventional](https://img.shields.io/badge/semantic--release-conventional-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)
21
- [![Checked with Biome](https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev)
22
20
 
23
- **Tree-shakeable color naming for TypeScript.** Identify, resolve, and convert colors across CSS, X11, and Pantone — with 5 perceptual distance metrics. Add your own colorspaces and metrics as you need them.
21
+ <p align="center">
22
+ <code>identify</code> &nbsp;•&nbsp; <code>resolve</code> &nbsp;•&nbsp; <code>convert</code>
23
+ </p>
24
+
25
+ **Tree-shakeable color naming for TypeScript** — across CSS, X11, and Pantone, with six distance metrics (four perceptual).
24
26
 
25
27
  <p align="center">
26
28
  <a href="https://simiancraft.github.io/chromonym/">
@@ -28,13 +30,17 @@
28
30
  </a>
29
31
  </p>
30
32
 
31
- Built as a *color-strategy machine*: the identification and resolution mechanism is the same across every colorspace, so plugging in a new set (RAL, HKS, NCS, or a custom brand palette) is one file. For color *manipulation* (mixing, scales, gamut mapping), reach for [`chroma-js`](https://gka.github.io/chroma.js/) or [`color.js`](https://colorjs.io/) — chromonym is the tool for *naming*.
33
+ Because "it's sort of magenta-ish, maybe?" doesn't copy-paste into code, and the nearest Pantone to your brand hex is something you should be able to answer without opening a swatch book.
34
+
35
+ The identification and resolution mechanism is the same across every colorspace, so future additions (RAL, HKS, NCS, or similar) are one file each. There's no public `registerColorspace` API yet — open an issue if you need a specific set.
36
+
37
+ For color *manipulation* (mixing, scales, gamut mapping), reach for [`chroma-js`](https://gka.github.io/chroma.js/) or [`color.js`](https://colorjs.io/) — chromonym is the tool for *naming*.
32
38
 
33
39
  ```ts
34
40
  import { identify, resolve, convert } from 'chromonym';
35
41
 
36
42
  // What Pantone is this brand color closest to? (perceptually, via CIEDE2000)
37
- identify('#E20074', { colorspace: 'pantone' }) // '213C'
43
+ identify('#E20074', { colorspace: 'pantone' }) // '213C' — T-Mobile magenta
38
44
 
39
45
  // Resolve any Pantone code back to RGB — prefix, spacing, case all fine
40
46
  resolve('Pantone 185 C', { colorspace: 'pantone' })// '#e4002b'
@@ -45,12 +51,16 @@ convert({ h: 120, s: 100, l: 50 }, { format: 'HEX' }) // '#00ff00'
45
51
 
46
52
  // The classic: nearest CSS name
47
53
  identify('#ff8080') // 'lightcoral'
54
+ identify('#bada55') // 'yellowgreen' — also the best-spelled hex
55
+ identify('#663399') // 'rebeccapurple'
48
56
  ```
49
57
 
50
58
  ## Install
51
59
 
52
60
  ```sh
53
61
  bun add chromonym
62
+ pnpm add chromonym
63
+ yarn add chromonym
54
64
  npm install chromonym
55
65
  ```
56
66
 
@@ -63,16 +73,17 @@ Three primary functions. All accept an optional `opts` object; defaults fire whe
63
73
  **Color → name.** Finds the nearest-match name in the chosen colorspace using the selected perceptual distance metric.
64
74
 
65
75
  ```ts
66
- function identify(
76
+ function identify<C extends ColorspaceName = 'web'>(
67
77
  input: ColorInput,
68
- opts?: { colorspace?: ColorspaceName; metric?: DistanceMetric },
69
- ): string | null
78
+ opts?: { colorspace?: C; metric?: DistanceMetric },
79
+ ): WebColorName | X11ColorName | PantoneColorName | null
70
80
  ```
71
81
 
72
82
  - **Default colorspace**: `'web'`.
73
83
  - **Default metric**: picked per colorspace — `'deltaE76'` for web and x11 (well-separated palettes), `'deltaE2000'` for pantone (dense, perceptually-packed). Override freely via `metric`.
74
84
  - Returns the matched name, or `null` if input is unrecognized (`detectFormat` returns `'UNKNOWN'`).
75
- - Ties are broken by the first entry encountered in the colorspace data.
85
+ - Return type narrows based on the `colorspace` option — e.g. `identify(hex, { colorspace: 'pantone' })` returns `PantoneColorName | null`, and the no-args call returns `WebColorName | null`.
86
+ - Ties go to whichever color was defined first — deterministic across runs, but not semantically meaningful.
76
87
 
77
88
  ```ts
78
89
  identify('#ff0000') // 'red'
@@ -152,20 +163,24 @@ import { web, x11, pantone } from 'chromonym';
152
163
  web.crimson // '#dc143c'
153
164
  ```
154
165
 
166
+ *Trivia:* `web.rebeccapurple` (`#663399`) entered CSS Color 4 in 2014 in memory of [Rebecca Meyer](https://meyerweb.com/eric/thoughts/2014/06/19/rebeccapurple/). The X11 set ships every gray name twice (`gray`, `grey`) plus numbered variants up to 99, so `x11.gray73` is a real key (`#bababa`). Yes, these are Greys. No, not the kind that visit during sleep paralysis.
167
+
155
168
  ## Distance metrics
156
169
 
157
- `identify` picks the nearest-named color by comparing your input to every entry in the chosen colorspace. *How* it measures "nearest" is controlled by `metric`. Five options, roughly ordered from fast-and-approximate to slow-and-correct:
170
+ *Six ways to ask "how different are two colors?"*
171
+
172
+ `identify` picks the nearest-named color by comparing your input to every entry in the chosen colorspace. *How* it measures "nearest" is controlled by `metric`. Six options, roughly ordered from fast-and-approximate to slow-and-correct:
158
173
 
159
174
  | Metric | What it does | When to use |
160
175
  |---|---|---|
161
176
  | `'euclidean-srgb'` | Raw `sqrt(Δr² + Δg² + Δb²)` in sRGB. Fastest. Not perceptually uniform — a unit of distance doesn't mean a unit of "visual difference." | Tight perf budgets; well-separated palettes where you just need the obvious answer. |
162
177
  | `'euclidean-linear'` | Same math, but on linearized (gamma-removed) RGB. Somewhat better in dark regions. Still not perceptual. | Physical-light mixing contexts; incremental upgrade from sRGB Euclidean. |
163
- | `'deltaE76'` | Euclidean in CIELAB (CIE 1976). Simple perceptual metric. 1 ΔE ≈ "just noticeable difference" for most of the gamut. | **Default for web and x11.** Sweet spot: meaningful perceptual accuracy, low cost. |
178
+ | `'deltaE76'` | Euclidean in CIELAB (CIE 1976). Simple perceptual metric. 1 ΔE ≈ "just noticeable difference" for most of the gamut. | **Default for web and x11.** Meaningful perceptual accuracy at low cost. |
164
179
  | `'deltaE94'` | ΔE76 + chroma/hue weighting (CIE 1994). Fixes "saturated colors feel too far apart." | When ΔE76 is over-penalizing saturated matches in your use case. |
165
180
  | `'deltaE2000'` | Full CIEDE2000 formula with blue/purple rotation correction. Industry standard for print, design tools, Pantone workflows. | **Default for pantone.** Use whenever accuracy matters, especially in the blue region where ΔE76/94 break down. |
166
- | `'deltaEok'` | Euclidean distance in OKLAB (Björn Ottosson, 2020). OKLAB is perceptually uniform by construction — no weighting formula needed. Strictly more uniform than CIELAB; often a better nearest-match than ΔE2000 in saturated-blue regions, and cheaper to compute. | Modern default if you want perceptual accuracy without the CIEDE2000 rotation-term complexity. |
181
+ | `'deltaEok'` | Euclidean distance in OKLAB (Björn Ottosson, 2020). OKLAB is perceptually uniform by construction — no weighting formula needed. Strictly more uniform than CIELAB; often a better nearest-match than ΔE2000 in saturated-blue regions, and cheaper to compute. | Use when you want perceptual accuracy without the CIEDE2000 rotation-term complexity. |
167
182
 
168
- Each metric trades cost for accuracy — the full scan over 907 Pantone entries is well under 1 ms even with ΔE2000, so for interactive UIs there's no practical reason not to use the most accurate metric. Batch-processing millions of colors is where you'd drop down.
183
+ Each metric trades cost for accuracy — the full scan over 907 Pantone entries is well under 1 ms even with ΔE2000. For interactive UIs, cost is usually irrelevant; drop to a cheaper metric when batch-processing millions of colors or when you need specific tie-breaking behavior.
169
184
 
170
185
  ```ts
171
186
  // Defaults: deltaE76 for web/x11, deltaE2000 for pantone.
@@ -178,12 +193,10 @@ identify('#ff8080', { metric: 'deltaE2000' }) // most accurate
178
193
  identify('#ff8080', { colorspace: 'pantone', metric: 'euclidean-srgb' }) // force fast for Pantone
179
194
  ```
180
195
 
181
- The low-level `rgbaToPantone` always uses `'deltaE2000'` — appropriate for Pantone matching, where this is what the industry tools use.
196
+ The low-level `rgbaToPantone` always uses `'deltaE2000'`.
182
197
 
183
198
  ## Formats
184
199
 
185
- Format keys are SCREAMING_CAPS — they are used as dispatch keys internally.
186
-
187
200
  | Key | Input accepted | Output shape |
188
201
  |---|---|---|
189
202
  | `'HEX'` | `'#rgb'`, `'#rrggbb'`, `'#rrggbbaa'` | `'#rrggbb'` string |
@@ -193,11 +206,9 @@ Format keys are SCREAMING_CAPS — they are used as dispatch keys internally.
193
206
  | `'HSV'` | `'hsv(h, s%, v%)'`, `{ h, s, v }` | `'hsv(h, s%, v%)'` string |
194
207
  | `'PANTONE'` | `'185 C'`, `'185C'`, `'Pantone 185 C'`, etc. | `'185C'`-style string |
195
208
 
196
- The runtime set `COLOR_FORMATS` is exported for enumeration / membership checks.
197
-
198
209
  ## Error handling
199
210
 
200
- The three dispatchers are asymmetric — chosen deliberately, but worth knowing:
211
+ The three dispatchers are asymmetric:
201
212
 
202
213
  | Function | Unrecognized / unknown input | Rationale |
203
214
  |---|---|---|
@@ -205,7 +216,7 @@ The three dispatchers are asymmetric — chosen deliberately, but worth knowing:
205
216
  | `resolve(name)` | returns `null` | lookup-flavored; name may legitimately not exist |
206
217
  | `convert(input)` | **throws** | parser-flavored; malformed color is a caller error |
207
218
 
208
- Low-level converters (`hexToRgba`, `rgbToRgba`, `hslToRgba`, `hsvToRgba`, `pantoneToRgba`) all **throw** on malformed input. The `rgbaTo*` direction never throws. The nearest-match returner `rgbaToPantone` always returns *some* Pantone code.
219
+ Low-level converters (`hexToRgba`, `rgbToRgba`, `hslToRgba`, `hsvToRgba`, `pantoneToRgba`) all **throw** on malformed input. The `rgbaTo*` direction never throws. `rgbaToPantone` is the special case: it always returns a Pantone code regardless of how distant the nearest match is, so don't treat the return as evidence the input was Pantone-like — check the ΔE yourself if that matters.
209
220
 
210
221
  If you want a `null`-returning variant of `convert`, wrap it:
211
222
 
@@ -230,16 +241,15 @@ const tryConvert = (input: ColorInput, opts = {}) => {
230
241
 
231
242
  ## Types
232
243
 
233
- All types are re-exported from the root:
244
+ Re-exported from the root barrel — `import type { ... } from 'chromonym'`:
234
245
 
235
- ```ts
236
- import type {
237
- ColorInput, ColorValue, ColorFormat, ColorspaceName,
238
- HexColor, Rgba, RgbInput, RgbaInput, HslInput, HsvInput, PantoneCode,
239
- Colorspace,
240
- WebColorName, X11ColorName, PantoneColorName,
241
- } from 'chromonym';
242
- ```
246
+ | Category | Types |
247
+ |---|---|
248
+ | Input / output unions | `ColorInput`, `ColorValue` |
249
+ | Format & colorspace keys | `ColorFormat`, `ColorspaceName` |
250
+ | Per-format shapes | `HexColor`, `Rgba`, `RgbInput`, `RgbaInput`, `HslInput`, `HsvInput`, `PantoneCode` |
251
+ | Palette container | `Colorspace` |
252
+ | Color-name unions | `WebColorName`, `X11ColorName`, `PantoneColorName` |
243
253
 
244
254
  ## Development
245
255
 
@@ -255,15 +265,13 @@ Regenerating colorspace data:
255
265
 
256
266
  ```sh
257
267
  bun run scripts/generate-x11.ts # reads /usr/share/X11/rgb.txt
258
- bun run scripts/generate-pantone.ts # reads color_library (MIT dependency)
268
+ bun run scripts/generate-pantone.ts # reads the color_library npm package (MIT © Radu Dragan)
259
269
  ```
260
270
 
261
271
  ## Coverage
262
272
 
263
273
  [![Coverage sunburst](https://codecov.io/github/simiancraft/chromonym/graphs/sunburst.svg?token=HYWM6G66YE)](https://codecov.io/github/simiancraft/chromonym)
264
274
 
265
- Each ring is a directory; each leaf is a file. Green is covered, red is gaps.
266
-
267
275
  ## See also
268
276
 
269
277
  chromonym deliberately limits its scope to color *naming* (resolving and identifying) and lightweight conversions. For other color-related work, reach for tools that do those things well:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromonym",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Tree-shakeable color-naming primitive for TypeScript. Identify, resolve, and convert colors across CSS, X11, and Pantone colorspaces — with perceptual distance metrics (CIEDE2000).",
5
5
  "keywords": [
6
6
  "color",