@colordx/core
@colordx/core is a zero-dependency JavaScript and TypeScript library for parsing, converting, comparing, and changing colors. Its core handles hex, RGB, HSL, OKLab, and OKLCH, including explicit sRGB gamut checks and mapping. Optional subpath plugins add named colors, accessibility checks, mixing and palettes, minification, CIE Lab/LCH, Display-P3, Rec.2020, A98 RGB, ProPhoto RGB, CMYK, HSV, and HWB. Instances are immutable, and low-allocation functions cover canvas and visualization hot loops.
An impressive modern color engine for OKLCH, gamut-aware design tools, and high-volume conversion. For routine hex and RGB work, its fast-moving majors and plugin-wide surface are more machinery than most teams need; colord is the calmer default.
Use it if
- You need first-class OKLCH and OKLab input, output, manipulation, and sRGB gamut mapping rather than treating them as conversion afterthoughts
- You want one typed color API that can opt into Display-P3, Rec.2020, A98 RGB, ProPhoto RGB, Lab, LCH, accessibility, palettes, and minification through subpath plugins
- You process color in canvas, charts, pickers, or image-like loops and can use the documented *Into functions to reuse output buffers
- You need zero runtime dependencies and dual ESM/CommonJS exports while keeping less common color spaces out of the base setup
- You want a long-settled API: npm shows versions 2.0.0, 3.0.0, 4.0.0, and 5.0.0 published between March 26 and March 29, 2026, so the project crossed four majors in four days before reaching 5.5.0
- You only need hex and RGB parsing or a few lighten calls: colord has a smaller conceptual surface and a more mature plugin model, while this README spans wide gamuts, matrices, zero-allocation variants, and global extension registration
- You expect all CSS Color 4 and 5 syntax: the roadmap still lists color-mix() evaluation and relative color syntax as future work, so browser-valid expressions are not automatically library-valid inputs
- You cannot choose a gamut policy: direct OKLCH to hex or RGB clips in linear sRGB and may shift hue, while mapSrgb reduces chroma to preserve hue and lightness; the README shows visibly different output for the two choices
- You want every parser available without setup: CSS names, Display-P3, Rec.2020, Lab, LCH, HWB, HSV, CMYK, accessibility, mixing, and minification require importing and registering plugins
- You assume mix means perceptual interpolation: the README states mix(), tints(), shades(), and tones() use sRGB; perceptually uniform output requires mixOklab or the Lab plugin
- You are implementing regulated accessibility decisions without independent review: the a11y plugin provides WCAG 2.x and APCA helpers, but APCA is described as a projected WCAG 3 replacement and thresholds still depend on text size and policy context
Setup reality
The base install has no dependencies, native code, credentials, or configuration file. Import colordx from @colordx/core and it can immediately parse hex, RGB, HSL, OKLab, and OKLCH. Everything else requires deliberate plugin registration. Import extend plus the plugin's default export, call extend([plugin]) once in a bootstrap module, and ensure that module runs before code parses that color space or calls augmented methods. Registration changes the shared Colordx surface, so scattered feature modules that register plugins as side effects make tests and bundles harder to reason about. Import plugin paths directly so a bundler can omit unused code. The package publishes both ESM and CommonJS plus matching declarations. Its unit conventions are easy to mix up: RGB objects use 0 to 255 channels, HSL saturation and lightness use 0 to 100, OKLCH lightness uses 0 to 1, alpha uses 0 to 1, and wide-gamut RGB plugin channels use 0 to 1. CIE Lab and OKLab share l/a/b-shaped objects, while LCH and OKLCH share l/c/h, so plugin inputs need the documented colorSpace discriminant. Parsing failure does not replace application validation; check isValid() before conversion when input is user-controlled. Objects are immutable, so every manipulation returns a new instance and ignoring the result does nothing. Out-of-gamut OKLCH and OKLab values remain stored unclamped, but toHex and toRgbString clip at output to match browser rendering. Use mapSrgb when authoring palettes that should preserve hue, and clampSrgb when you want an object describing clipped screen output. Display-P3 and other wide-gamut strings also depend on actual browser and display support. For tight loops, the *Into variants require caller-owned output arrays; functions producing both linear and gamma output require two distinct buffers. Finally, version pinning deserves attention: the package is active and typed, but its March 2026 history contains several rapid major releases, so read changelogs before relaxing a major range.
Patterns
Validate a user-supplied colorvalidate-color-input
import { colordx } from '@colordx/core';
const color = colordx(input);
if (!color.isValid()) {
throw new TypeError('Unsupported color');
}
const normalized = color.toHex();
Validate before conversion when input is untrusted. Browser-valid CSS Color 4 or 5 syntax may still be outside the library's current parsers.
Convert one color to common formatsconvert-color-formats
const color = colordx('#3d7a9f');
color.toRgb(); // { r, g, b, alpha }
color.toHslString(); // CSS Color 4 space syntax
color.toOklch(); // { l, c, h, alpha }
color.toNumber(); // integer form
Conversion methods apply format-specific default precision. Pass a precision argument when serialized decimals form part of a stable output contract.
Emit comma-separated legacy RGBwrite-legacy-rgb
const modern = colordx({ r: 255, g: 0, b: 0, alpha: 0.5 }).toRgbString();
const legacy = colordx({ r: 255, g: 0, b: 0, alpha: 0.5 }).toRgbString({ legacy: true });
// modern: rgb(255 0 0 / 0.5)
// legacy: rgba(255, 0, 0, 0.5)
Legacy output switches to rgba() when alpha is below one. Use it only for consumers that cannot accept current CSS space and slash syntax.
Chain immutable color adjustmentschain-immutable-changes
const base = colordx('#3d7a9f');
const variant = base
.lighten(0.1)
.saturate(0.05)
.rotate(20)
.alpha(0.8);
console.log(base.toHex(), variant.toHex());
Every manipulation returns a new Colordx instance. Calling base.lighten(0.1) without storing or chaining the result leaves base unchanged.
Choose absolute or relative lighteninglighten-relatively
const color = colordx('#1a0000');
const absolute = color.lighten(0.1);
const relative = color.lighten(0.1, { relative: true });
Absolute mode adds ten percentage points. Relative mode adds ten percent of the current lightness, which produces a much smaller change on dark colors.
Map an OKLCH color into sRGBmap-srgb-gamut
import { colordx, inGamutSrgb } from '@colordx/core';
const authored = colordx('oklch(0.5 0.4 180)');
if (!inGamutSrgb(authored.toOklchString())) {
const mapped = authored.mapSrgb();
console.log(mapped.toHex());
}
toHex alone clips channels and may move hue. mapSrgb reduces chroma using the documented CSS Color 4 strategy to preserve lightness and hue more closely.
Register and serialize Display-P3enable-display-p3
import { colordx, extend } from '@colordx/core';
import p3, { inGamutP3 } from '@colordx/core/plugins/p3';
extend([p3]);
const color = colordx('color(display-p3 0.92 0.2 0.14)');
console.log(color.toP3String(), inGamutP3(color.toOklchString()));
Register the plugin once before parsing P3 strings or calling instance P3 methods. A valid P3 value can still be clipped by a display that only supports sRGB.
Mix colors in Oklabmix-perceptually
import { colordx, extend } from '@colordx/core';
import mix from '@colordx/core/plugins/mix';
extend([mix]);
const midpoint = colordx('#000').mixOklab('#fff', 0.5).toHex();
The plain mix method uses sRGB. Use mixOklab for perceptual interpolation; tints, shades, and tones still use the plugin's sRGB mix behavior.
Check WCAG 2.x text contrastcheck-text-contrast
import { colordx, extend } from '@colordx/core';
import a11y from '@colordx/core/plugins/a11y';
extend([a11y]);
const text = colordx('#333');
const passesAA = text.isReadable('#fff');
const score = text.readableScore('#fff');
The default is AA normal text. Pass level and size options for other policies, and evaluate non-color requirements separately.
Enable CSS named colorsparse-named-colors
import { colordx, extend } from '@colordx/core';
import names from '@colordx/core/plugins/names';
extend([names]);
colordx('rebeccapurple').toHex(); // #663399
colordx('#ff0000').toName(); // red
Color names are not part of the base parser. toName returns undefined when no exact CSS name exists unless closest matching is requested.
Choose the shortest CSS representationminify-css-color
import { colordx, extend } from '@colordx/core';
import minify from '@colordx/core/plugins/minify';
import names from '@colordx/core/plugins/names';
extend([minify, names]);
const shortest = colordx('#ff0000').minify({ name: true }); // red
The name option depends on the names plugin. The minifier compares valid candidates and preserves precision rather than blindly rounding HSL.
Avoid allocations in a pixel loopreuse-conversion-buffer
import { oklchToRgbChannelsInto } from '@colordx/core';
const rgb = new Float64Array(3);
for (const [l, c, h] of pixels) {
oklchToRgbChannelsInto(rgb, l, c, h);
output.push(
Math.max(0, Math.min(255, Math.round(rgb[0] * 255))),
Math.max(0, Math.min(255, Math.round(rgb[1] * 255))),
Math.max(0, Math.min(255, Math.round(rgb[2] * 255))),
);
}
Reuse one Float64Array only after profiling shows allocation pressure. Channel helpers may return out-of-gamut values, so clamp before byte conversion.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| colord | npm | Choose it for a compact, mature core with familiar RGB/HSL manipulation and opt-in plugins |
| culori | npm | Choose it when broad color-science coverage, interpolation modes, filters, and functional composition matter more than a chain API |
| chroma-js | npm | Choose it for established data-visualization scales, domain mapping, bezier palettes, and a long-used API |
| tinycolor2 | npm | Choose it for legacy projects that only need common web colors and value compatibility over modern wide-gamut support |