color-value-tools 1.1.0 → 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
@@ -56,22 +56,29 @@ const { normalizeColor, mixColors } = require('color-value-tools');
56
56
 
57
57
  | Function | Description |
58
58
  |---|---|
59
- | `getColorType(value)` | Returns `'hex'`, `'css-var'`, `'rgb'`, `'hsl'`, `'named'`, or `'unknown'` |
60
- | `isHexColor(value)` | Detects 3- or 6-digit hex strings (with or without `#`) |
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
61
  | `isRgbColor(value)` | Detects `rgb()` / `rgba()` strings |
62
62
  | `isHslColor(value)` | Detects `hsl()` / `hsla()` strings |
63
+ | `isOklchColor(value)` | Detects `oklch()` / `oklcha()` strings |
64
+ | `isColorFunction(value)` | Detects `color(display-p3 ...)` / `color(srgb ...)` strings |
63
65
  | `isCssVariable(value)` | Checks for `var(--name)` pattern |
64
- | `extractCssVariableName(value)` | Extracts `--name` from `var(--name)` |
66
+ | `extractCssVariableName(value)` | Extracts `--name` from `var(--name, fallback)` |
65
67
 
66
68
  ### Parsing & Normalization
67
69
 
68
70
  | Function | Description |
69
71
  |---|---|
70
- | `normalizeColor(input)` | Universal parser. Accepts hex string, `rgb()`, `hsl()`, named color, or `{r,g,b}` / `{h,s,l}` object. Returns `{ type, hex, r, g, b, a, h, s, l, v }` |
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 |
71
74
  | `normalizeHex(hex)` | Normalizes 3- or 6-digit hex to lowercase 6-digit with `#` |
72
75
  | `rgbaStringToRgba(str)` | Parses `rgb()` / `rgba()` string to `{r, g, b, a}` |
73
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}` |
74
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?}` |
75
82
 
76
83
  ### Conversions
77
84
 
@@ -102,40 +109,54 @@ const { normalizeColor, mixColors } = require('color-value-tools');
102
109
  | `oklabToRgb({L,a,b})` | `→ {r, g, b}` |
103
110
  | `rgbToOklch({r,g,b})` | `→ {L, C, H}` (Oklch) |
104
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 |
105
115
  | `rgbaToHex8({r,g,b,a})` | Alias for `rgbaToHex` |
106
116
 
107
117
  ### Manipulation
108
118
 
109
119
  | Function | Description |
110
120
  |---|---|
111
- | `lighten(color, amount)` | Increase lightness by `amount` (0–100) |
112
- | `darken(color, amount)` | Decrease lightness by `amount` (0–100) |
113
- | `saturate(color, amount)` | Increase saturation by `amount` (0–100) |
114
- | `desaturate(color, amount)` | Decrease saturation by `amount` (0–100) |
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) |
115
125
  | `setAlpha(color, alpha)` | Returns `rgba(...)` with the given alpha (0–1) |
116
126
  | `getAlpha(color)` | Returns the alpha channel value (0–1) |
117
127
  | `invertColor(color)` | Inverts RGB channels |
118
128
  | `grayscale(color)` | Converts to grayscale using ITU-R BT.709 weights |
119
129
  | `rotateHue(hex, degrees)` | Rotates hue by degrees (supports negative values) |
120
130
  | `adjustHexBrightness(hex, offsetPercent)` | Lightens (positive) or darkens (negative) by percentage |
121
- | `mixColors(c1, c2, t, opts?)` | Linearly interpolates between two colors. `t` = 0–1. Options: `mode: 'rgb'|'hsl'`, `format: 'hex'|'rgb'|'rgba'|'hsl'` |
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'`) |
122
140
 
123
141
  ### Color Harmonies
124
142
 
125
143
  | Function | Description |
126
144
  |---|---|
127
- | `complement(color)` | Returns the complementary color (180° rotation) |
128
- | `triadic(color)` | Returns 3 colors evenly spaced 120° apart |
129
- | `analogous(color, angle?)` | Returns 3 neighboring colors (default ±30°) |
130
- | `splitComplementary(color)` | Returns the base + two colors at 150° and 210° |
131
- | `tetradic(color)` | Returns 4 colors evenly spaced 90° apart |
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 |
132
150
 
133
151
  ### Palette Generation
134
152
 
135
153
  | Function | Description |
136
154
  |---|---|
