chromonym 3.1.1 → 3.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.
- package/NOTICE.md +243 -1
- package/README.md +51 -43
- package/dist/conversions/rgb.d.ts +7 -1
- package/dist/conversions/rgb.d.ts.map +1 -1
- package/dist/conversions/rgb.js +11 -2
- package/dist/conversions/rgb.js.map +1 -1
- package/dist/convert.d.ts +73 -21
- package/dist/convert.d.ts.map +1 -1
- package/dist/convert.js +13 -2
- package/dist/convert.js.map +1 -1
- package/dist/defineColorPalette.d.ts +53 -0
- package/dist/defineColorPalette.d.ts.map +1 -0
- package/dist/defineColorPalette.js +103 -0
- package/dist/defineColorPalette.js.map +1 -0
- package/dist/detectFormat.d.ts +26 -3
- package/dist/detectFormat.d.ts.map +1 -1
- package/dist/detectFormat.js +20 -0
- package/dist/detectFormat.js.map +1 -1
- package/dist/identify.d.ts +63 -17
- package/dist/identify.d.ts.map +1 -1
- package/dist/identify.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/indexing.d.ts +13 -3
- package/dist/indexing.d.ts.map +1 -1
- package/dist/indexing.js +67 -19
- package/dist/indexing.js.map +1 -1
- package/dist/palettes/crayola.d.ts +16 -0
- package/dist/palettes/crayola.d.ts.map +1 -1
- package/dist/palettes/crayola.js +16 -0
- package/dist/palettes/crayola.js.map +1 -1
- package/dist/palettes/fs595b.d.ts +467 -0
- package/dist/palettes/fs595b.d.ts.map +1 -0
- package/dist/palettes/fs595b.js +256 -0
- package/dist/palettes/fs595b.js.map +1 -0
- package/dist/palettes/fs595c.d.ts +1224 -0
- package/dist/palettes/fs595c.d.ts.map +1 -0
- package/dist/palettes/fs595c.js +633 -0
- package/dist/palettes/fs595c.js.map +1 -0
- package/dist/palettes/isccNbs.d.ts +562 -0
- package/dist/palettes/isccNbs.d.ts.map +1 -0
- package/dist/palettes/isccNbs.js +300 -0
- package/dist/palettes/isccNbs.js.map +1 -0
- package/dist/palettes/nbs.d.ts +578 -0
- package/dist/palettes/nbs.d.ts.map +1 -0
- package/dist/palettes/nbs.js +309 -0
- package/dist/palettes/nbs.js.map +1 -0
- package/dist/palettes/ncs.d.ts +3949 -0
- package/dist/palettes/ncs.d.ts.map +1 -0
- package/dist/palettes/ncs.js +2011 -0
- package/dist/palettes/ncs.js.map +1 -0
- package/dist/palettes/ntc.d.ts +3170 -0
- package/dist/palettes/ntc.d.ts.map +1 -0
- package/dist/palettes/ntc.js +1602 -0
- package/dist/palettes/ntc.js.map +1 -0
- package/dist/palettes/pantone.d.ts +18 -1
- package/dist/palettes/pantone.d.ts.map +1 -1
- package/dist/palettes/pantone.js +18 -1
- package/dist/palettes/pantone.js.map +1 -1
- package/dist/palettes/resene.d.ts +2802 -0
- package/dist/palettes/resene.d.ts.map +1 -0
- package/dist/palettes/resene.js +1422 -0
- package/dist/palettes/resene.js.map +1 -0
- package/dist/palettes/web.d.ts +10 -0
- package/dist/palettes/web.d.ts.map +1 -1
- package/dist/palettes/web.js +10 -0
- package/dist/palettes/web.js.map +1 -1
- package/dist/palettes/x11.d.ts +15 -10
- package/dist/palettes/x11.d.ts.map +1 -1
- package/dist/palettes/x11.js +15 -10
- package/dist/palettes/x11.js.map +1 -1
- package/dist/palettes/xkcd.d.ts +1880 -0
- package/dist/palettes/xkcd.d.ts.map +1 -0
- package/dist/palettes/xkcd.js +955 -0
- package/dist/palettes/xkcd.js.map +1 -0
- package/dist/resolve.d.ts +58 -14
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +32 -16
- package/dist/resolve.js.map +1 -1
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +44 -5
package/NOTICE.md
CHANGED
|
@@ -67,7 +67,7 @@ Crayola's published approximations.
|
|
|
67
67
|
|
|
68
68
|
### Scope
|
|
69
69
|
|
|
70
|
-
The shipped set is
|
|
70
|
+
The shipped set is 84 entries: the current 64-standard box, the
|
|
71
71
|
iconic fluorescent "Neon" range, and widely-recognized modern
|
|
72
72
|
additions. It is not exhaustive: historical, retired, specialty-box
|
|
73
73
|
(Gem Tones, Heads 'N Tails, Multicultural, etc.), and regional-variant
|
|
@@ -93,6 +93,248 @@ the public domain.
|
|
|
93
93
|
The `web` palette mirrors the CSS Color Module Level 4 named-color
|
|
94
94
|
set, which is a public specification.
|
|
95
95
|
|
|
96
|
+
## Resene
|
|
97
|
+
|
|
98
|
+
The `resene` palette (1378 entries) is derived from the Raku
|
|
99
|
+
module **`Color::Names::Resene`** in the
|
|
100
|
+
[`thundergnat/Color-Names`](https://github.com/thundergnat/Color-Names)
|
|
101
|
+
repository, which digitizes the named-color catalog published by
|
|
102
|
+
**Resene Paints Ltd.** (New Zealand). Resene's naming system has
|
|
103
|
+
distinctly local flavor, including te reo Māori and New Zealand
|
|
104
|
+
place names ("Pohutukawa," "Acapulco," "Afghan Tan").
|
|
105
|
+
|
|
106
|
+
- Chromonym uses **nominative references** to Resene's published
|
|
107
|
+
color names; no trademark claim or endorsement is implied.
|
|
108
|
+
- Canonical keys are the lowercase single-word form the source
|
|
109
|
+
module uses (`treepoppy` for "Tree Poppy"); the display form
|
|
110
|
+
and Resene's own Munsell-style code (e.g. `BR32-011-076`) are
|
|
111
|
+
captured as trailing comments on each palette entry for human
|
|
112
|
+
readers.
|
|
113
|
+
- sRGB hex values are taken verbatim from the upstream module's
|
|
114
|
+
RGB byte triplets. Resene's physical paint samples under
|
|
115
|
+
their own published lighting conditions are the authoritative
|
|
116
|
+
reference; this digital approximation is provided for
|
|
117
|
+
comparative / tooling use, not for paint specification.
|
|
118
|
+
- If Resene Paints Ltd. objects to this palette's inclusion,
|
|
119
|
+
open an issue and we will revisit.
|
|
120
|
+
|
|
121
|
+
## Natural Colour System (NCS)
|
|
122
|
+
|
|
123
|
+
The `ncs` palette (1950 entries) is derived from the Raku module
|
|
124
|
+
**`Color::Names::NCS`** in the
|
|
125
|
+
[`thundergnat/Color-Names`](https://github.com/thundergnat/Color-Names)
|
|
126
|
+
repository, which digitizes the **Natural Colour System®** Atlas.
|
|
127
|
+
NCS is a Swedish perceptual color system based on Hering's
|
|
128
|
+
opponent-process theory; its notation (e.g. `S 2030-R80B`)
|
|
129
|
+
describes each color by its blackness, chromaticness, and hue
|
|
130
|
+
position between the four elementary chromatics.
|
|
131
|
+
|
|
132
|
+
- **NCS® is a registered trademark of NCS Colour AB** (Stockholm).
|
|
133
|
+
Chromonym is **not affiliated with, endorsed by, licensed by,
|
|
134
|
+
or certified by NCS Colour AB**. The shipped palette is a
|
|
135
|
+
community approximation intended for on-screen reference and
|
|
136
|
+
nearest-name identification; it is **not suitable for paint
|
|
137
|
+
specification or color matching** against the official NCS
|
|
138
|
+
Atlas.
|
|
139
|
+
- NCS is **device-independent by design**. Any sRGB approximation
|
|
140
|
+
is lossy by construction. The values shipped here come from
|
|
141
|
+
thundergnat's Raku module's own digitization; different NCS
|
|
142
|
+
publications give slightly different sRGB values for the same
|
|
143
|
+
code.
|
|
144
|
+
- Canonical keys are the NCS codes themselves (e.g. `2030-R80B`).
|
|
145
|
+
The palette's custom normalizer accepts optional `NCS ` and
|
|
146
|
+
`S ` prefixes, so `NCS S 2030-R80B`, `S 2030-R80B`,
|
|
147
|
+
`2030-R80B`, and `2030r80b` all resolve identically. `S` is
|
|
148
|
+
only treated as a prefix when followed by a digit, so inputs
|
|
149
|
+
that don't match an NCS code aren't silently rewritten.
|
|
150
|
+
|
|
151
|
+
### Accuracy envelope
|
|
152
|
+
|
|
153
|
+
NCS specifies colors by percentages on an opponent-process model;
|
|
154
|
+
those percentages must be translated to sRGB through a calibration
|
|
155
|
+
that differs between publications. As a rough guide for the
|
|
156
|
+
digitization chromonym ships:
|
|
157
|
+
|
|
158
|
+
- **Mid-chroma / mid-value codes** (blackness and chromaticness
|
|
159
|
+
both between ~20 and ~60) are the best-behaved; the upstream
|
|
160
|
+
digitization is usually within a few ΔE2000 of any reasonable
|
|
161
|
+
alternate rendering.
|
|
162
|
+
- **High chromaticness, low blackness** codes (e.g. `S 1080-Y90R`,
|
|
163
|
+
`S 0580-G`) sit at or outside the sRGB gamut boundary. The hex
|
|
164
|
+
shipped here is gamut-clipped; a different digitization may
|
|
165
|
+
clip differently, producing visibly different hex values for
|
|
166
|
+
the same NCS code.
|
|
167
|
+
- **Very low-blackness neutrals** (e.g. the `0300-N` / `0500-N`
|
|
168
|
+
range) are close to sRGB white; fine-grained differences between
|
|
169
|
+
digitizations are largely imperceptible.
|
|
170
|
+
|
|
171
|
+
If brand-critical or specification-critical NCS matching is needed,
|
|
172
|
+
consult NCS Colour AB directly: <https://ncscolour.com/>.
|
|
173
|
+
|
|
174
|
+
## NBS (thundergnat digitization)
|
|
175
|
+
|
|
176
|
+
The `nbs` palette (267 entries) is a **second digitization of the
|
|
177
|
+
same 1955 NBS Method of Designating Colors** covered by the
|
|
178
|
+
`isccNbs` palette above; it is sourced from the Raku module
|
|
179
|
+
**`Color::Names::NBS`** in
|
|
180
|
+
[`thundergnat/Color-Names`](https://github.com/thundergnat/Color-Names).
|
|
181
|
+
Ships alongside `isccNbs` (not as a replacement) because the two
|
|
182
|
+
digitizations produce materially different sRGB values for the
|
|
183
|
+
same named blocks: average ΔE2000 of ~4.6 across the 260 shared
|
|
184
|
+
names, max ΔE2000 of 31 for the most saturated blocks like
|
|
185
|
+
"Vivid bluish green."
|
|
186
|
+
|
|
187
|
+
The two palettes serve different reference workflows:
|
|
188
|
+
|
|
189
|
+
- **`isccNbs`** (Paul Centore) computes Munsell-centroid CIELAB
|
|
190
|
+
then maps to sRGB. "Vivid" blocks land near the sRGB saturation
|
|
191
|
+
edge; seven blocks are left as gaps because their centroids
|
|
192
|
+
fall outside the sRGB gamut.
|
|
193
|
+
- **`nbs`** (thundergnat) appears to use the sample chip RGBs
|
|
194
|
+
from the original NBS publication, which were already gamut-
|
|
195
|
+
compressed by the physical chip material. "Vivid" blocks read
|
|
196
|
+
as more muted, and all 267 entries are covered — including
|
|
197
|
+
the seven blocks whose Munsell centroids fall outside the sRGB
|
|
198
|
+
gamut and are therefore omitted from `isccNbs`. `nbs` does not
|
|
199
|
+
"recover" the out-of-gamut centroids; it ships the already-
|
|
200
|
+
compressed physical chip approximation instead, which is a
|
|
201
|
+
different kind of lossy.
|
|
202
|
+
|
|
203
|
+
Pick `nbs` for historical / swatch-book matching; pick `isccNbs`
|
|
204
|
+
for modern digital design work. The name vocabulary (the 1955
|
|
205
|
+
NBS publication itself) is a US-government document not subject
|
|
206
|
+
to copyright restrictions when faithfully reproduced; the
|
|
207
|
+
specific sRGB values in each palette are credited to their
|
|
208
|
+
respective digitizers.
|
|
209
|
+
|
|
210
|
+
## ISCC-NBS Colour System
|
|
211
|
+
|
|
212
|
+
The `isccNbs` palette (260 entries) is the **ISCC-NBS Method of
|
|
213
|
+
Designating Colors**, a 1955 joint publication of the
|
|
214
|
+
**Inter-Society Color Council** and the US **National Bureau of
|
|
215
|
+
Standards** (now NIST). The system partitions the Munsell color
|
|
216
|
+
solid into 267 named blocks at Level 3 resolution with canonical
|
|
217
|
+
names like "Vivid pink," "Strong purplish red," and "Dark
|
|
218
|
+
yellowish brown."
|
|
219
|
+
|
|
220
|
+
sRGB centroids for each named block are scraped from **Paul
|
|
221
|
+
Centore's** digitization at
|
|
222
|
+
<https://www.munsellcolorscienceforpainters.com/ISCCNBS/ISCCNBSSystem.html>,
|
|
223
|
+
part of his *Munsell Colour Science for Painters* site.
|
|
224
|
+
|
|
225
|
+
**Why 260 and not 267:** Seven ISCC-NBS blocks sit outside the
|
|
226
|
+
sRGB gamut (notably the most-saturated greens, blues, and
|
|
227
|
+
orange-yellows): `Deep green`, `Deep bluish green`, `Vivid
|
|
228
|
+
greenish blue`, `Deep greenish blue`, `Brilliant orange`, `Deep
|
|
229
|
+
olive green`, and `Vivid orange yellow`. Centore's source marks
|
|
230
|
+
these as `rgb(NaN,NaN,NaN)`; chromonym skips them rather than
|
|
231
|
+
ship a fabricated sRGB approximation.
|
|
232
|
+
|
|
233
|
+
The ISCC-NBS name system itself is a US-government publication
|
|
234
|
+
and is not subject to copyright restrictions when faithfully
|
|
235
|
+
reproduced. Paul Centore's specific sRGB-centroid computations
|
|
236
|
+
are credited here; if the author objects to Chromonym's use of
|
|
237
|
+
his digitization, open an issue and we will revisit or redo the
|
|
238
|
+
centroids from the primary Munsell data.
|
|
239
|
+
|
|
240
|
+
## Federal Standard 595 (FS595B and FS595C)
|
|
241
|
+
|
|
242
|
+
The `fs595b` (209 entries) and `fs595c` (589 entries) palettes
|
|
243
|
+
are derived from the Raku modules **`Color::Names::FS595B`** and
|
|
244
|
+
**`Color::Names::FS595C`** in the
|
|
245
|
+
[`thundergnat/Color-Names`](https://github.com/thundergnat/Color-Names)
|
|
246
|
+
repository, which digitize the US Federal Government's
|
|
247
|
+
**Federal Standard 595** paint-color specification (used for
|
|
248
|
+
military, aviation, and government-equipment coatings). FS595B
|
|
249
|
+
is the 1989 revision, superseded by FS595C in 2008. Both are
|
|
250
|
+
still in active use depending on which revision a given piece of
|
|
251
|
+
documentation or equipment references. The Federal Standard
|
|
252
|
+
itself is a US-government-published specification and is not
|
|
253
|
+
subject to copyright restrictions when faithfully reproduced.
|
|
254
|
+
|
|
255
|
+
- Canonical keys in both palettes are the 5-digit FS codes
|
|
256
|
+
prefixed with `FS ` (e.g. `FS 11136`). Common names attached
|
|
257
|
+
to many chips in the source (e.g. "Insignia Red") are captured
|
|
258
|
+
as trailing code comments on each palette entry for human
|
|
259
|
+
readers but are **not** used as lookup keys because the spec
|
|
260
|
+
reuses many across multiple chips with different hex values
|
|
261
|
+
(18–20 different "Green"s, 11 "Tan"s, etc., depending on
|
|
262
|
+
revision).
|
|
263
|
+
- Same-coded chips can differ in hex between the B and C
|
|
264
|
+
revisions; for example `FS 11136` ("Insignia Red") is
|
|
265
|
+
`#9b2f25` in FS595B versus `#a32b25` in FS595C. Pick the
|
|
266
|
+
palette that matches your reference documentation.
|
|
267
|
+
- sRGB hex values are taken verbatim from the upstream modules'
|
|
268
|
+
RGB byte triplets; the published standard specifies color
|
|
269
|
+
chips under controlled lighting, and all digital sRGB
|
|
270
|
+
approximations including these are necessarily simplified.
|
|
271
|
+
- Chromonym is **not affiliated with** any US Government agency
|
|
272
|
+
or defense contractor; no endorsement is implied.
|
|
273
|
+
|
|
274
|
+
## XKCD color survey
|
|
275
|
+
|
|
276
|
+
The `xkcd` palette (923 entries after normalization-collision
|
|
277
|
+
dedupe) is derived from **Randall Munroe's 2010 XKCD color
|
|
278
|
+
survey results**, published at <https://xkcd.com/color/rgb.txt>
|
|
279
|
+
and released to the **public domain under CC0 1.0 Universal**.
|
|
280
|
+
Chromonym reproduces the dataset verbatim apart from:
|
|
281
|
+
|
|
282
|
+
- Hex casing normalized to lowercase.
|
|
283
|
+
- Entries whose names collapse to the same canonical key under
|
|
284
|
+
`standardNormalize` are deduped first-seen wins (e.g. the
|
|
285
|
+
survey's `darkblue` and `dark blue` entries both normalize to
|
|
286
|
+
`darkblue`; only the first is retained).
|
|
287
|
+
|
|
288
|
+
The XKCD color-survey names are crowd-sourced and include
|
|
289
|
+
colorful non-standard descriptors. Chromonym takes no editorial
|
|
290
|
+
stance on the names and ships them as-is.
|
|
291
|
+
|
|
292
|
+
## Name That Color (NTC)
|
|
293
|
+
|
|
294
|
+
The `ntc` palette (1566 entries) is derived from Chirag Mehta's
|
|
295
|
+
**"Name That Color"** dataset, first published at
|
|
296
|
+
<https://chir.ag/projects/ntc/>. Chromonym consumes the dataset via
|
|
297
|
+
the MIT-licensed **`colorjs/color-namer`** npm package
|
|
298
|
+
(<https://github.com/colorjs/color-namer>), which redistributes the
|
|
299
|
+
list in a machine-readable form. The shipped palette was generated
|
|
300
|
+
against commit `abb3d184ab63db9327908319cc45b55c91493bb7` of the
|
|
301
|
+
upstream repository, so the entry set is deterministic and
|
|
302
|
+
reproducible.
|
|
303
|
+
|
|
304
|
+
- Names are used as **nominative references** to the publicly
|
|
305
|
+
distributed NTC dataset; no endorsement or trademark claim is
|
|
306
|
+
implied.
|
|
307
|
+
- sRGB hex values are taken verbatim from the upstream list.
|
|
308
|
+
- No modifications are made beyond casing normalization of the hex
|
|
309
|
+
string and sorting by display name.
|
|
310
|
+
|
|
311
|
+
The NTC dataset has been widely redistributed since 2006 under
|
|
312
|
+
terms allowing free reuse with attribution. If the upstream author
|
|
313
|
+
objects to Chromonym's inclusion of the list, open an issue and we
|
|
314
|
+
will revisit.
|
|
315
|
+
|
|
316
|
+
## Bauhaus Modern (demo site typeface)
|
|
317
|
+
|
|
318
|
+
The demo site at <https://simiancraft.github.io/chromonym/> embeds
|
|
319
|
+
**Bauhaus Modern**, a display typeface designed by **Nils Kähler**
|
|
320
|
+
(2016). The font file is hosted under
|
|
321
|
+
[`demo/public/fonts/`](./demo/public/fonts/) and is used only as
|
|
322
|
+
display type for the demo wordmark.
|
|
323
|
+
|
|
324
|
+
- Source: <https://www.dafont.com/bauhaus-modern.font>
|
|
325
|
+
- License terms: "Free for personal use" (per the dafont listing)
|
|
326
|
+
- Use for this demo was arranged with the designer's knowledge via
|
|
327
|
+
email; a donation was made, and the use case (MIT-licensed library
|
|
328
|
+
demo, display-type only, with visible attribution) was described.
|
|
329
|
+
See [`demo/public/fonts/README.txt`](./demo/public/fonts/README.txt)
|
|
330
|
+
for the full note.
|
|
331
|
+
|
|
332
|
+
The font is not part of the published npm package (`files` allowlist
|
|
333
|
+
ships only the library, not the demo site). Downstream users of the
|
|
334
|
+
`chromonym` package do not redistribute Bauhaus Modern by installing
|
|
335
|
+
the library. Please do not repackage or redistribute the font file
|
|
336
|
+
outside this demo without following up with the designer directly.
|
|
337
|
+
|
|
96
338
|
## Upstream dependencies
|
|
97
339
|
|
|
98
340
|
See `package.json` for runtime and development dependencies. All are MIT
|
package/README.md
CHANGED
|
@@ -14,13 +14,18 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/chromonym)
|
|
15
15
|
[](https://github.com/simiancraft/chromonym/actions/workflows/ci.yml)
|
|
16
16
|
[](https://codecov.io/github/simiancraft/chromonym)
|
|
17
|
-
[](#what-you-actually-ship)
|
|
18
|
+
[](https://securityscorecards.dev/viewer/?uri=github.com/simiancraft/chromonym)
|
|
18
19
|
|
|
19
20
|
<p align="center">
|
|
20
21
|
<code>identify</code> • <code>resolve</code> • <code>convert</code>
|
|
21
22
|
</p>
|
|
22
23
|
|
|
23
|
-
**The last color-naming library you'll ever need.** One API — `identify`, `resolve`, `convert` — works against any palette you throw at it. Ships CSS, X11, Pantone,
|
|
24
|
+
**The last color-naming library you'll ever need.** One API — `identify`, `resolve`, `convert` — works against any palette you throw at it. Ships twelve palettes out of the box (CSS, X11, Pantone, Crayola, NTC, XKCD, Resene, NCS, ISCC-NBS, NBS, FS 595 B, FS 595 C); bring your own (brand colors, paint chips, game factions, chart themes) with full TypeScript inference and zero registration. Six distance metrics — from fast Euclidean to industry-standard CIEDE2000 and OKLAB. Tree-shakes to the bone — you only pay for palettes you import.
|
|
25
|
+
|
|
26
|
+
<sub>*chromonym* (n.): a name for a color; *chroma* + *-onym*. Also the library.</sub>
|
|
27
|
+
|
|
28
|
+
<sub>Every public export ships with JSDoc and a runnable `@example`; autocomplete and hover do the teaching.</sub>
|
|
24
29
|
|
|
25
30
|
<p align="center">
|
|
26
31
|
<a href="https://simiancraft.github.io/chromonym/">
|
|
@@ -80,18 +85,6 @@ yarn add chromonym
|
|
|
80
85
|
npm install chromonym
|
|
81
86
|
```
|
|
82
87
|
|
|
83
|
-
## Migrating from 1.x
|
|
84
|
-
|
|
85
|
-
v2 collapsed the five-verb draft API (`identify`, `identifyAll`, `translate`, `resolve`, `convert`) into **three verbs** by folding the duplicates into options on existing verbs. Mechanical rewrites:
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
// v1 → v2
|
|
89
|
-
identifyAll(color, { palette, k: 3 }) // → identify(color, { palette, k: 3 })
|
|
90
|
-
translate(name, { from: web, to: pantone }) // → identify(name, { palette: pantone, source: web })
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
`resolve` and `convert` are unchanged. If you were only using those two, no edits are needed. See [`CHANGELOG.md`](CHANGELOG.md) for the full list of v3.0 breaking changes.
|
|
94
|
-
|
|
95
88
|
## API
|
|
96
89
|
|
|
97
90
|
Three verbs. All accept an optional `opts` object; defaults fire when omitted.
|
|
@@ -273,22 +266,24 @@ type Palette<Name extends string = string> = {
|
|
|
273
266
|
|
|
274
267
|
The primary shape. Any object matching `Palette<Name>` works — no registry, no plugin, no side effects. Bundlers only include the palettes you actually import.
|
|
275
268
|
|
|
269
|
+
The recommended pattern is `defineColorPalette`: it accepts mixed color-value formats (paste whatever came out of your design tool), normalizes everything to hex once at module load, and preserves the literal-key union for full narrowing through `identify` / `resolve` / `convert`.
|
|
270
|
+
|
|
276
271
|
```ts
|
|
277
|
-
import { identify, resolve
|
|
272
|
+
import { defineColorPalette, identify, resolve } from 'chromonym';
|
|
278
273
|
|
|
279
|
-
const warhammer = {
|
|
274
|
+
const warhammer = defineColorPalette({
|
|
280
275
|
name: 'warhammer40k',
|
|
281
276
|
colors: {
|
|
282
|
-
'world eaters red': '#8b1a1a',
|
|
283
|
-
'adeptus red': '
|
|
284
|
-
'sons of malice white':
|
|
285
|
-
'the flawless host purple':
|
|
286
|
-
'nurgle green': '
|
|
277
|
+
'world eaters red': '#8b1a1a', // hex
|
|
278
|
+
'adeptus red': 'rgb(101, 32, 34)', // rgb string
|
|
279
|
+
'sons of malice white': { r: 232, g: 228, b: 216 }, // rgb object
|
|
280
|
+
'the flawless host purple': [107, 45, 125], // rgb tuple
|
|
281
|
+
'nurgle green': 'hsl(78, 38%, 39%)', // hsl string
|
|
287
282
|
'alpha legion teal': '#2a6d7a',
|
|
288
283
|
},
|
|
289
|
-
normalize: (s
|
|
284
|
+
normalize: (s) => s.toLowerCase().replace(/[^a-z0-9]/g, ''),
|
|
290
285
|
defaultMetric: 'deltaE2000',
|
|
291
|
-
}
|
|
286
|
+
});
|
|
292
287
|
|
|
293
288
|
identify('#750c0c', { palette: warhammer })
|
|
294
289
|
// → 'world eaters red' (return type narrows to the palette's key union)
|
|
@@ -297,14 +292,26 @@ resolve('Nurgle Green', { palette: warhammer })
|
|
|
297
292
|
// → '#748c3f' (your normalizer handles case + punctuation)
|
|
298
293
|
```
|
|
299
294
|
|
|
300
|
-
|
|
295
|
+
Unparseable values are dropped with a `console.warn` naming the palette, the offending key, and the bad value; the rest of the palette loads normally. This keeps a single typo out of your brand kit from taking down the whole module.
|
|
296
|
+
|
|
297
|
+
If you prefer zero runtime and already have hex-only values, the `as const satisfies Palette<'A' | 'B' | ...>` pattern still works — list the key union once in the generic and it narrows identically.
|
|
298
|
+
|
|
299
|
+
### The twelve we ship
|
|
301
300
|
|
|
302
301
|
| Name | Entries | Source |
|
|
303
302
|
|---|---|---|
|
|
304
303
|
| `web` (default) | 148 | CSS Color Module Level 4 named colors |
|
|
305
304
|
| `x11` | 658 | X.Org `rgb.txt` (public domain) |
|
|
306
305
|
| `pantone` | 907 | Pantone Coated (C) — community approximations (not Pantone-licensed) |
|
|
307
|
-
| `crayola` |
|
|
306
|
+
| `crayola` | 84 | Crayola crayon colors — community approximations (not Crayola-licensed) |
|
|
307
|
+
| `ntc` | 1566 | Chirag Mehta's "Name That Color" dataset (broad coverage of design / UI / hardware names) |
|
|
308
|
+
| `xkcd` | 923 | XKCD 2010 color-survey results (CC0; crowd-sourced common-speech names) |
|
|
309
|
+
| `fs595b` | 209 | Federal Standard 595B paint chips (1989 revision; modelers / historical equipment) |
|
|
310
|
+
| `fs595c` | 589 | Federal Standard 595C paint chips (US Government spec; military / aviation coatings) |
|
|
311
|
+
| `isccNbs` | 260 | ISCC-NBS Method of Designating Colors (1955 US standard; Munsell-partitioned named blocks) |
|
|
312
|
+
| `nbs` | 267 | Second digitization of the same 1955 standard; different sRGB (matches 1955 chip book; covers the 7 blocks `isccNbs` leaves as gaps) |
|
|
313
|
+
| `resene` | 1378 | Resene Paints (NZ) catalog; te reo Māori and NZ place-names in the naming |
|
|
314
|
+
| `ncs` | 1950 | Natural Colour System (Sweden) — perceptual, structured notation (`2030-R80B`), not NCS-licensed |
|
|
308
315
|
|
|
309
316
|
Importable directly, or via subpath exports for stricter tree-shaking:
|
|
310
317
|
|
|
@@ -321,7 +328,7 @@ crayola.colors['Granny Smith Apple'] // '#a8e4a0'
|
|
|
321
328
|
|
|
322
329
|
Each palette's keys follow its own domain convention — so `identify`'s output pastes back into whatever ecosystem the name came from. `web` uses the CSS spec's single-word form (`rebeccapurple`), `x11` uses the X.Org docs' spaced form (`'antique white 1'`), `pantone` uses the official print notation (`'185 C'`), `crayola` uses the Title Case form printed on the crayon wrapper (`'Granny Smith Apple'`).
|
|
323
330
|
|
|
324
|
-
*Trivia:* `web.colors.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
|
|
331
|
+
*Trivia:* `web.colors.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 100, so `x11.colors['gray 73']` is a real key (`#bababa`). Yes, these are Greys. No, not the kind that visit during sleep paralysis.
|
|
325
332
|
|
|
326
333
|
### Cross-palette translation
|
|
327
334
|
|
|
@@ -382,7 +389,7 @@ Rule of thumb: use `source` when you want one clean call and `null` on miss; use
|
|
|
382
389
|
|
|
383
390
|
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.
|
|
384
391
|
|
|
385
|
-
> **Note on x11's default (ΔE76):** x11 has 658 entries, many of them saturated blue/purple ramps where ΔE76 is a known weak spot. The default optimizes for UI scrubbing speed (~5 µs vs ~90 µs for ΔE2000). If perceptual accuracy matters more than latency, override per call with `{ metric: 'deltaEok' }`
|
|
392
|
+
> **Note on x11's default (ΔE76):** x11 has 658 entries, many of them saturated blue/purple ramps where ΔE76 is a known weak spot. The default optimizes for UI scrubbing speed (~5 µs vs ~90 µs for ΔE2000). If perceptual accuracy matters more than latency, override per call with `{ metric: 'deltaEok' }` for better separation in the blue and purple region.
|
|
386
393
|
|
|
387
394
|
```ts
|
|
388
395
|
// Defaults: read from the palette's own `defaultMetric`.
|
|
@@ -443,6 +450,22 @@ const tryConvert = (input: ColorInput, opts = {}) => {
|
|
|
443
450
|
- BYO palettes have zero library cost beyond your own data.
|
|
444
451
|
- Subpath exports (`chromonym/web`, `chromonym/x11`, `chromonym/pantone`, `chromonym/conversions/hex`, `chromonym/math/deltaE`, etc.) let you import a single palette or converter without going through the root barrel.
|
|
445
452
|
|
|
453
|
+
### What you actually ship
|
|
454
|
+
|
|
455
|
+
**Install size and bundle size are not the same number.** Install size is what lands in your `node_modules/` after `npm install`; bundle size is what ships to your users after tree-shaking, minification, and gzip. For chromonym the gap is roughly two orders of magnitude.
|
|
456
|
+
|
|
457
|
+
The published tarball is **~2.5 MB unpacked / ~557 kB gzipped** because it carries twelve palettes and ~9000 named-color entries; that's the install-size figure `packagephobia` reports. Your **production bundle** pays only for what you actually import, measured with `esbuild --minify`:
|
|
458
|
+
|
|
459
|
+
| Import | min | gzip |
|
|
460
|
+
|---|---|---|
|
|
461
|
+
| `import { defineColorPalette } from 'chromonym'` (pure BYO; no built-in) | 3.8 kB | **1.5 kB** |
|
|
462
|
+
| `import { identify, web } from 'chromonym'` | 10.9 kB | **4.7 kB** |
|
|
463
|
+
| `import { pantoneToRgba } from 'chromonym'` (single conversion) | 17.5 kB | **6.8 kB** |
|
|
464
|
+
| `import { identify, pantone } from 'chromonym'` | 27 kB | **11.3 kB** |
|
|
465
|
+
| `import { identify, ntc } from 'chromonym'` | 43 kB | **20 kB** |
|
|
466
|
+
|
|
467
|
+
If you're evaluating chromonym against a size budget, the gzip column above is what actually lands in your production build.
|
|
468
|
+
|
|
446
469
|
## Types
|
|
447
470
|
|
|
448
471
|
Re-exported from the root barrel — `import type { ... } from 'chromonym'`:
|
|
@@ -466,17 +489,6 @@ bun test
|
|
|
466
489
|
bun run build
|
|
467
490
|
```
|
|
468
491
|
|
|
469
|
-
Regenerating palette data:
|
|
470
|
-
|
|
471
|
-
```sh
|
|
472
|
-
bun run scripts/generate-x11.ts # reads /usr/share/X11/rgb.txt
|
|
473
|
-
bun run scripts/generate-pantone.ts # reads the color_library npm package (MIT © Radu Dragan)
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## Coverage
|
|
477
|
-
|
|
478
|
-
[](https://codecov.io/github/simiancraft/chromonym)
|
|
479
|
-
|
|
480
492
|
## See also
|
|
481
493
|
|
|
482
494
|
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:
|
|
@@ -491,8 +503,4 @@ MIT © [the-simian](https://github.com/the-simian)
|
|
|
491
503
|
|
|
492
504
|
<p align="center"><sub>Crafted with care by <a href="https://simiancraft.com">Simiancraft</a>.</sub></p>
|
|
493
505
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
> ### Pantone® trademark notice
|
|
497
|
-
>
|
|
498
|
-
> **Pantone®** is a registered trademark of **Pantone LLC**. Chromonym is **not affiliated with, endorsed by, or certified by Pantone LLC**. The `pantone` palette ships **community-derived sRGB approximations** of the Pantone Coated (C) set. Values will **not** match a licensed Pantone reference exactly and are unsuitable for print color specification. See [`NOTICE.md`](./NOTICE.md) for full text.
|
|
506
|
+
<p align="center"><sub>Pantone® and Crayola® are trademarks of their respective owners; Chromonym is not affiliated. Palette values are community approximations. Demo typeface <a href="https://www.dafont.com/bauhaus-modern.font">Bauhaus Modern</a> © Nils Kähler. Full attributions: <a href="./NOTICE.md">NOTICE.md</a>.</sub></p>
|
|
@@ -3,7 +3,13 @@ import type { Rgba, RgbaInput, RgbInput, RgbString } from '../types.js';
|
|
|
3
3
|
* Normalize any RGB/RGBA input shape (string, tuple, or object) into
|
|
4
4
|
* the canonical Rgba representation. Alpha defaults to 1 when absent.
|
|
5
5
|
* Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].
|
|
6
|
-
* Throws on malformed strings.
|
|
6
|
+
* Throws on malformed strings. String parsing is linear-time; no polynomial
|
|
7
|
+
* backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is
|
|
8
|
+
* accepted per the CSS Color spec.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* rgbToRgba('rgba(255, 0, 0, .5)');
|
|
12
|
+
* // => { r: 255, g: 0, b: 0, a: 0.5 }
|
|
7
13
|
*/
|
|
8
14
|
export declare function rgbToRgba(input: RgbInput | RgbaInput): Rgba;
|
|
9
15
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAgBnF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA+B3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAK/C","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\n// Alpha group split into two unambiguous alternatives to avoid polynomial\n// backtracking (CodeQL js/polynomial-redos) on malformed inputs like\n// `rgb(9,9,9,99...9`.\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d+(?:\\.\\d+)?|\\.\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings. String parsing is linear-time; no polynomial\n * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is\n * accepted per the CSS Color spec.\n *\n * @example\n * rgbToRgba('rgba(255, 0, 0, .5)');\n * // => { r: 255, g: 0, b: 0, a: 0.5 }\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
|
package/dist/conversions/rgb.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { clamp, requireFinite } from '../math/clamp.js';
|
|
2
|
-
|
|
2
|
+
// Alpha group split into two unambiguous alternatives to avoid polynomial
|
|
3
|
+
// backtracking (CodeQL js/polynomial-redos) on malformed inputs like
|
|
4
|
+
// `rgb(9,9,9,99...9`.
|
|
5
|
+
const RGB_RE = /^rgba?\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:,\s*(\d+(?:\.\d+)?|\.\d+)\s*)?\)$/i;
|
|
3
6
|
function sanitizeChannel(n, label) {
|
|
4
7
|
return clamp(requireFinite(n, label), 0, 255);
|
|
5
8
|
}
|
|
@@ -10,7 +13,13 @@ function sanitizeAlpha(n) {
|
|
|
10
13
|
* Normalize any RGB/RGBA input shape (string, tuple, or object) into
|
|
11
14
|
* the canonical Rgba representation. Alpha defaults to 1 when absent.
|
|
12
15
|
* Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].
|
|
13
|
-
* Throws on malformed strings.
|
|
16
|
+
* Throws on malformed strings. String parsing is linear-time; no polynomial
|
|
17
|
+
* backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is
|
|
18
|
+
* accepted per the CSS Color spec.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* rgbToRgba('rgba(255, 0, 0, .5)');
|
|
22
|
+
* // => { r: 255, g: 0, b: 0, a: 0.5 }
|
|
14
23
|
*/
|
|
15
24
|
export function rgbToRgba(input) {
|
|
16
25
|
if (Array.isArray(input)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,MAAM,GACV
|
|
1
|
+
{"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"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","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\n// Alpha group split into two unambiguous alternatives to avoid polynomial\n// backtracking (CodeQL js/polynomial-redos) on malformed inputs like\n// `rgb(9,9,9,99...9`.\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d+(?:\\.\\d+)?|\\.\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings. String parsing is linear-time; no polynomial\n * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is\n * accepted per the CSS Color spec.\n *\n * @example\n * rgbToRgba('rgba(255, 0, 0, .5)');\n * // => { r: 255, g: 0, b: 0, a: 0.5 }\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
|
package/dist/convert.d.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
import { type DetectedFormat } from './detectFormat.js';
|
|
2
|
-
import type { ColorFormat, ColorInput, ColorValue, Palette, Rgba } from './types.js';
|
|
2
|
+
import type { ColorFormat, ColorInput, ColorValue, Palette, PaletteKey, Rgba } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Normalize any recognized color input to canonical `Rgba`.
|
|
5
5
|
* Pass `knownFormat` when the caller has already run `detectFormat` to skip
|
|
6
6
|
* the redundant detection. Throws on `UNKNOWN` input.
|
|
7
7
|
*
|
|
8
|
-
* Palette names (Pantone codes, BYO keys) are not accepted here
|
|
8
|
+
* Palette names (Pantone codes, BYO keys) are not accepted here; they
|
|
9
9
|
* require palette data to parse. Use `convert(name, { palette })` or
|
|
10
10
|
* `resolve(name, { palette })` at the higher level.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* toRgba('#ff0000'); // { r: 255, g: 0, b: 0, a: 1 }
|
|
14
|
+
* toRgba('rgb(255, 0, 0)'); // { r: 255, g: 0, b: 0, a: 1 }
|
|
15
|
+
* toRgba([255, 0, 0]); // { r: 255, g: 0, b: 0, a: 1 }
|
|
16
|
+
* toRgba({ h: 0, s: 100, l: 50 }); // { r: 255, g: 0, b: 0, a: 1 }
|
|
11
17
|
*/
|
|
12
18
|
export declare function toRgba(input: ColorInput, knownFormat?: DetectedFormat): Rgba;
|
|
13
19
|
/**
|
|
14
20
|
* Emit a canonical `Rgba` in the target structural format. Palette-independent.
|
|
15
21
|
*
|
|
16
22
|
* For palette-key output (e.g. nearest Pantone code, brand name), use
|
|
17
|
-
* `convert(rgba, { format: 'NAME', palette })
|
|
23
|
+
* `convert(rgba, { format: 'NAME', palette })`; or `identify` for fuzzy,
|
|
18
24
|
* `rgbaToPantone` for the low-level Pantone-only path.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* fromRgba({ r: 255, g: 0, b: 0, a: 1 }); // '#ff0000'
|
|
28
|
+
* fromRgba({ r: 255, g: 0, b: 0, a: 1 }, 'HSL'); // 'hsl(0, 100%, 50%)'
|
|
29
|
+
* fromRgba({ r: 255, g: 0, b: 0, a: 0.5 }, 'RGB'); // 'rgb(255, 0, 0)' (alpha dropped)
|
|
19
30
|
*/
|
|
20
31
|
export declare function fromRgba(rgba: Rgba, format?: ColorFormat): ColorValue;
|
|
21
|
-
/**
|
|
22
|
-
* Extract string keys from a Palette's `colors` map.
|
|
23
|
-
* Matches the `PaletteKey<P>` helper in `identify.ts` — kept local so
|
|
24
|
-
* `convert` doesn't depend on `identify`.
|
|
25
|
-
*/
|
|
26
|
-
type PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;
|
|
27
32
|
/**
|
|
28
33
|
* Detect the input color format, normalize to Rgba, and emit the result
|
|
29
34
|
* in the requested output format.
|
|
@@ -46,16 +51,63 @@ type PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;
|
|
|
46
51
|
* Throws on unrecognized input (parser-flavored — `identify`/`resolve`
|
|
47
52
|
* are the `null`-returning alternatives).
|
|
48
53
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Options for the structural (palette-free) variant of {@link convert}.
|
|
56
|
+
* Use when you only need to translate between HEX / RGB / RGBA / HSL / HSV.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* convert('#ff0000', { format: 'HSL' }); // 'hsl(0, 100%, 50%)'
|
|
60
|
+
*/
|
|
61
|
+
export type ConvertStructuralOptions = {
|
|
62
|
+
/**
|
|
63
|
+
* Target output format. Defaults to `'HEX'`.
|
|
64
|
+
*/
|
|
65
|
+
readonly format?: ColorFormat;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Options for {@link convert} when reverse-looking-up a palette name
|
|
69
|
+
* from an exact color value. Generic so the return narrows to the
|
|
70
|
+
* palette's literal-key union.
|
|
71
|
+
*
|
|
72
|
+
* Throws at runtime if the input isn't a pixel-exact match in the
|
|
73
|
+
* palette; use `identify` for nearest-match semantics.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* convert('#e4002b', { palette: pantone, format: 'NAME' }); // '185 C'
|
|
77
|
+
* convert('#663399', { palette: web, format: 'NAME' }); // 'rebeccapurple'
|
|
78
|
+
*/
|
|
79
|
+
export type ConvertNameOptions<P extends Palette> = {
|
|
80
|
+
/**
|
|
81
|
+
* Palette to reverse-look-up against. Required when `format` is `'NAME'`.
|
|
82
|
+
*/
|
|
83
|
+
readonly palette: P;
|
|
84
|
+
/**
|
|
85
|
+
* Output discriminator. `'NAME'` returns the palette's canonical key
|
|
86
|
+
* for the input color.
|
|
87
|
+
*/
|
|
88
|
+
readonly format: 'NAME';
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Options for {@link convert} when a palette is provided but the output
|
|
92
|
+
* is a structural format (HEX / RGB / HSL / HSV). Unlocks palette-name
|
|
93
|
+
* *input*: the `input` may be `'185 C'`, `'Acme Red'`, etc., which is
|
|
94
|
+
* resolved through `palette.normalize` before conversion.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* convert('185 C', { palette: pantone }); // '#e4002b'
|
|
98
|
+
* convert('185 C', { palette: pantone, format: 'HSL' }); // 'hsl(349, 100%, 45%)'
|
|
99
|
+
*/
|
|
100
|
+
export type ConvertPaletteOptions<P extends Palette> = {
|
|
101
|
+
/**
|
|
102
|
+
* Palette used to parse name-shaped inputs.
|
|
103
|
+
*/
|
|
104
|
+
readonly palette: P;
|
|
105
|
+
/**
|
|
106
|
+
* Target output format. Defaults to `'HEX'`.
|
|
107
|
+
*/
|
|
108
|
+
readonly format?: ColorFormat;
|
|
109
|
+
};
|
|
110
|
+
export declare function convert(input: ColorInput, opts?: ConvertStructuralOptions): ColorValue;
|
|
111
|
+
export declare function convert<P extends Palette>(input: ColorInput | string, opts: ConvertNameOptions<P>): PaletteKey<P>;
|
|
112
|
+
export declare function convert<P extends Palette>(input: ColorInput | string, opts: ConvertPaletteOptions<P>): ColorValue;
|
|
61
113
|
//# sourceMappingURL=convert.d.ts.map
|