color-value-tools 1.1.9 → 1.1.10

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 +35 -694
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -6,31 +6,6 @@ A comprehensive utility library for parsing, converting, manipulating, and analy
6
6
 
7
7
  ---
8
8
 
9
- ## Contents
10
-
11
- - [Features](#features)
12
- - [Installation](#installation)
13
- - [Quick start](#quick-start)
14
- - [Detection](#detection)
15
- - [Parsing & Normalization](#parsing--normalization)
16
- - [Conversions](#conversions)
17
- - [Manipulation](#manipulation)
18
- - [Interpolation & Scales](#interpolation--scales)
19
- - [Color Harmonies](#color-harmonies)
20
- - [Palette Generation](#palette-generation)
21
- - [Accessibility (WCAG)](#accessibility-wcag)
22
- - [Color Blindness Simulation](#color-blindness-simulation)
23
- - [Formatting](#formatting)
24
- - [Cache Management](#cache-management)
25
- - [Generator Functions](#generator-functions)
26
- - [CLI](#cli)
27
- - [Cookbook](#cookbook)
28
- - [TypeScript types](#typescript-types)
29
- - [Architecture](#architecture)
30
- - [Bundle size & dependencies](#bundle-size--dependencies)
31
-
32
- ---
33
-
34
9
  ## Features
35
10
 
36
11
  - **Universal parser** — `normalizeColor()` accepts hex (3/4/6/8-digit), `rgb()` / `rgba()`, `hsl()` / `hsla()`, `hwb()`, `oklch()` / `oklcha()`, `color(display-p3 ...)`, CSS named colors (all 148), and plain `{r,g,b}` / `{h,s,l}` objects
@@ -38,13 +13,13 @@ A comprehensive utility library for parsing, converting, manipulating, and analy
38
13
  - **Manipulation** — `lighten`, `darken`, `saturate`, `desaturate`, `invertColor`, `grayscale`, `setAlpha`, `rotateHue`, `adjustHexBrightness`
39
14
  - **Mixing & Interpolation** — `mixColors` and `interpolateColors` in 6 color spaces with 4 hue interpolation modes; `createColorScale` across multiple anchor colors with custom positions; `midpointColor` for a perceptual midpoint
40
15
  - **Color Harmonies** — `complement`, `triadic`, `analogous`, `splitComplementary`, `tetradic`
41
- - **Palette Generation** — `colorShades`, `monochromatic`, `tints`, `shades`, `tones` — all Oklab-accurate where applicable
16
+ - **Palette Generation** — `colorShades`, `monochromatic`, `tints`, `shades`, `tones`, `randomColor` — all Oklab-accurate where applicable
42
17
  - **WCAG Accessibility** — `relativeLuminance`, `contrastRatio`, `wcagLevel`, `bestTextColor`, `bestContrastColor`, `bestContrastPalette`, `isReadableOnBackground` (solid, semi-transparent, and gradient backgrounds)
43
18
  - **Color Blindness Simulation** — protanopia, deuteranopia, tritanopia using Vienot 1999 matrices on linear RGB
44
19
  - **Perceptual color distance** — `colorDeltaE` implementing the full CIEDE2000 formula
45
20
  - **CSS Formatting** — `toHslString`, `toHwbString`, `toOklchString`, `toColorP3String`
46
- - **Cache** — LRU-style `normalizeColorCached`; `getCacheStats`, `clearColorCache`, `enableCache` / `disableCache`
47
- - **Generator functions** — `generateGradientColors*`, `generateTints*`, `generateShades*` for lazy iteration without pre-allocating arrays
21
+ - **Memoization cache** — `normalizeColorCached` caches parsed results in memory; `getCacheStats`, `clearColorCache`, `enableCache` / `disableCache`
22
+ - **Lazy generator functions** — `generateGradientColors`, `generateTints`, `generateShades` for iterating without pre-allocating arrays
48
23
  - **CLI (`cvt`)** — inspect any color from the terminal: full info, all conversions, WCAG contrast, shades, harmonies, nearest named color
49
24
  - **Zero dependencies** — no runtime deps; ships as tree-shakeable ESM + CommonJS
50
25
 
@@ -52,46 +27,50 @@ A comprehensive utility library for parsing, converting, manipulating, and analy
52
27
 
53
28
  ## Installation
54
29
 
30
+ Requires Node.js `18+`. No runtime or peer dependencies — the package is fully framework-agnostic and works in any JS/TS environment (browser, Node, edge runtimes).
31
+
55
32
  ```bash
56
33
  npm install color-value-tools
57
34
  ```
58
35
 
59
- No peer dependencies required.
60
-
61
- ---
62
-
63
- ## Quick start
36
+ ### Quick start
64
37
 
65
38
  ```ts
66
39
  import {
67
- normalizeColor, mixColors,
68
- lighten, darken, saturate,
69
- complement, triadic,
70
- wcagLevel, bestTextColor,
71
- colorDeltaE, randomColor,
40
+ normalizeColor,
41
+ mixColors,
42
+ lighten,
43
+ darken,
44
+ saturate,
45
+ complement,
46
+ triadic,
47
+ wcagLevel,
48
+ bestTextColor,
49
+ colorDeltaE,
50
+ randomColor,
72
51
  } from 'color-value-tools'
73
52
 
74
53
  // Parse any color format
75
54
  normalizeColor('#3498db')
76
- // { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, h: 204, s: 70, l: 53, v: 86, a: 1 }
55
+ // { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, a: 1, h: 204, s: 70, l: 53, v: 86 }
77
56
 
78
57
  // Manipulate
79
- lighten('#3498db', 15) // '#6ab4e8'
80
- darken('#3498db', 15) // '#1a6da3'
81
- saturate('#3498db', 20) // '#1a8fe8'
58
+ lighten('#3498db', 15) // '#74b9e7'
59
+ darken('#3498db', 15) // '#1d6ea5'
60
+ saturate('#3498db', 20) // '#1b9df3'
82
61
 
83
62
  // Harmonies
84
- complement('#3498db') // '#db6034'
85
- triadic('#3498db') // ['#3498db', '#db3498', '#98db34']
63
+ complement('#3498db') // '#db7633'
64
+ triadic('#3498db') // ['#3498db', '#db3398', '#98db33']
86
65
 
87
66
  // WCAG accessibility
88
- wcagLevel('#ffffff', '#3498db') // 'AA'
89
- bestTextColor('#3498db') // '#ffffff'
67
+ wcagLevel('#ffffff', '#3498db') // 'AA-large'
68
+ bestTextColor('#3498db') // '#000000'
90
69
 
91
70
  // Perceptual color distance (CIEDE2000)
92
- colorDeltaE('#ff0000', '#fe0000') // ~0.9
71
+ colorDeltaE('#ff0000', '#fe0000') // 0.2079
93
72
 
94
- // Random color
73
+ // Random color within a hue/saturation range
95
74
  randomColor({ hRange: [200, 260], sRange: [60, 80] })
96
75
  ```
97
76
 
@@ -103,642 +82,14 @@ const { normalizeColor, mixColors } = require('color-value-tools')
103
82
 
104
83
  ---
105
84
 
106
- ## Detection
85
+ ## Documentation & links
107
86
 
108
- | Function | Description |
109
- |---|---|
110
- | `getColorType(value)` | Returns `'hex'` \| `'css-var'` \| `'rgb'` \| `'hsl'` \| `'named'` \| `'oklch'` \| `'color'` \| `'unknown'` |
111
- | `isHexColor(value)` | Detects 3-, 4-, or 6-digit hex strings (with or without `#`) |
112
- | `isRgbColor(value)` | Detects `rgb()` / `rgba()` strings |
113
- | `isHslColor(value)` | Detects `hsl()` / `hsla()` strings |
114
- | `isOklchColor(value)` | Detects `oklch()` / `oklcha()` strings |
115
- | `isColorFunction(value)` | Detects `color(display-p3 ...)`, `color(srgb ...)`, `color(srgb-linear ...)` strings |
116
- | `isCssVariable(value)` | Checks for `var(--name)` pattern |
117
- | `extractCssVariableName(value)` | Extracts `--name` from `var(--name, fallback)` |
118
-
119
- ---
120
-
121
- ## Parsing & Normalization
122
-
123
- | Function | Description |
124
- |---|---|
125
- | `normalizeColor(input)` | Universal parser. Accepts hex (3/4/6/8-digit), `rgb()`, `hsl()`, `hwb()`, `oklch()`, `color()`, named color, `{r,g,b}` object, `{h,s,l}` object. Returns `{ type, hex, r, g, b, a, h, s, l, v }` |
126
- | `normalizeColorCached(input)` | Same as `normalizeColor` but uses an internal LRU-style cache. String input only |
127
- | `normalizeHex(hex)` | Normalizes 3- or 6-digit hex to lowercase 6-digit with `#` |
128
- | `rgbaStringToRgba(str)` | Parses `rgb()` / `rgba()` string to `{r, g, b, a}`; supports percentage channels |
129
- | `hex8ToRgba(hex)` | Parses 8-digit hex (`#rrggbbaa`) or 4-digit hex (`#rgba`) to `{r, g, b, a}` |
130
- | `shortHexToRgba(hex)` | Parses 4-digit hex (`#rgba`) to `{r, g, b, a}` — e.g. `#f0f0` → `{r:255,g:0,b:255,a:0}` |
131
- | `parseHwbString(str)` | Parses `hwb()` string to `{H, W, B, alpha}` |
132
- | `parseOklchString(str)` | Parses `oklch(L C H / alpha)` to `{L, C, H, alpha}`; accepts percentage L |
133
- | `parseColorFn(str)` | Parses `color(display-p3 r g b / alpha)` to `{space, r, g, b, alpha}` |
134
- | `parseCssVar(value)` | Parses `var(--name, fallback)` to `{variableName, fallback?}` |
135
-
136
- ### `normalizeColor` return value
137
-
138
- ```ts
139
- {
140
- type: 'hex' | 'rgb' | 'hsl' | 'oklch' | 'color' | 'named' | 'css-var' | 'unknown'
141
- hex?: string // '#rrggbb'
142
- r?: number // 0–255
143
- g?: number // 0–255
144
- b?: number // 0–255
145
- a?: number // 0–1
146
- h?: number // hue 0–360
147
- s?: number // HSL saturation 0–100
148
- l?: number // HSL lightness 0–100
149
- v?: number // HSV value 0–100
150
- raw?: string // set for css-var type
151
- }
152
- ```
153
-
154
- ```ts
155
- // Object input
156
- normalizeColor({ r: 52, g: 152, b: 219 }) // → full result with hex, h, s, l, v
157
- normalizeColor({ h: 204, s: 70, l: 53 }) // → full result with hex, r, g, b, v
158
-
159
- // CSS variable — returns raw, no color channels
160
- normalizeColor('var(--brand-color)') // → { type: 'css-var', raw: 'var(--brand-color)' }
161
- ```
162
-
163
- ---
164
-
165
- ## Conversions
166
-
167
- | Function | Description |
168
- |---|---|
169
- | `hexToRgb(hex)` | `→ [r, g, b]` |
170
- | `hexToRgba(hex, opacity)` | `→ rgba(...)` string |
171
- | `hexToHsl(hex)` | `→ [h, s, l]` |
172
- | `hexToHsv(hex)` | `→ [h, s, v]` |
173
- | `hslToHex(h, s, l)` | `→ #rrggbb` |
174
- | `hslToRgb(h, s, l)` | `→ {r, g, b}` |
175
- | `hsvToHex(h, s, v)` | `→ #rrggbb` |
176
- | `hsvToRgb(h, s, v)` | `→ {r, g, b}` |
177
- | `rgbToHex({r,g,b})` | `→ #rrggbb` |
178
- | `rgbaToHex({r,g,b,a})` | `→ #rrggbbaa` |
179
- | `rgbaToHex8({r,g,b,a})` | Alias for `rgbaToHex` |
180
- | `rgbToRgbaString({r,g,b}, a)` | `→ rgba(...)` string |
181
- | `rgbToHsl({r,g,b})` | `→ [h, s, l]` |
182
- | `rgbToHsv({r,g,b})` | `→ [h, s, v]` |
183
- | `rgbToHwb({r,g,b})` | `→ [H, W, B]` |
184
- | `hwbToRgb(H, W, B)` | `→ {r, g, b}` |
185
- | `rgbToCmyk({r,g,b})` | `→ {c, m, y, k}` (0–1 range) |
186
- | `cmykToRgb({c,m,y,k})` | `→ {r, g, b}` |
187
- | `rgbToLab({r,g,b})` | `→ {L, a, b}` (CIE Lab D65) |
188
- | `labToRgb({L,a,b})` | `→ {r, g, b}` |
189
- | `rgbToLch({r,g,b})` | `→ {L, C, H}` (CIE LCH) |
190
- | `lchToRgb({L,C,H})` | `→ {r, g, b}` |
191
- | `rgbToOklab({r,g,b})` | `→ {L, a, b}` (Oklab) |
192
- | `oklabToRgb({L,a,b})` | `→ {r, g, b}` |
193
- | `rgbToOklch({r,g,b})` | `→ {L, C, H}` (Oklch) |
194
- | `oklchToRgb({L,C,H})` | `→ {r, g, b}` |
195
- | `rgbToDisplayP3({r,g,b})` | `→ {r, g, b}` in Display P3 space (0–1 per channel) |
196
- | `displayP3ToRgb({r,g,b})` | `→ {r, g, b}` sRGB (0–255) |
197
- | `toDisplayP3Hex(color)` | Converts any color → Display P3 → hex |
198
-
199
- ---
200
-
201
- ## Manipulation
202
-
203
- | Function | Description |
204
- |---|---|
205
- | `lighten(color, amount)` | Increase HSL lightness by `amount` (0–100) |
206
- | `darken(color, amount)` | Decrease HSL lightness by `amount` (0–100) |
207
- | `saturate(color, amount)` | Increase HSL saturation by `amount` (0–100) |
208
- | `desaturate(color, amount)` | Decrease HSL saturation by `amount` (0–100) |
209
- | `setAlpha(color, alpha)` | Returns `rgba(...)` with the given alpha (0–1) |
210
- | `getAlpha(color)` | Returns the alpha channel value (0–1) |
211
- | `invertColor(color)` | Inverts all three RGB channels |
212
- | `grayscale(color)` | Converts to grayscale using ITU-R BT.709 perceptual weights |
213
- | `rotateHue(hex, degrees)` | Rotates hue by degrees; supports negative values |
214
- | `adjustHexBrightness(hex, offsetPercent)` | Lightens (positive) or darkens (negative) by percentage |
215
- | `mixColors(c1, c2, t, opts?)` | Interpolates between two colors. `t` = 0–1. `mode`: `'rgb'` \| `'hsl'` \| `'lab'` \| `'lch'` \| `'oklab'` \| `'oklch'`. `hueInterpolation`: `'shorter'` \| `'longer'` \| `'increasing'` \| `'decreasing'` |
216
-
217
- ### `mixColors` example
218
-
219
- ```ts
220
- import { mixColors } from 'color-value-tools'
221
-
222
- // RGB mix at midpoint
223
- mixColors('#e74c3c', '#3498db', 0.5)
224
- // → '#8dc6bc'
225
-
226
- // Perceptually even mix in Oklab
227
- mixColors('#e74c3c', '#3498db', 0.5, { mode: 'oklab', format: 'hex' })
228
-
229
- // HSL with "longer" hue path
230
- mixColors('#e74c3c', '#3498db', 0.5, { mode: 'hsl', hueInterpolation: 'longer' })
231
-
232
- // Get rgba string output
233
- mixColors('#ff0000', '#0000ff', 0.25, { mode: 'lab', format: 'rgba' })
234
- ```
235
-
236
- ---
237
-
238
- ## Interpolation & Scales
239
-
240
- | Function | Description |
241
- |---|---|
242
- | `interpolateColors(c1, c2, steps, opts?)` | Returns array of `steps` colors from `c1` to `c2`. Same `space` / `format` / `hueInterpolation` options as `mixColors` |
243
- | `createColorScale(anchors, steps, opts?)` | Generates a `steps`-color scale across multiple anchor colors with optional positions (0–1) |
244
- | `midpointColor(c1, c2, opts?)` | Perceptual midpoint between two colors (default space: `'oklab'`) |
245
-
246
- ### `createColorScale` — multi-stop gradient
247
-
248
- ```ts
249
- import { createColorScale } from 'color-value-tools'
250
-
251
- // Evenly spaced anchors
252
- createColorScale(['#ff0000', '#ffff00', '#00ff00'], 9)
253
-
254
- // With explicit positions
255
- createColorScale(
256
- [
257
- { color: '#1a1a2e', position: 0 },
258
- { color: '#e94560', position: 0.4 },
259
- { color: '#f5a623', position: 1 },
260
- ],
261
- 12,
262
- { space: 'oklch' }
263
- )
264
- ```
265
-
266
- ---
267
-
268
- ## Color Harmonies
269
-
270
- | Function | Description |
271
- |---|---|
272
- | `complement(color)` | Complementary color (180° rotation) |
273
- | `triadic(color)` | 3 colors evenly spaced 120° apart |
274
- | `analogous(color, angle?)` | 3 neighboring colors (default ±30°) |
275
- | `splitComplementary(color)` | Base + two colors at 150° and 210° |
276
- | `tetradic(color)` | 4 colors evenly spaced 90° apart |
277
-
278
- ```ts
279
- import { triadic, analogous, tetradic } from 'color-value-tools'
280
-
281
- triadic('#6c3483') // ['#6c3483', '#34836c', '#83346c']
282
- analogous('#6c3483', 45) // ['#833469', '#6c3483', '#3469…']
283
- tetradic('#6c3483') // 4 colors
284
- ```
285
-
286
- ---
287
-
288
- ## Palette Generation
289
-
290
- | Function | Description |
291
- |---|---|
292
- | `colorShades(color, steps?)` | Light-to-dark HSL scale (default 9 steps) |
293
- | `monochromatic(color, steps?)` | Varying saturation at fixed lightness (default 5 steps) |
294
- | `tints(color, steps?)` | Mix toward white in Oklab (default 5 steps) |
295
- | `shades(color, steps?)` | Mix toward black in Oklab (default 5 steps) |
296
- | `tones(color, steps?, gray?)` | Mix toward gray in Oklab (default 5 steps, gray `#808080`) |
297
-
298
- ---
299
-
300
- ## Accessibility (WCAG)
301
-
302
- | Function | Description |
303
- |---|---|
304
- | `relativeLuminance(color)` | WCAG relative luminance (0–1) |
305
- | `contrastRatio(c1, c2)` | WCAG contrast ratio (1–21) |
306
- | `wcagLevel(fg, bg)` | Returns `'AAA'` \| `'AA'` \| `'AA-large'` \| `'fail'` |
307
- | `bestTextColor(bg)` | Returns `'#000000'` or `'#ffffff'` for best contrast on `bg` |
308
- | `bestContrastColor(bg, candidates)` | Picks the most readable color from an array of candidates |
309
- | `bestContrastPalette(bg, palettes, opts?)` | Picks the palette with best overall contrast. Returns `{ paletteIndex, palette, minContrastRatio, avgContrastRatio }` |
310
- | `isReadableOnBackground(text, bg, opts?)` | Checks readability on solid, semi-transparent, or gradient backgrounds. Returns `{ readable, minContrastRatio, wcagLevel }` |
311
- | `isDark(color, threshold?)` | `true` if luminance is below threshold (default 0.5) |
312
- | `isLight(color, threshold?)` | Inverse of `isDark` |
313
-
314
- ### `isReadableOnBackground` — complex backgrounds
315
-
316
- `background` can be a plain color string, a semi-transparent spec, or a gradient with multiple stops:
317
-
318
- ```ts
319
- import { isReadableOnBackground } from 'color-value-tools'
320
-
321
- // Solid background
322
- isReadableOnBackground('#ffffff', '#3498db')
323
- // → { readable: true, minContrastRatio: 3.05, wcagLevel: 'AA-large' }
324
-
325
- // Semi-transparent overlay composited over white
326
- isReadableOnBackground('#ffffff', {
327
- type: 'semi-transparent',
328
- color: 'rgba(52, 152, 219, 0.5)',
329
- underlay: '#f0f0f0',
330
- })
331
-
332
- // Gradient — worst stop is used for the result
333
- isReadableOnBackground('#ffffff', {
334
- type: 'gradient',
335
- stops: ['#1a1a2e', '#e94560', '#f5a623'],
336
- })
337
-
338
- // AAA + large text
339
- isReadableOnBackground('#000000', '#f0f0f0', { level: 'AAA', largeText: true })
340
- ```
341
-
342
- ### `bestContrastPalette` — pick accessible palette
343
-
344
- ```ts
345
- import { bestContrastPalette } from 'color-value-tools'
346
-
347
- const result = bestContrastPalette('#1a1a2e', [
348
- ['#ffffff', '#f0f0f0', '#cccccc'],
349
- ['#ffff00', '#ffd700', '#ff8c00'],
350
- ])
351
- // → { paletteIndex: 0, palette: [...], minContrastRatio: 12.1, avgContrastRatio: 14.3 }
352
- ```
353
-
354
- ---
355
-
356
- ## Color Blindness Simulation
357
-
358
- Simulates perception using Vienot 1999 matrices applied to linearized RGB.
359
-
360
- | Function | Description |
361
- |---|---|
362
- | `simulateProtanopia(color)` | No L-cones (red-blind) |
363
- | `simulateDeuteranopia(color)` | No M-cones (green-blind) |
364
- | `simulateTritanopia(color)` | No S-cones (blue-blind) |
365
- | `simulateColorBlindness(color, type)` | Generic — `type`: `'protanopia'` \| `'deuteranopia'` \| `'tritanopia'` |
366
-
367
- ```ts
368
- import { simulateColorBlindness } from 'color-value-tools'
369
-
370
- simulateColorBlindness('#e74c3c', 'deuteranopia') // '#9a7b00'
371
- simulateColorBlindness('#3498db', 'protanopia') // '#5282db'
372
- ```
373
-
374
- ---
375
-
376
- ## Formatting
377
-
378
- | Function | Description |
379
- |---|---|
380
- | `toHslString(h, s, l, alpha?)` | Formats as `hsl(...)` or `hsla(...)` |
381
- | `toHwbString(H, W, B, alpha?)` | Formats as `hwb(...)` with optional alpha |
382
- | `toOklchString(color, alpha?)` | Converts any color to `oklch(L C H)` CSS string |
383
- | `toColorP3String(color, alpha?)` | Converts any color to `color(display-p3 r g b)` CSS string |
384
-
385
- ```ts
386
- import { toOklchString, toColorP3String, toHwbString } from 'color-value-tools'
387
-
388
- toOklchString('#3498db') // 'oklch(0.6 0.1175 234.77)'
389
- toColorP3String('#3498db') // 'color(display-p3 0.2493 0.5875 0.8479)'
390
- toHwbString(204, 20, 14) // 'hwb(204 20% 14%)'
391
- toHwbString(204, 20, 14, 0.8) // 'hwb(204 20% 14% / 0.8)'
392
- ```
393
-
394
- ---
395
-
396
- ## Cache Management
397
-
398
- Useful in rendering loops, color pickers, or any context with repeated `normalizeColor` calls on the same values.
399
-
400
- | Function | Description |
401
- |---|---|
402
- | `normalizeColorCached(input)` | Cached version of `normalizeColor` for repeated string calls |
403
- | `clearColorCache()` | Clears the normalization cache and resets hit counter |
404
- | `getCacheStats()` | Returns `{ size: number, hits: number }` |
405
- | `enableCache()` | Re-enables the cache (on by default) |
406
- | `disableCache()` | Disables caching (useful in tests) |
407
-
408
- ```ts
409
- import { normalizeColorCached, getCacheStats, clearColorCache } from 'color-value-tools'
410
-
411
- // First call — parsed and cached
412
- normalizeColorCached('#3498db')
413
-
414
- // Second call — instant cache hit
415
- normalizeColorCached('#3498db')
416
-
417
- getCacheStats() // { size: 1, hits: 1 }
418
- clearColorCache() // { size: 0, hits: 0 } reset
419
- ```
420
-
421
- ---
422
-
423
- ## Generator Functions
424
-
425
- Useful for large palettes and frame-by-frame animations without allocating full arrays upfront.
426
-
427
- | Function | Description |
428
- |---|---|
429
- | `generateGradientColors*(start, end, steps, opts?)` | Yields `steps` colors from `start` to `end`. Same `mode` / `format` options as `mixColors` |
430
- | `generateTints*(color, steps, opts?)` | Yields tints toward white in Oklab |
431
- | `generateShades*(color, steps, opts?)` | Yields shades toward black in Oklab |
432
-
433
- ```ts
434
- import { generateGradientColors, generateTints } from 'color-value-tools'
435
-
436
- // Process one color at a time — no intermediate array
437
- for (const color of generateGradientColors('#ff0000', '#0000ff', 1000, { mode: 'oklch' })) {
438
- renderPixel(color)
439
- }
440
-
441
- // Collect lazily
442
- const first5 = [...generateTints('#e74c3c', 100)].slice(0, 5)
443
- ```
444
-
445
- ---
446
-
447
- ## CLI
448
-
449
- The `cvt` CLI is included in the package. Install globally or use via `npx`:
450
-
451
- ```bash
452
- npm install -g color-value-tools
453
- ```
454
-
455
- ```bash
456
- # Full info (default command)
457
- cvt "#3498db"
458
-
459
- # All format conversions — hex, rgb, hsl, hsv, hwb, lab, lch, oklab, oklch, cmyk
460
- cvt "#3498db" convert
461
-
462
- # Contrast ratio and WCAG level
463
- cvt "#3498db" contrast "#ffffff"
464
-
465
- # Generate 7 shades
466
- cvt "#3498db" shades 7
467
-
468
- # All harmonies — complement, triadic, analogous, split-comp, tetradic
469
- cvt "#3498db" harmonies
470
-
471
- # Nearest CSS named color
472
- cvt "cornflowerblue" nearest
473
- ```
474
-
475
- Accepts any valid color format: hex, `rgb()`, `hsl()`, `oklch()`, named colors.
476
-
477
- ```
478
- cvt "#3498db"
479
- ────────────────────────────────────────
480
- Type: hex
481
- Hex: #3498db
482
- RGB: rgb(52, 152, 219)
483
- HSL: hsl(204, 70%, 53%)
484
- HSV: hsv(204, 76%, 86%)
485
- Nearest named: cornflowerblue
486
- Best text on it: #ffffff
487
- ```
488
-
489
- ---
490
-
491
- ## Cookbook
492
-
493
- ### Generate an accessible button palette
494
-
495
- ```ts
496
- import { colorShades, bestTextColor, wcagLevel } from 'color-value-tools'
497
-
498
- function buttonPalette(base: string) {
499
- return colorShades(base, 9).map(shade => ({
500
- bg: shade,
501
- text: bestTextColor(shade),
502
- wcag: wcagLevel(bestTextColor(shade), shade),
503
- }))
504
- }
505
-
506
- buttonPalette('#3498db')
507
- // [{ bg: '#ffffff', text: '#000000', wcag: 'AAA' }, ...]
508
- ```
509
-
510
- ### Theme-aware color adaptation
511
-
512
- ```ts
513
- import { isDark, lighten, darken } from 'color-value-tools'
514
-
515
- function adaptToTheme(color: string, isDarkTheme: boolean): string {
516
- return isDarkTheme ? lighten(color, 20) : darken(color, 10)
517
- }
518
- ```
519
-
520
- ### Mix two brand colors at a perceptual midpoint
521
-
522
- ```ts
523
- import { midpointColor } from 'color-value-tools'
524
-
525
- const mid = midpointColor('#e74c3c', '#3498db') // Oklab midpoint
526
- const midLch = midpointColor('#e74c3c', '#3498db', { space: 'oklch' })
527
- ```
528
-
529
- ### Build a triadic scheme and check contrast
530
-
531
- ```ts
532
- import { triadic, contrastRatio } from 'color-value-tools'
533
-
534
- const [base, second, third] = triadic('#6c3483')
535
- console.log(contrastRatio(base, '#ffffff')) // e.g. 8.4
536
- console.log(contrastRatio(second, '#ffffff'))
537
- ```
538
-
539
- ### Convert any color string to all formats at once
540
-
541
- ```ts
542
- import { normalizeColor, rgbToOklch, rgbToCmyk, toColorP3String } from 'color-value-tools'
543
-
544
- const n = normalizeColor('hsl(204, 70%, 53%)')
545
- const oklch = rgbToOklch({ r: n.r!, g: n.g!, b: n.b! })
546
- const cmyk = rgbToCmyk({ r: n.r!, g: n.g!, b: n.b! })
547
- const p3 = toColorP3String(n.hex!)
548
- console.log(n.hex, oklch, cmyk, p3)
549
- ```
550
-
551
- ### Find the nearest CSS named color
552
-
553
- ```ts
554
- import { toNearestNamedColor } from 'color-value-tools'
555
-
556
- toNearestNamedColor('#1a8ccc') // 'steelblue'
557
- toNearestNamedColor('#e74c3c') // 'tomato'
558
- ```
559
-
560
- ### Random palette within a hue range
561
-
562
- ```ts
563
- import { randomColor, colorShades } from 'color-value-tools'
564
-
565
- const accent = randomColor({ hRange: [200, 260], sRange: [60, 80], lRange: [40, 60] })
566
- const palette = colorShades(accent, 5)
567
- ```
568
-
569
- ### Simulate color blindness for a palette
570
-
571
- ```ts
572
- import { triadic, simulateColorBlindness } from 'color-value-tools'
573
-
574
- const palette = triadic('#e74c3c')
575
- const deuteranopia = palette.map(c => simulateColorBlindness(c, 'deuteranopia'))
576
- // Compare palette vs deuteranopia to verify distinguishability
577
- ```
578
-
579
- ### Lazy gradient — process 10 000 colors one at a time
580
-
581
- ```ts
582
- import { generateGradientColors } from 'color-value-tools'
583
-
584
- for (const color of generateGradientColors('#1a1a2e', '#f5a623', 10_000, { mode: 'oklch' })) {
585
- ctx.fillStyle = color
586
- ctx.fillRect(x++, 0, 1, height)
587
- }
588
- ```
589
-
590
- ---
591
-
592
- ## TypeScript types
593
-
594
- All public types are exported from the package root:
595
-
596
- ```ts
597
- import type {
598
- ColorType, // 'hex' | 'css-var' | 'rgb' | 'hsl' | 'named' | 'oklch' | 'color' | 'unknown'
599
- WcagLevel, // 'AAA' | 'AA' | 'AA-large' | 'fail'
600
- ColorBlindnessType, // 'protanopia' | 'deuteranopia' | 'tritanopia'
601
- BackgroundSpec, // string | { type: 'semi-transparent'; ... } | { type: 'gradient'; ... }
602
- PaletteScore, // { palette, minContrastRatio, avgContrastRatio }
603
- } from 'color-value-tools'
604
- ```
605
-
606
- ### `BackgroundSpec` — discriminated union
607
-
608
- ```ts
609
- import type { BackgroundSpec } from 'color-value-tools'
610
-
611
- const solid: BackgroundSpec = '#3498db'
612
-
613
- const semiTransparent: BackgroundSpec = {
614
- type: 'semi-transparent',
615
- color: 'rgba(0,0,0,0.4)',
616
- underlay: '#ffffff', // optional, defaults to white
617
- }
618
-
619
- const gradient: BackgroundSpec = {
620
- type: 'gradient',
621
- stops: ['#1a1a2e', '#e94560', '#f5a623'],
622
- }
623
- ```
624
-
625
- ### `PaletteScore` — from `bestContrastPalette`
626
-
627
- ```ts
628
- import type { PaletteScore } from 'color-value-tools'
629
-
630
- const result: PaletteScore & { paletteIndex: number } = bestContrastPalette(bg, palettes)
631
- result.paletteIndex // number
632
- result.palette // string[]
633
- result.minContrastRatio // number
634
- result.avgContrastRatio // number
635
- ```
636
-
637
- ### Inference from `normalizeColor`
638
-
639
- ```ts
640
- import { normalizeColor } from 'color-value-tools'
641
-
642
- const n = normalizeColor('#3498db')
643
- // TypeScript knows: n.hex, n.r, n.g, n.b, n.h, n.s, n.l, n.v, n.a, n.type
644
-
645
- if (n.type !== 'unknown' && n.type !== 'css-var') {
646
- const r: number = n.r! // available for all resolved color types
647
- }
648
- ```
649
-
650
- ---
651
-
652
- ## Architecture
653
-
654
- ```
655
- color-value-tools
656
- │
657
- ├── Detection
658
- │ getColorType, isHexColor, isRgbColor, isHslColor,
659
- │ isOklchColor, isColorFunction, isCssVariable
660
- │
661
- ├── Parsing
662
- │ normalizeColor → universal entry point; handles all formats + objects
663
- │ rgbaStringToRgba → rgb()/rgba() parser
664
- │ hex8ToRgba → 8-digit and 4-digit hex with alpha
665
- │ parseOklchString → oklch(L C H / alpha) with % L support
666
- │ parseColorFn → color(display-p3 ...) / color(srgb ...)
667
- │ parseHwbString → hwb() string
668
- │ parseCssVar → var(--name, fallback)
669
- │
670
- ├── Color Math — every space via RGB as the hub
671
- │ sRGB ↔ Linear (srgbChanToLinear / linearChanToSrgb)
672
- │ XYZ D65 intermediate for Lab/LCH
673
- │ Oklab/Oklch — Björn Ottosson matrices
674
- │ Display P3 — ICC sRGB→P3 and P3→sRGB matrices
675
- │
676
- ├── Manipulation
677
- │ lighten / darken / saturate / desaturate — via HSL
678
- │ invertColor — RGB invert
679
- │ grayscale — BT.709 perceptual weights
680
- │ rotateHue — via HSL hue shift
681
- │ adjustHexBrightness — linear channel blend toward 0/255
682
- │ setAlpha / getAlpha — rgba string manipulation
683
- │
684
- ├── mixColors (core of interpolation system)
685
- │ 6 color space modes: rgb | hsl | lab | lch | oklab | oklch
686
- │ 4 hue interpolation modes: shorter | longer | increasing | decreasing
687
- │ 4 output formats: hex | rgb | rgba | hsl
688
- │ Used internally by: interpolateColors, createColorScale,
689
- │ midpointColor, tints, shades, tones,
690
- │ generateGradientColors*, generateTints*, generateShades*
691
- │
692
- ├── Color Harmonies
693
- │ All implemented as hue rotations on normalized hex
694
- │
695
- ├── Palette Generation
696
- │ colorShades — HSL lightness sweep
697
- │ monochromatic — HSL saturation sweep
698
- │ tints / shades / tones — Oklab interpolation via mixColors
699
- │
700
- ├── Accessibility
701
- │ relativeLuminance — WCAG linearization formula
702
- │ contrastRatio — (L1+0.05)/(L2+0.05)
703
- │ wcagLevel — ratio thresholds 3 / 4.5 / 7
704
- │ isReadableOnBackground — handles 3 background types;
705
- │ semi-transparent: alpha-composites onto underlay before ratio check
706
- │ gradient: evaluates each stop, uses minimum ratio
707
- │ bestContrastPalette — score = avg×0.4 + min×0.6
708
- │
709
- ├── Color Blindness
710
- │ Vienot 1999 matrices on linear RGB
711
- │ 3 types: protanopia / deuteranopia / tritanopia
712
- │
713
- ├── Cache (Section 5.1)
714
- │ Simple Map<string, NormalizeResult>
715
- │ normalizeColorCached — Map lookup before calling normalizeColor
716
- │ getCacheStats / clearColorCache / enableCache / disableCache
717
- │
718
- ├── Generator functions (Section 5.2)
719
- │ ES2015 generator syntax (*) — yield one color at a time
720
- │ generateGradientColors* → mixColors inside the generator
721
- │ generateTints* / generateShades* → Oklab mix toward white/black
722
- │
723
- └── CLI (dist/cli/bin/cli.js → cvt)
724
- Commands: info | convert | contrast | shades | harmonies | nearest
725
- Accepts any color format normalizeColor understands
726
- ```
727
-
728
- ---
729
-
730
- ## Bundle size & dependencies
731
-
732
- | | |
733
- |---|---|
734
- | **Runtime dependencies** | None |
735
- | **Peer dependencies** | None |
736
- | **Dev dependencies** | TypeScript, Vitest |
737
- | **ESM entry** | `dist/esm/index.js` (tree-shakeable) |
738
- | **CJS entry** | `dist/cjs/index.js` |
739
- | **CLI entry** | `dist/cli/bin/cli.js` → `cvt` |
740
-
741
- The package ships both ESM (`type: module`) and CommonJS modules. Every function is a named export — unused functions are eliminated by bundlers that support tree-shaking.
87
+ - 📖 **Full documentation:** [npm.vuecraft.ru/en/packages/color-value-tools](https://npm.vuecraft.ru/en/packages/color-value-tools/guide/overview.html)
88
+ - 🌐 **VueCraft:** [vuecraft.ru/en](https://vuecraft.ru/en)
89
+ - 👤 **Author:** [macrulez.ru/en](https://macrulez.ru/en)
90
+ - 💻 **GitHub:** [macrulezru/color-value-tools](https://github.com/macrulezru/color-value-tools)
91
+ - 📦 **NPM:** [color-value-tools](https://www.npmjs.com/package/color-value-tools)
92
+ - 🐛 **Issues:** [github.com/macrulezru/color-value-tools/issues](https://github.com/macrulezru/color-value-tools/issues)
742
93
 
743
94
  ---
744
95
 
@@ -748,19 +99,9 @@ MIT
748
99
 
749
100
  ---
750
101
 
751
- ## Author
752
-
753
- Danil Lisin Vladimirovich aka Macrulez
754
-
755
- GitHub: [macrulezru](https://github.com/macrulezru) · Website: [npm.vuecraft.ru/en/](https://npm.vuecraft.ru/en/packages/color-value-tools/)
756
-
757
- Bugs and questions — [issues](https://github.com/macrulezru/color-value-tools/issues)
758
-
759
- ---
760
-
761
102
  ## 💖 Support the project
762
103
 
763
- Open source takes time and effort. If my work saves you time or brings value, consider supporting further development.
104
+ Open source takes time and effort. If this library saves you time or brings value, consider supporting further development.
764
105
 
765
106
  <a href="https://donate.cryptocloud.plus/M6O34NIN" target="_blank">
766
107
  <img src="https://img.shields.io/badge/Donate-CryptoCloud-8A2BE2?style=for-the-badge&logo=cryptocurrency&logoColor=white" alt="Donate via CryptoCloud">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "color-value-tools",
3
- "version": "1.1.9",
3
+ "version": "1.1.10",
4
4
  "description": "Parse, convert and manipulate colors across hex, RGB, HSL, Lab, LCH, OKLCH and CMYK, with WCAG contrast checks and CSS variables. Zero dependencies.",
5
5
  "author": "macrulez <macrulezru@gmail.com> (https://macrulez.ru/en)",
6
6
  "license": "MIT",