137
- | `colorShades(color, steps?)` | Generates a light-to-dark scale (default 9 steps) |
138
- | `monochromatic(color, steps?)` | Generates shades by varying saturation (default 5 steps) |
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`) |
139
160
 
140
161
  ### Accessibility (WCAG)
141
162
 
@@ -143,18 +164,52 @@ const { normalizeColor, mixColors } = require('color-value-tools');
143
164
  |---|---|
144
165
  | `relativeLuminance(color)` | WCAG relative luminance (0–1) |
145
166
  | `contrastRatio(c1, c2)` | WCAG contrast ratio (1–21) |
146
- | `wcagLevel(fg, bg)` | Returns `'AAA'`, `'AA'`, `'AA-large'`, or `'fail'` |
167
+ | `wcagLevel(fg, bg)` | Returns `'AAA'` \| `'AA'` \| `'AA-large'` \| `'fail'` |
147
168
  | `bestTextColor(bg)` | Returns `'#000000'` or `'#ffffff'` for best contrast on `bg` |
148
- | `bestContrastColor(bg, candidates)` | Picks the most readable color from `candidates` |
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}` |
149
172
  | `isDark(color, threshold?)` | `true` if luminance is below threshold (default 0.5) |
150
173
  | `isLight(color, threshold?)` | Inverse of `isDark` |
151
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
+
152
186
  ### Formatting
153
187
 
154
188
  | Function | Description |
155
189
  |---|---|
156
190
  | `toHslString(h, s, l, alpha?)` | Formats as `hsl(...)` or `hsla(...)` |
157
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 |
158
213
 
159
214
  ### Utilities
160
215
 
@@ -166,6 +221,115 @@ const { normalizeColor, mixColors } = require('color-value-tools');
166
221
 
167
222
  ---
168
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
+
169
333
  ## Author
170
334
 
171
335
  Danil Lisin Vladimirovich aka Macrulez
@@ -1,10 +1,54 @@
1
- export type ColorType = 'hex' | 'css-var' | 'rgb' | 'hsl' | 'named' | 'unknown';
1
+ export type ColorType = 'hex' | 'css-var' | 'rgb' | 'hsl' | 'named' | 'oklch' | 'color' | 'unknown';
2
2
  export declare function isCssVariable(value: string): boolean;
3
3
  export declare function isHexColor(value: string): boolean;
4
+ export declare function isOklchColor(value: string): boolean;
5
+ export declare function isColorFunction(value: string): boolean;
4
6
  export declare function isRgbColor(value: string): boolean;
5
7
  export declare function isHslColor(value: string): boolean;
6
8
  export declare function getColorType(value: string): ColorType;
7
9
  export declare function extractCssVariableName(value: string): string;
10
+ export declare function parseCssVar(value: string): {
11
+ variableName: string;
12
+ fallback?: string;
13
+ } | null;
14
+ export declare function parseOklchString(str: string): {
15
+ L: number;
16
+ C: number;
17
+ H: number;
18
+ alpha: number;
19
+ } | null;
20
+ export declare function parseColorFn(str: string): {
21
+ space: string;
22
+ r: number;
23
+ g: number;
24
+ b: number;
25
+ alpha: number;
26
+ } | null;
27
+ export declare function rgbToDisplayP3(rgb: {
28
+ r: number;
29
+ g: number;
30
+ b: number;
31
+ }): {
32
+ r: number;
33
+ g: number;
34
+ b: number;
35
+ };
36
+ export declare function displayP3ToRgb(p3: {
37
+ r: number;
38
+ g: number;
39
+ b: number;
40
+ }): {
41
+ r: number;
42
+ g: number;
43
+ b: number;
44
+ };
45
+ export declare function toDisplayP3Hex(color: string): string;
46
+ export declare function shortHexToRgba(hex: string): {
47
+ r: number;
48
+ g: number;
49
+ b: number;
50
+ a: number;
51
+ } | null;
8
52
  export declare function normalizeHex(hex: string): string;
9
53
  export declare function hexToRgb(hex: string): [number, number, number];
10
54
  export declare function hexToRgba(hex: string, opacity?: number): string;
