color-value-tools 1.0.1 → 1.1.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.
package/README.md CHANGED
@@ -1,70 +1,343 @@
1
- # color-value-tools
2
-
3
- A tiny utility library for parsing, converting, and manipulating color values across common formats (hex, RGB, HSL, HSV, Lab, CMYK) and CSS variables.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install color-value-tools
9
- ```
10
-
11
- ## Usage
12
-
13
- Example (ESM / TypeScript):
14
-
15
- ```ts
16
- import { normalizeColor, mixColors } from 'color-value-tools';
17
-
18
- console.log(normalizeColor('#3498db'));
19
- // { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, ... }
20
-
21
- console.log(mixColors('#ff0000', '#0000ff', 0.5));
22
- // '#800080'
23
- ```
24
-
25
- Example (CommonJS / Node):
26
-
27
- ```js
28
- const { normalizeColor, mixColors } = require('color-value-tools');
29
- console.log(normalizeColor('rgba(255,0,0,0.5)'));
30
- ```
31
-
32
- ## API & Options
33
-
34
- - **`isCssVariable`**: Check if a string is a CSS variable (e.g. `var(--main)`).
35
- - **`isHexColor`**: Detect hex color strings (3- or 6-digit, with or without `#`).
36
- - **`isRgbColor`**: Detect `rgb()` / `rgba()` color strings.
37
- - **`isHslColor`**: Detect `hsl()` / `hsla()` color strings.
38
- - **`getColorType`**: Returns the color type: `hex`, `css-var`, `rgb`, `hsl`, `named`, or `unknown`.
39
- - **`extractCssVariableName`**: Extracts the CSS variable name from `var(--name)`.
40
- - **`normalizeHex`**: Normalizes and validates hex strings, returns a 6-digit lowercase hex (fallback `#f5e477`).
41
- - **`hexToRgb`**: Convert a hex color to an `[r, g, b]` tuple.
42
- - **`hexToRgba`**: Convert a hex color to an `rgba(...)` string with opacity.
43
- - **`hexToHsl`**: Convert a hex color to an `[h, s, l]` tuple.
44
- - **`hslToHex`**: Convert HSL values to a hex color string.
45
- - **`adjustHexBrightness`**: Lighten or darken a hex color by a percentage offset.
46
- - **`rotateHue`**: Rotate the hue of a hex color by degrees.
47
- - **`rgbToHex` / `rgbaToHex`**: Convert RGB(A) channels to `#rrggbb` / `#rrggbbaa`.
48
- - **`rgbToRgbaString` / `rgbaStringToRgba`**: Build and parse `rgba(...)` / `rgb(...)` strings.
49
- - **`rgbToHsl` / `hslToRgb`**: RGB ↔ HSL conversions.
50
- - **`rgbToHsv` / `hsvToRgb`**: RGB ↔ HSV conversions.
51
- - **`hexToHsv` / `hsvToHex`**: Hex ↔ HSV helpers.
52
- - **`hex8ToRgba` / `rgbaToHex8`**: Parse and build 8-digit hex with alpha.
53
- - **`normalizeColor`**: Universal parser/normalizer returning `{ type, hex?, r?, g?, b?, a?, h?, s?, l?, v? }`.
54
- - **`mixColors`**: Linear interpolation between two colors (supports `rgb` or `hsl` mixing, returns hex/rgb/rgba/hsl).
55
- - **`relativeLuminance` / `contrastRatio`**: WCAG relative luminance and contrast ratio.
56
- - **`isDark` / `isLight`**: Quick luminance-based checks.
57
- - **`rgbToCmyk` / `cmykToRgb`**: CMYK conversions for print scenarios.
58
- - **`rgbToLab` / `labToRgb` / `rgbToLch` / `lchToRgb`**: Perceptual color space conversions (Lab / LCH) for advanced operations.
59
-
60
- ## Author
61
-
62
- Danil Lisin Vladimirovich aka Macrulez
63
-
64
- GitHub: [macrulezru](https://github.com/macrulezru)
65
-
66
- Website: [macrulez.ru](https://macrulez.ru/)
67
-
68
- ## License
69
-
70
- MIT
1
+ # color-value-tools
2
+
3
+ A comprehensive utility library for parsing, converting, manipulating, and analyzing color values across all major color models — hex, RGB, HSL, HSV, HWB, Lab, LCH, OKLAB, OKLCH, CMYK plus CSS variables and named colors.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install color-value-tools
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import {
15
+ normalizeColor, mixColors,
16
+ lighten, darken, saturate,
17
+ complement, triadic,
18
+ wcagLevel, bestTextColor,
19
+ colorDeltaE, randomColor,
20
+ } from 'color-value-tools';
21
+
22
+ // Parse any color format
23
+ normalizeColor('#3498db');
24
+ // { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, h: 204, s: 70, l: 53, ... }
25
+
26
+ // Manipulate
27
+ lighten('#3498db', 15); // '#6ab4e8'
28
+ darken('#3498db', 15); // '#1a6da3'
29
+ saturate('#3498db', 20); // '#1a8fe8'
30
+
31
+ // Harmonies
32
+ complement('#3498db'); // '#db6034'
33
+ triadic('#3498db'); // ['#3498db', '#db3498', '#98db34']
34
+
35
+ // WCAG accessibility
36
+ wcagLevel('#ffffff', '#3498db'); // 'AA'
37
+ bestTextColor('#3498db'); // '#ffffff'
38
+
39
+ // Perceptual color distance (CIEDE2000)
40
+ colorDeltaE('#ff0000', '#fe0000'); // ~0.9
41
+
42
+ // Random color
43
+ randomColor({ hRange: [200, 260], sRange: [60, 80] });
44
+ ```
45
+
46
+ CommonJS:
47
+ ```js
48
+ const { normalizeColor, mixColors } = require('color-value-tools');
49
+ ```
50
+
51
+ ---
52
+
53
+ ## API Reference
54
+
55
+ ### Detection
56
+
57
+ | Function | Description |
58
+ |---|---|
59
+ | `getColorType(value)` | Returns `'hex'` \| `'css-var'` \| `'rgb'` \| `'hsl'` \| `'named'` \| `'oklch'` \| `'color'` \| `'unknown'` |
60
+ | `isHexColor(value)` | Detects 3-, 4-, 6- or 8-digit hex strings (with or without `#`) |
61
+ | `isRgbColor(value)` | Detects `rgb()` / `rgba()` strings |
62
+ | `isHslColor(value)` | Detects `hsl()` / `hsla()` strings |
63
+ | `isOklchColor(value)` | Detects `oklch()` / `oklcha()` strings |
64
+ | `isColorFunction(value)` | Detects `color(display-p3 ...)` / `color(srgb ...)` strings |
65
+ | `isCssVariable(value)` | Checks for `var(--name)` pattern |
66
+ | `extractCssVariableName(value)` | Extracts `--name` from `var(--name, fallback)` |
67
+
68
+ ### Parsing & Normalization
69
+
70
+ | Function | Description |
71
+ |---|---|
72
+ | `normalizeColor(input)` | Universal parser. Accepts hex, `rgb()`, `hsl()`, `oklch()`, `color()`, named color, `{r,g,b}` / `{h,s,l}`. Returns `{ type, hex, r, g, b, a, h, s, l, v }` |
73
+ | `normalizeColorCached(input)` | Same as `normalizeColor` but uses an internal LRU-style cache |
74
+ | `normalizeHex(hex)` | Normalizes 3- or 6-digit hex to lowercase 6-digit with `#` |
75
+ | `rgbaStringToRgba(str)` | Parses `rgb()` / `rgba()` string to `{r, g, b, a}` |
76
+ | `hex8ToRgba(hex)` | Parses 8-digit hex (`#rrggbbaa`) to `{r, g, b, a}` |
77
+ | `shortHexToRgba(hex)` | Parses 4-digit hex (`#rgba`) to `{r, g, b, a}` — e.g. `#f0f0` → `{r:255,g:0,b:255,a:0}` |
78
+ | `parseHwbString(str)` | Parses `hwb()` string to `{H, W, B, alpha}` |
79
+ | `parseOklchString(str)` | Parses `oklch(L C H / alpha)` to `{L, C, H, alpha}` |
80
+ | `parseColorFn(str)` | Parses `color(display-p3 r g b / alpha)` to `{space, r, g, b, alpha}` |
81
+ | `parseCssVar(value)` | Parses `var(--name, fallback)` to `{variableName, fallback?}` |
82
+
83
+ ### Conversions
84
+
85
+ | Function | Description |
86
+ |---|---|
87
+ | `hexToRgb(hex)` | `→ [r, g, b]` |
88
+ | `hexToRgba(hex, opacity)` | `→ rgba(...)` string |
89
+ | `hexToHsl(hex)` | `→ [h, s, l]` |
90
+ | `hexToHsv(hex)` | `→ [h, s, v]` |
91
+ | `hslToHex(h, s, l)` | `→ #rrggbb` |
92
+ | `hslToRgb(h, s, l)` | `→ {r, g, b}` |
93
+ | `hsvToHex(h, s, v)` | `→ #rrggbb` |
94
+ | `hsvToRgb(h, s, v)` | `→ {r, g, b}` |
95
+ | `rgbToHex({r,g,b})` | `→ #rrggbb` |
96
+ | `rgbaToHex({r,g,b,a})` | `→ #rrggbbaa` |
97
+ | `rgbToRgbaString({r,g,b}, a)` | `→ rgba(...)` string |
98
+ | `rgbToHsl({r,g,b})` | `→ [h, s, l]` |
99
+ | `rgbToHsv({r,g,b})` | `→ [h, s, v]` |
100
+ | `rgbToHwb({r,g,b})` | `→ [H, W, B]` |
101
+ | `hwbToRgb(H, W, B)` | `→ {r, g, b}` |
102
+ | `rgbToCmyk({r,g,b})` | `→ {c, m, y, k}` (0–1 range) |
103
+ | `cmykToRgb({c,m,y,k})` | `→ {r, g, b}` |
104
+ | `rgbToLab({r,g,b})` | `→ {L, a, b}` (CIE Lab D65) |
105
+ | `labToRgb({L,a,b})` | `→ {r, g, b}` |
106
+ | `rgbToLch({r,g,b})` | `→ {L, C, H}` (CIE LCH) |
107
+ | `lchToRgb({L,C,H})` | `→ {r, g, b}` |
108
+ | `rgbToOklab({r,g,b})` | `→ {L, a, b}` (Oklab) |
109
+ | `oklabToRgb({L,a,b})` | `→ {r, g, b}` |
110
+ | `rgbToOklch({r,g,b})` | `→ {L, C, H}` (Oklch) |
111
+ | `oklchToRgb({L,C,H})` | `→ {r, g, b}` |
112
+ | `rgbToDisplayP3({r,g,b})` | `→ {r, g, b}` in Display P3 space (0–1 per channel) |
113
+ | `displayP3ToRgb({r,g,b})` | `→ {r, g, b}` sRGB (0–255) |
114
+ | `toDisplayP3Hex(color)` | Converts any color → Display P3 → hex |
115
+ | `rgbaToHex8({r,g,b,a})` | Alias for `rgbaToHex` |
116
+
117
+ ### Manipulation
118
+
119
+ | Function | Description |
120
+ |---|---|
121
+ | `lighten(color, amount)` | Increase HSL lightness by `amount` (0–100) |
122
+ | `darken(color, amount)` | Decrease HSL lightness by `amount` (0–100) |
123
+ | `saturate(color, amount)` | Increase HSL saturation by `amount` (0–100) |
124
+ | `desaturate(color, amount)` | Decrease HSL saturation by `amount` (0–100) |
125
+ | `setAlpha(color, alpha)` | Returns `rgba(...)` with the given alpha (0–1) |
126
+ | `getAlpha(color)` | Returns the alpha channel value (0–1) |
127
+ | `invertColor(color)` | Inverts RGB channels |
128
+ | `grayscale(color)` | Converts to grayscale using ITU-R BT.709 weights |
129
+ | `rotateHue(hex, degrees)` | Rotates hue by degrees (supports negative values) |
130
+ | `adjustHexBrightness(hex, offsetPercent)` | Lightens (positive) or darkens (negative) by percentage |
131
+ | `mixColors(c1, c2, t, opts?)` | Interpolates between two colors. `t` = 0–1. `mode`: `'rgb'`\|`'hsl'`\|`'lab'`\|`'lch'`\|`'oklab'`\|`'oklch'`. `hueInterpolation`: `'shorter'`\|`'longer'`\|`'increasing'`\|`'decreasing'` |
132
+
133
+ ### Interpolation & Scales
134
+
135
+ | Function | Description |
136
+ |---|---|
137
+ | `interpolateColors(c1, c2, steps, opts?)` | Returns array of `steps` colors from `c1` to `c2`. Same `space`/`format`/`hueInterpolation` options as `mixColors` |
138
+ | `createColorScale(anchors, steps, opts?)` | Generates a `steps`-color scale across multiple anchor colors with optional positions (0–1) |
139
+ | `midpointColor(c1, c2, opts?)` | Perceptual midpoint between two colors (default space: `'oklab'`) |
140
+
141
+ ### Color Harmonies
142
+
143
+ | Function | Description |
144
+ |---|---|
145
+ | `complement(color)` | Complementary color (180° rotation) |
146
+ | `triadic(color)` | 3 colors evenly spaced 120° apart |
147
+ | `analogous(color, angle?)` | 3 neighboring colors (default ±30°) |
148
+ | `splitComplementary(color)` | Base + two colors at 150° and 210° |
149
+ | `tetradic(color)` | 4 colors evenly spaced 90° apart |
150
+
151
+ ### Palette Generation
152
+
153
+ | Function | Description |
154
+ |---|---|
155
+ | `colorShades(color, steps?)` | Light-to-dark HSL scale (default 9 steps) |
156
+ | `monochromatic(color, steps?)` | Varying saturation at fixed lightness (default 5 steps) |
157
+ | `tints(color, steps?)` | Mix toward white in Oklab (default 5 steps) |
158
+ | `shades(color, steps?)` | Mix toward black in Oklab (default 5 steps) |
159
+ | `tones(color, steps?, gray?)` | Mix toward gray in Oklab (default 5 steps, gray `#808080`) |
160
+
161
+ ### Accessibility (WCAG)
162
+
163
+ | Function | Description |
164
+ |---|---|
165
+ | `relativeLuminance(color)` | WCAG relative luminance (0–1) |
166
+ | `contrastRatio(c1, c2)` | WCAG contrast ratio (1–21) |
167
+ | `wcagLevel(fg, bg)` | Returns `'AAA'` \| `'AA'` \| `'AA-large'` \| `'fail'` |
168
+ | `bestTextColor(bg)` | Returns `'#000000'` or `'#ffffff'` for best contrast on `bg` |
169
+ | `bestContrastColor(bg, candidates)` | Picks the most readable color from an array of candidates |
170
+ | `bestContrastPalette(bg, palettes, opts?)` | Picks the palette with best overall contrast. Returns `{paletteIndex, palette, minContrastRatio, avgContrastRatio}` |
171
+ | `isReadableOnBackground(text, bg, opts?)` | Checks readability on solid, semi-transparent, or gradient backgrounds. Returns `{readable, minContrastRatio, wcagLevel}` |
172
+ | `isDark(color, threshold?)` | `true` if luminance is below threshold (default 0.5) |
173
+ | `isLight(color, threshold?)` | Inverse of `isDark` |
174
+
175
+ ### Color Blindness Simulation
176
+
177
+ Simulates perception using Vienot 1999 matrices applied to linear RGB.
178
+
179
+ | Function | Description |
180
+ |---|---|
181
+ | `simulateProtanopia(color)` | No L-cones (red-blind) |
182
+ | `simulateDeuteranopia(color)` | No M-cones (green-blind) |
183
+ | `simulateTritanopia(color)` | No S-cones (blue-blind) |
184
+ | `simulateColorBlindness(color, type)` | Generic — `type`: `'protanopia'` \| `'deuteranopia'` \| `'tritanopia'` |
185
+
186
+ ### Formatting
187
+
188
+ | Function | Description |
189
+ |---|---|
190
+ | `toHslString(h, s, l, alpha?)` | Formats as `hsl(...)` or `hsla(...)` |
191
+ | `toHwbString(H, W, B, alpha?)` | Formats as `hwb(...)` with optional alpha |
192
+ | `toOklchString(color, alpha?)` | Converts any color to `oklch(L C H)` CSS string |
193
+ | `toColorP3String(color, alpha?)` | Converts any color to `color(display-p3 r g b)` CSS string |
194
+
195
+ ### Cache Management
196
+
197
+ | Function | Description |
198
+ |---|---|
199
+ | `normalizeColorCached(input)` | Cached version of `normalizeColor` for repeated calls |
200
+ | `clearColorCache()` | Clears the normalization cache |
201
+ | `getCacheStats()` | Returns `{size, hits}` |
202
+ | `enableCache()` / `disableCache()` | Toggle caching on/off |
203
+
204
+ ### Generator Functions
205
+
206
+ Useful for large palettes and frame-by-frame animations without allocating full arrays.
207
+
208
+ | Function | Description |
209
+ |---|---|
210
+ | `generateGradientColors*(start, end, steps, opts?)` | Yields `steps` colors from `start` to `end` |
211
+ | `generateTints*(color, steps, opts?)` | Yields tints toward white |
212
+ | `generateShades*(color, steps, opts?)` | Yields shades toward black |
213
+
214
+ ### Utilities
215
+
216
+ | Function | Description |
217
+ |---|---|
218
+ | `colorDeltaE(c1, c2)` | Perceptual color distance using CIEDE2000 |
219
+ | `randomColor(options?)` | Generates a random color. Options: `hRange`, `sRange`, `lRange` (each `[min, max]`) |
220
+ | `toNearestNamedColor(color)` | Returns the closest CSS named color name (all 148 standard colors) |
221
+
222
+ ---
223
+
224
+ ## CLI
225
+
226
+ After installing globally or via `npx`, you can inspect colors directly from the terminal:
227
+
228
+ ```bash
229
+ npm install -g color-value-tools
230
+ ```
231
+
232
+ ```bash
233
+ # Full info (default)
234
+ cvt "#3498db"
235
+
236
+ # All format conversions
237
+ cvt "#3498db" convert
238
+
239
+ # Contrast ratio and WCAG level
240
+ cvt "#3498db" contrast "#ffffff"
241
+
242
+ # Generate 7 shades
243
+ cvt "#3498db" shades 7
244
+
245
+ # All harmonies
246
+ cvt "#3498db" harmonies
247
+
248
+ # Nearest CSS named color
249
+ cvt "cornflowerblue" nearest
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Cookbook
255
+
256
+ ### Generate an accessible button palette
257
+
258
+ ```ts
259
+ import { colorShades, bestTextColor, wcagLevel } from 'color-value-tools';
260
+
261
+ function buttonPalette(base: string) {
262
+ const shades = colorShades(base, 9);
263
+ return shades.map(shade => ({
264
+ bg: shade,
265
+ text: bestTextColor(shade),
266
+ wcag: wcagLevel(bestTextColor(shade), shade),
267
+ }));
268
+ }
269
+
270
+ buttonPalette('#3498db');
271
+ // [{ bg: '#ffffff', text: '#000000', wcag: 'AAA' }, ...]
272
+ ```
273
+
274
+ ### Theme-aware dark/light color
275
+
276
+ ```ts
277
+ import { isDark, lighten, darken } from 'color-value-tools';
278
+
279
+ function adaptToTheme(color: string, isDarkTheme: boolean): string {
280
+ return isDarkTheme ? lighten(color, 20) : darken(color, 10);
281
+ }
282
+ ```
283
+
284
+ ### Mix two brand colors at a midpoint
285
+
286
+ ```ts
287
+ import { mixColors } from 'color-value-tools';
288
+
289
+ const mid = mixColors('#e74c3c', '#3498db', 0.5, { mode: 'hsl', format: 'hex' });
290
+ // Perceptually even blend between red and blue
291
+ ```
292
+
293
+ ### Build a triadic color scheme and check contrast
294
+
295
+ ```ts
296
+ import { triadic, contrastRatio } from 'color-value-tools';
297
+
298
+ const [base, second, third] = triadic('#6c3483');
299
+ console.log(contrastRatio(base, '#ffffff')); // e.g. 8.4
300
+ console.log(contrastRatio(second, '#ffffff'));
301
+ ```
302
+
303
+ ### Convert a CSS color string to all formats at once
304
+
305
+ ```ts
306
+ import { normalizeColor, rgbToOklch, rgbToCmyk } from 'color-value-tools';
307
+
308
+ const n = normalizeColor('hsl(204, 70%, 53%)');
309
+ const oklch = rgbToOklch({ r: n.r!, g: n.g!, b: n.b! });
310
+ const cmyk = rgbToCmyk({ r: n.r!, g: n.g!, b: n.b! });
311
+ console.log(n.hex, oklch, cmyk);
312
+ ```
313
+
314
+ ### Find the perceptually nearest named CSS color
315
+
316
+ ```ts
317
+ import { toNearestNamedColor } from 'color-value-tools';
318
+
319
+ toNearestNamedColor('#1a8ccc'); // 'steelblue'
320
+ ```
321
+
322
+ ### Random palette within a hue range
323
+
324
+ ```ts
325
+ import { randomColor, colorShades } from 'color-value-tools';
326
+
327
+ const accent = randomColor({ hRange: [200, 260], sRange: [60, 80], lRange: [40, 60] });
328
+ const palette = colorShades(accent, 5);
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Author
334
+
335
+ Danil Lisin Vladimirovich aka Macrulez
336
+
337
+ GitHub: [macrulezru](https://github.com/macrulezru)
338
+
339
+ Website: [macrulez.ru](https://macrulez.ru/)
340
+
341
+ ## License
342
+
343
+ MIT