@nextcss/color-tools
v2.0.0
Published
Performance optimized useful tools for working with colors.
Maintainers
Readme
Color Tools
Performance optimized useful tools for working with colors. This package is a module of the nextcss project. It provides a comprehensive set of color conversion and manipulation utilities for both browser and Node.js environments with full TypeScript support.
Table of Contents
General
Installation ▪ Quick Start ▪ Compatibility ▪ Benchmarks
Conversion Functions
HEX ▪ RGB ▪ HSL ▪ HWB ▪ OKLAB ▪ CMYK
Additional Functions
Color Shift ▪ Tone Map ▪ Brightness ▪ Colorify ▪ Random Colors
Other
Sponsoring
If you find this project useful, please consider sponsoring me by:
Donate via GitHub ▪ Donate via PayPal
Give the repo a Star ▪ Follow me on GitHub ▪ Share the project on X
Installation
yarn add -D @nextcss/color-toolsnpm i -D @nextcss/color-toolsQuick Start
import { hsl2hex, hslColorShift, toneMap, brightness } from '@nextcss/color-tools';
// Convert HSL to HEX
const hex = hsl2hex([207, 90, 54]);
// Lighten a color by 20%
const lightened = hslColorShift(hex, 20);
// Generate a full tone scale from the lightened color
const scale = toneMap(lightened);
// Check perceived brightness for contrast decisions
const brightnessValue = brightness(lightened);Compatibility
This package works in both browser and Node.js environments. It includes ESM and CommonJS builds, so both import and require statements work everywhere. Full TypeScript support is included.
Benchmarks
Environment: Benchmarks were run on an Intel Core i5-12600K, Node.js v22.16.0, using Benny in a single-threaded environment. Results are measured in operations per second (ops/s) - higher is better.
Baseline: nextcss vs. colord (ops/s)
| Operation | colord | nextcss | Δ | | ------------- | --------- | ---------- | ------ | | HEX → RGB | 5,899,814 | 16,842,215 | +185% | | HEX → HSL | 5,819,305 | 13,408,262 | +130% | | RGB → HEX | 2,938,363 | 62,153,898 | +2016% | | RGB → HSL | 3,489,380 | 57,524,837 | +1549% |
Note: colord is used as a baseline reference only — it uses a chainable wrapper architecture with additional overhead, so numbers are not a direct apples-to-apples comparison, but give a familiar point of reference.
Internal conversion throughput (ops/s)
| From \ To | HEX | RGB | HSL | HWB | OKLAB | CMYK | | ---------- | ----- | ----- | ----- | ----- | ----- | ----- | | HEX | — | 17.1M | 13.5M | 13.8M | 4.8M | 13.4M | | RGB | 61.3M | — | 57.0M | 54.7M | 5.1M | 49.6M | | HSL | 11.0M | 16.2M | — | 13.5M | 3.9M | 12.4M | | HWB | 32.1M | 69.5M | 32.7M | — | 4.9M | 29.9M | | OKLAB | 5.8M | 7.7M | 6.2M | 6.1M | — | 6.1M | | CMYK | 15.5M | 77.7M | 37.3M | 39.9M | 5.0M | — |
Note: OKLAB conversions are intentionally slower — they involve perceptually uniform color math with significantly more CPU-intensive calculations (non-linear gamma expansion, matrix transforms) compared to geometric color space conversions like HSL or HWB.
Other operations
| Operation | Fastest | ops/s | Slowest | ops/s | | ----------------------- | ------- | ---------- | ------- | --------- | | Random color generation | HSL | 94,094,590 | OKLAB | 3,559,931 | | Color shifting | RGB | 7,915,344 | OKLAB | 2,209,869 | | Tone mapping | RGB | 200,815 | OKLAB | 93,504 |
Note: Tone mapping operates on a fundamentally different scale — it internally uses color shifting, adding cumulative overhead per step — sub-200K ops/s reflects this layered computation rather than a single conversion.
Conversion Functions
The library provides comprehensive conversion functions between HEX, RGB, HSL, HWB, OKLab, and CMYK color spaces.
HEX Conversions
Convert from HEX to other color spaces.
Syntax
hex2rgb(hex: string): [red, green, blue, alpha?] | undefined
hex2hsl(hex: string): [hue, saturation, lightness, alpha?] | undefined
hex2hwb(hex: string): [hue, whiteness, blackness, alpha?] | undefined
hex2oklab(hex: string): [lightness, a, b, alpha?] | undefined
hex2cmyk(hex: string): [cyan, magenta, yellow, black, alpha?] | undefinedReturn values:
hex2rgb:[red (0-255), green (0-255), blue (0-255)]or withalpha (0-100)hex2hsl:[hue (0-360), saturation (0-100), lightness (0-100)]or withalpha (0-100)hex2hwb:[hue (0-360), whiteness (0-100), blackness (0-100)]or withalpha (0-100)hex2oklab:[lightness (0-1), a (-0.4 to 0.4), b (-0.4 to 0.4)]or withalpha (0-100)hex2cmyk:[cyan (0-100), magenta (0-100), yellow (0-100), black (0-100)]or withalpha (0-100)
Example
import { hex2rgb, hex2hsl, hex2hwb, hex2oklab, hex2cmyk } from '@nextcss/color-tools';
hex2rgb('#eee'); // [238, 238, 238]
hex2rgb('#2196f3bf'); // [33, 150, 243, 75]
hex2hsl('#2196f3'); // [207, 90, 54]
hex2hsl('#2196f3bf'); // [207, 90, 54, 75]
hex2hwb('#ff0000'); // [0, 0, 0]
hex2oklab('#269dd9'); // [0.627, 0.224, 0.125]
hex2cmyk('#ff0000'); // [0, 100, 100, 0]RGB Conversions
Convert from RGB array to other formats.
Array format: [red, green, blue, alpha?]
red,green,blue: 0–255alpha(optional): 0–100
Syntax
rgb2hex(rgb: [red, green, blue, alpha?]): string | undefined
rgb2hsl(rgb: [red, green, blue, alpha?]): [hue, saturation, lightness, alpha?] | undefined
rgb2hwb(rgb: [red, green, blue, alpha?]): [hue, whiteness, blackness, alpha?] | undefined
rgb2oklab(rgb: [red, green, blue, alpha?]): [lightness, a, b, alpha?] | undefined
rgb2cmyk(rgb: [red, green, blue, alpha?]): [cyan, magenta, yellow, black, alpha?] | undefinedReturn values:
rgb2hex: HEX color string (e.g.'#ff0000')rgb2hsl:[hue (0-360), saturation (0-100), lightness (0-100)]or withalpha (0-100)rgb2hwb:[hue (0-360), whiteness (0-100), blackness (0-100)]or withalpha (0-100)rgb2oklab:[lightness (0-1), a (-0.4 to 0.4), b (-0.4 to 0.4)]or withalpha (0-100)rgb2cmyk:[cyan (0-100), magenta (0-100), yellow (0-100), black (0-100)]or withalpha (0-100)
Example
import { rgb2hex, rgb2hsl, rgb2hwb, rgb2oklab, rgb2cmyk } from '@nextcss/color-tools';
rgb2hex([238, 238, 238]); // '#eeeeee'
rgb2hex([33, 150, 243, 75]); // '#2196f3bf'
rgb2hsl([255, 0, 0]); // [0, 100, 50]
rgb2hsl([255, 0, 0, 50]); // [0, 100, 50, 50]
rgb2hwb([255, 0, 0]); // [0, 0, 0]
rgb2oklab([255, 0, 0]); // [0.627, 0.224, 0.125]
rgb2cmyk([255, 0, 0]); // [0, 100, 100, 0]HSL Conversions
Convert from HSL array to other formats.
Array format: [hue, saturation, lightness, alpha?]
hue: 0–360 (degrees)saturation: 0–100 (%)lightness: 0–100 (%)alpha(optional): 0–100
Syntax
hsl2hex(hsl: [hue, saturation, lightness, alpha?]): string | undefined
hsl2rgb(hsl: [hue, saturation, lightness, alpha?]): [red, green, blue, alpha?] | undefined
hsl2hwb(hsl: [hue, saturation, lightness, alpha?]): [hue, whiteness, blackness, alpha?] | undefined
hsl2oklab(hsl: [hue, saturation, lightness, alpha?]): [lightness, a, b, alpha?] | undefined
hsl2cmyk(hsl: [hue, saturation, lightness, alpha?]): [cyan, magenta, yellow, black, alpha?] | undefinedReturn values:
hsl2hex: HEX color string (e.g.'#ff0000')hsl2rgb:[red (0-255), green (0-255), blue (0-255)]or withalpha (0-100)hsl2hwb:[hue (0-360), whiteness (0-100), blackness (0-100)]or withalpha (0-100)hsl2oklab:[lightness (0-1), a (-0.4 to 0.4), b (-0.4 to 0.4)]or withalpha (0-100)hsl2cmyk:[cyan (0-100), magenta (0-100), yellow (0-100), black (0-100)]or withalpha (0-100)
Example
import { hsl2hex, hsl2rgb, hsl2hwb, hsl2oklab, hsl2cmyk } from '@nextcss/color-tools';
hsl2hex([0, 100, 50]); // '#ff0000'
hsl2hex([200, 70, 50]); // '#269dd9'
hsl2rgb([0, 100, 50]); // [255, 0, 0]
hsl2rgb([0, 100, 50, 50]); // [255, 0, 0, 50]
hsl2hwb([0, 100, 50]); // [0, 0, 0]
hsl2oklab([0, 100, 50]); // [0.627, 0.224, 0.125]
hsl2cmyk([0, 100, 50]); // [0, 100, 100, 0]HWB Conversions
Convert from HWB (Hue, Whiteness, Blackness) array.
Array format: [hue, whiteness, blackness, alpha?]
hue: 0–360 (degrees)whiteness: 0–100 (%)blackness: 0–100 (%)alpha(optional): 0–100
Syntax
hwb2hex(hwb: [hue, whiteness, blackness, alpha?]): string | undefined
hwb2rgb(hwb: [hue, whiteness, blackness, alpha?]): [red, green, blue, alpha?] | undefined
hwb2hsl(hwb: [hue, whiteness, blackness, alpha?]): [hue, saturation, lightness, alpha?] | undefined
hwb2oklab(hwb: [hue, whiteness, blackness, alpha?]): [lightness, a, b, alpha?] | undefined
hwb2cmyk(hwb: [hue, whiteness, blackness, alpha?]): [cyan, magenta, yellow, black, alpha?] | undefinedReturn values:
hwb2hex: HEX color string (e.g.'#ff0000')hwb2rgb:[red (0-255), green (0-255), blue (0-255)]or withalpha (0-100)hwb2hsl:[hue (0-360), saturation (0-100), lightness (0-100)]or withalpha (0-100)hwb2oklab:[lightness (0-1), a (-0.4 to 0.4), b (-0.4 to 0.4)]or withalpha (0-100)hwb2cmyk:[cyan (0-100), magenta (0-100), yellow (0-100), black (0-100)]or withalpha (0-100)
Example
import { hwb2hex, hwb2rgb, hwb2hsl, hwb2oklab, hwb2cmyk } from '@nextcss/color-tools';
hwb2hex([0, 0, 0]); // '#ff0000'
hwb2rgb([0, 0, 0]); // [255, 0, 0]
hwb2hsl([0, 0, 0]); // [0, 100, 50]
hwb2oklab([0, 0, 0]); // [0.627, 0.224, 0.125]
hwb2cmyk([0, 0, 0]); // [0, 100, 100, 0]OKLab Conversions
Convert from OKLab (perceptual color space) array.
Array format: [lightness, a, b, alpha?]
lightness: 0–1 (perceived brightness)a: −0.4 to 0.4 (green–red axis)b: −0.4 to 0.4 (blue–yellow axis)alpha(optional): 0–100
Syntax
oklab2hex(oklab: [lightness, a, b, alpha?]): string | undefined
oklab2rgb(oklab: [lightness, a, b, alpha?]): [red, green, blue, alpha?] | undefined
oklab2hsl(oklab: [lightness, a, b, alpha?]): [hue, saturation, lightness, alpha?] | undefined
oklab2hwb(oklab: [lightness, a, b, alpha?]): [hue, whiteness, blackness, alpha?] | undefined
oklab2cmyk(oklab: [lightness, a, b, alpha?]): [cyan, magenta, yellow, black, alpha?] | undefinedReturn values:
oklab2hex: HEX color string (e.g.'#ff0000')oklab2rgb:[red (0-255), green (0-255), blue (0-255)]or withalpha (0-100)oklab2hsl:[hue (0-360), saturation (0-100), lightness (0-100)]or withalpha (0-100)oklab2hwb:[hue (0-360), whiteness (0-100), blackness (0-100)]or withalpha (0-100)oklab2cmyk:[cyan (0-100), magenta (0-100), yellow (0-100), black (0-100)]or withalpha (0-100)
Example
import { oklab2hex, oklab2rgb, oklab2hsl, oklab2hwb, oklab2cmyk } from '@nextcss/color-tools';
oklab2hex([0.627, 0.224, 0.125]); // '#ff0000'
oklab2rgb([0.627, 0.224, 0.125]); // [255, 0, 0]
oklab2hsl([0.627, 0.224, 0.125]); // [0, 100, 50]
oklab2hwb([0.627, 0.224, 0.125]); // [0, 0, 0]
oklab2cmyk([0.627, 0.224, 0.125]); // [0, 100, 100, 0]CMYK Conversions
Convert from CMYK (cyan, magenta, yellow, black) array.
Array format: [cyan, magenta, yellow, black, alpha?]
cyan: 0–100magenta: 0–100yellow: 0–100black: 0–100alpha(optional): 0–100
Syntax
cmyk2hex(cmyk: [cyan, magenta, yellow, black, alpha?]): string | undefined
cmyk2rgb(cmyk: [cyan, magenta, yellow, black, alpha?]): [red, green, blue, alpha?] | undefined
cmyk2hsl(cmyk: [cyan, magenta, yellow, black, alpha?]): [hue, saturation, lightness, alpha?] | undefined
cmyk2hwb(cmyk: [cyan, magenta, yellow, black, alpha?]): [hue, whiteness, blackness, alpha?] | undefined
cmyk2oklab(cmyk: [cyan, magenta, yellow, black, alpha?]): [lightness, a, b, alpha?] | undefinedReturn values:
cmyk2hex: HEX color string (e.g.'#ff0000')cmyk2rgb:[red (0-255), green (0-255), blue (0-255)]or withalpha (0-100)cmyk2hsl:[hue (0-360), saturation (0-100), lightness (0-100)]or withalpha (0-100)cmyk2hwb:[hue (0-360), whiteness (0-100), blackness (0-100)]or withalpha (0-100)cmyk2oklab:[lightness (0-1), a (-0.4 to 0.4), b (-0.4 to 0.4)]or withalpha (0-100)
Example
import { cmyk2hex, cmyk2rgb, cmyk2hsl, cmyk2hwb, cmyk2oklab } from '@nextcss/color-tools';
cmyk2hex([0, 100, 100, 0]); // '#ff0000'
cmyk2rgb([0, 100, 100, 0]); // [255, 0, 0]
cmyk2hsl([0, 100, 100, 0]); // [0, 100, 50]
cmyk2hwb([0, 100, 100, 0]); // [0, 0, 0]
cmyk2oklab([0, 100, 100, 0]); // [0.627, 0.224, 0.125]Additional Functions
Useful tools for color manipulation, including brightness shifting, tone mapping, and random color generation.
Color Shift
Shift the brightness of a color by a percentage. Positive values lighten, negative values darken. Four implementations available using different color spaces — choose based on perceptual needs.
RGB Color Shift
Shift using RGB color space.
Syntax
rgbColorShift(hex: string, percentage: number): string | undefinedParameters:
hex(string): Input color in HEX formatpercentage(number): Brightness shift in range -100 to 100- Positive values: lighten the color
- Negative values: darken the color
Example
import { rgbColorShift } from '@nextcss/color-tools';
rgbColorShift('#eee', 10); // '#f5f5f5' (lighter)
rgbColorShift('#eee', -10); // '#e5e5e5' (darker)HSL Color Shift
Shift using HSL color space.
Syntax
hslColorShift(hex: string, percentage: number): string | undefinedParameters:
hex(string): Input color in HEX formatpercentage(number): Lightness shift in range -100 to 100- Positive values: lighten the color
- Negative values: darken the color
Example
import { hslColorShift } from '@nextcss/color-tools';
hslColorShift('#2196f3', 20); // lighter
hslColorShift('#2196f3', -20); // darkerHWB Color Shift
Shift using HWB color space.
Syntax
hwbColorShift(hex: string, percentage: number): string | undefinedParameters:
hex(string): Input color in HEX formatpercentage(number): Brightness shift in range -100 to 100- Positive values: lighten the color
- Negative values: darken the color
Example
import { hwbColorShift } from '@nextcss/color-tools';
hwbColorShift('#2196f3', 15); // lighter
hwbColorShift('#2196f3', -15); // darkerOKLab Color Shift
Shift using OKLab (best for perceptually uniform results).
Syntax
oklabColorShift(hex: string, percentage: number): string | undefinedParameters:
hex(string): Input color in HEX formatpercentage(number): Brightness shift in range -100 to 100- Positive values: lighten the color
- Negative values: darken the color
Example
import { oklabColorShift } from '@nextcss/color-tools';
oklabColorShift('#2196f3', 15); // perceptually lighter
oklabColorShift('#2196f3', -15); // perceptually darkerTone Map
Generate a complete tonal scale from a base color. Returns an object with tone steps (50–950) mapped to HEX color strings.
Syntax
toneMap(
hex: string,
mode?: 'rgb' | 'hsl' | 'hwb' | 'oklab',
customTones?: Partial<Record<50 | 100 | 150 | 200 | 250 | 300 | 350 | 400 | 450 | 500 | 550 | 600 | 650 | 700 | 750 | 800 | 850 | 900 | 950, number>>
): Record<string, string> | undefinedParameters:
hex(string): Base color in HEX formatmode(optional): Color space for interpolation —'rgb'|'hsl'|'hwb'|'oklab'(default:'rgb')customTones(optional): Override default tone shift percentages.- Object with numeric tone step keys (any number, e.g. 50, 100, 150, ..., 950, or custom values) and color shift percentages (-100 to 100) as values.
- Each value determines how much to lighten or darken the base color for that tone step.
- If omitted, uses default tone steps (50, 100, 150, ..., 950) with default percentages:
[90, 85, 74, 62, 50, 40, 30, 20, 10, 0, -11, -23, -34, -45, -56, -68, -79, -90, -97]
Example
import { toneMap } from '@nextcss/color-tools';
// Default tone map (uses default shift percentages)
const tones = toneMap('#2196f3');
console.log(tones);
// {
// 50: '#e3f2fd', (tone 50, +90% lighter)
// 100: '#bbdefb', (tone 100, +85% lighter)
// 150: '#90caf9',
// ...
// 500: '#2196f3', (tone 500, no shift)
// ...
// 950: '#0d47a1' (tone 950, -97% darker)
// }
// Using OKLab for better perceptual uniformity
const tonesOklab = toneMap('#2196f3', 'oklab');
// Custom shift percentages for specific tones
const customTonesMap = toneMap('#2196f3', 'hsl', {
50: 95, // extra light (instead of default 90)
100: 80, // slightly less light (instead of default 85)
500: 0, // exact base color
950: -95, // extra dark (instead of default -97)
});
// Custom tone steps (not limited to standard 50, 100, 150... 950)
const customStepsMap = toneMap('#2196f3', 'oklab', {
0: 100, // lightest
10: 80,
20: 50,
30: 0, // base color at step 30
40: -50,
50: -100, // darkest
});Brightness
Calculate the perceived brightness of a color as a percentage (0–100). Useful for determining text contrast or applying adaptive styling.
Syntax
brightness(hex: string): number | undefinedExample
import { brightness } from '@nextcss/color-tools';
brightness('#000000'); // 0 (darkest)
brightness('#ffffff'); // 100 (lightest)
brightness('#269dd9'); // ~53 (neutral)
// Use for contrast decisions
if (brightness(color) < 50) {
textColor = '#ffffff'; // light text on dark bg
} else {
textColor = '#000000'; // dark text on light bg
}Colorify
Generate a consistent, deterministic color from any string input (e.g., usernames, IDs). Outputs the same color for the same input every time.
Syntax
colorify(
str: string,
saturation?: number,
lightness?: number,
alpha?: number
): string | undefinedParameters:
str(string): Input stringsaturation(optional): 0–100 (default: 50)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100 (default: 100)
Example
import { colorify } from '@nextcss/color-tools';
colorify('[email protected]'); // '#40bf79' (consistent)
colorify('[email protected]'); // '#ff6b9d' (different)
// Adjust saturation for muted tones
colorify('user123', 30); // lower saturation
// Custom lightness and alpha
colorify('dark-mode', 60, 30, 80); // dark with 80% opacity
// Same input always produces same color
colorify('alice') === colorify('alice'); // trueRandom Colors
Generate random colors in any format. All random functions accept optional saturation, lightness, and alpha parameters.
Random HEX
Syntax
randomHex(saturation?: number, lightness?: number, alpha?: number): string | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomHex } from '@nextcss/color-tools';
randomHex(); // '#7de889' (random)
randomHex(50); // with saturation
randomHex(65, 80); // with saturation + lightness
randomHex(65, 80, 50); // with alpha (50%)Random RGB
Syntax
randomRgb(saturation?: number, lightness?: number, alpha?: number): [red, green, blue, alpha?] | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomRgb } from '@nextcss/color-tools';
randomRgb(); // [232, 193, 125]
randomRgb(50); // with saturation
randomRgb(65, 80); // with saturation + lightness
randomRgb(65, 80, 50); // with alpha (50%)Random HSL
Syntax
randomHsl(saturation?: number, lightness?: number, alpha?: number): [hue, saturation, lightness, alpha?] | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomHsl } from '@nextcss/color-tools';
randomHsl(); // [294, 70, 50]
randomHsl(50); // with saturation
randomHsl(65, 80); // with saturation + lightness
randomHsl(65, 80, 50); // with alpha (50%)Random HWB
Syntax
randomHwb(saturation?: number, lightness?: number, alpha?: number): [hue, whiteness, blackness, alpha?] | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomHwb } from '@nextcss/color-tools';
randomHwb(); // [45, 10, 20]
randomHwb(60, 50, 75); // with parametersRandom OKLab
Syntax
randomOklab(saturation?: number, lightness?: number, alpha?: number): [lightness, a, b, alpha?] | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomOklab } from '@nextcss/color-tools';
randomOklab(); // [0.627, 0.224, 0.125]
randomOklab(70, 50, 90); // with parametersRandom CMYK
Syntax
randomCmyk(saturation?: number, lightness?: number, alpha?: number): [cyan, magenta, yellow, black, alpha?] | undefinedParameters:
saturation(optional): 0–100 (default: 70)lightness(optional): 0–100 (default: 50)alpha(optional): 0–100
Example
import { randomCmyk } from '@nextcss/color-tools';
randomCmyk(); // [0, 100, 100, 0]
randomCmyk(70, 50, 90); // with parametersGuidelines
See the project Code of Conduct, Contributing, and Security Policy for details.
License
MIT License © 2022 Zsolt Tövis
If you find this project useful, please consider sponsoring me by: Donate via GitHub / Donate via PayPal / Give the repo a Star / Follow me on GitHub / Share the project on X
Made with ❤️ for developers who love clean APIs and don't need a heavy library with tons of dependencies.