@@ -153,8 +197,9 @@ export declare function tetradic(color: string): [string, string, string, string
153
197
  export declare function colorShades(color: string, steps?: number): string[];
154
198
  export declare function monochromatic(color: string, steps?: number): string[];
155
199
  export declare function mixColors(c1: string, c2: string, t: number, opts?: {
156
- mode?: 'rgb' | 'hsl';
200
+ mode?: 'rgb' | 'hsl' | 'lab' | 'lch' | 'oklab' | 'oklch';
157
201
  format?: 'hex' | 'rgb' | 'rgba' | 'hsl';
202
+ hueInterpolation?: 'shorter' | 'longer' | 'increasing' | 'decreasing';
158
203
  }): string;
159
204
  export declare function relativeLuminance(color: string): number;
160
205
  export declare function contrastRatio(a: string, b: string): number;
@@ -281,3 +326,72 @@ export declare function randomColor(options?: {
281
326
  lRange?: [number, number];
282
327
  }): string;
283
328
  export declare function toNearestNamedColor(color: string): string;
329
+ export declare function toOklchString(color: string, alpha?: number): string;
330
+ export declare function toColorP3String(color: string, alpha?: number): string;
331
+ export declare function interpolateColors(color1: string, color2: string, steps: number, options?: {
332
+ space?: 'rgb' | 'hsl' | 'lab' | 'lch' | 'oklab' | 'oklch';
333
+ format?: 'hex' | 'rgb' | 'rgba' | 'hsl';
334
+ hueInterpolation?: 'shorter' | 'longer' | 'increasing' | 'decreasing';
335
+ }): string[];
336
+ export declare function createColorScale(anchors: string[] | Array<{
337
+ color: string;
338
+ position?: number;
339
+ }>, steps: number, options?: {
340
+ space?: 'rgb' | 'hsl' | 'oklab' | 'oklch';
341
+ format?: 'hex' | 'rgb' | 'hsl';
342
+ }): string[];
343
+ export declare function midpointColor(color1: string, color2: string, options?: {
344
+ space?: 'lab' | 'lch' | 'oklab' | 'oklch';
345
+ }): string;
346
+ export declare function tints(color: string, steps?: number): string[];
347
+ export declare function shades(color: string, steps?: number): string[];
348
+ export declare function tones(color: string, steps?: number, gray?: string): string[];
349
+ export type ColorBlindnessType = 'protanopia' | 'deuteranopia' | 'tritanopia';
350
+ export declare function simulateProtanopia(color: string): string;
351
+ export declare function simulateDeuteranopia(color: string): string;
352
+ export declare function simulateTritanopia(color: string): string;
353
+ export declare function simulateColorBlindness(color: string, type: ColorBlindnessType): string;
354
+ export type BackgroundSpec = string | {
355
+ type: 'semi-transparent';
356
+ color: string;
357
+ underlay?: string;
358
+ } | {
359
+ type: 'gradient';
360
+ stops: string[];
361
+ };
362
+ export declare function isReadableOnBackground(textColor: string, background: BackgroundSpec, options?: {
363
+ level?: 'AA' | 'AAA';
364
+ largeText?: boolean;
365
+ }): {
366
+ readable: boolean;
367
+ minContrastRatio: number;
368
+ wcagLevel: WcagLevel;
369
+ };
370
+ export interface PaletteScore {
371
+ palette: string[];
372
+ minContrastRatio: number;
373
+ avgContrastRatio: number;
374
+ }
375
+ export declare function bestContrastPalette(background: string, palettes: string[][], options?: {
376
+ weights?: number[];
377
+ }): PaletteScore & {
378
+ paletteIndex: number;
379
+ };
380
+ export declare function clearColorCache(): void;
381
+ export declare function getCacheStats(): {
382
+ size: number;
383
+ hits: number;
384
+ };
385
+ export declare function enableCache(): void;
386
+ export declare function disableCache(): void;
387
+ export declare function normalizeColorCached(input: string): ReturnType<typeof normalizeColor>;
388
+ export declare function generateGradientColors(start: string, end: string, steps: number, options?: {
389
+ mode?: 'rgb' | 'hsl' | 'oklab' | 'oklch';
390
+ format?: 'hex' | 'rgb' | 'hsl';
391
+ }): Generator<string>;
392
+ export declare function generateTints(color: string, steps: number, options?: {
393
+ format?: 'hex' | 'rgb' | 'hsl';
394
+ }): Generator<string>;
395
+ export declare function generateShades(color: string, steps: number, options?: {
396
+ format?: 'hex' | 'rgb' | 'hsl';
397
+ }): Generator<string>;