@readytools/colors
v1.0.7
Published
Modern zero-dependency color conversion, contrast checking, and palette generation for frontend designers by ReadyTools.
Maintainers
Readme
@readytools/colors
Lightweight color conversion, palette generation, and accessibility utilities.
Full documentation: https://www.readytools.co/docs/colors/
- ESM-only package (
"type": "module"). Works great in Next.js, Vite, etc. - Zero dependencies.
- Focused on real frontend needs: conversions, WCAG contrast, palette builders, CSS helpers.
📦 Install
npm install @readytools/colorsImport (ESM):
import {
// conversions
hexToRgb, rgbToHex, rgbToHsl, hslToRgb, hexToCmyk,
// accessibility
luminance, contrastRatio, isAccessible, getContrastColor, ensureContrast,
// adjustments
lighten, darken, saturate, desaturate, adjustHue, invert, mix,
// generators
generateShades, generateTints, generateTones, generateHueVariants, scale, shadeScale,
// palettes
complementary, analogous, triadic, tetradic,
// parsing / validation / formats
isValidHex, normalizeHex, parseColor, hexAToRgba, rgbaToHexA, toCSS,
// Lab & ΔE
rgbToXyz, xyzToLab, rgbToLab, labToRgb, deltaE76
} from "@readytools/colors";If you need CommonJS
require()support, consider transpiling your app or bundling ESM. This lib is ESM-only by design.
🚀 Quick start
const rgb = hexToRgb("#ff6600"); // { r: 255, g: 102, b: 0 }
const hex = rgbToHex(rgb.r, rgb.g, rgb.b); // "#ff6600"
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); // { h: 24, s: 100, l: 50 }
const rgb2 = hslToRgb(hsl.h, hsl.s, hsl.l); // { r: 255, g: 102, b: 0 }
const bg = "#ff6600";
const text = getContrastColor(bg); // "#000000"
const css = toCSS(bg, "background-color", "hsl");
// "background-color: hsl(24 100% 50%);"📚 API Reference
Conversions
hexToRgb(hex: string): { r: number, g: number, b: number }
Supports
#rgband#rrggbb.Example:
hexToRgb("#0af"); // { r: 0, g: 170, b: 255 } hexToRgb("#00aaff"); // { r: 0, g: 170, b: 255 }
rgbToHex(r: number, g: number, b: number): string
Clamps 0–255 and pads with zeros.
Example:
rgbToHex(0,170,255); // "#00aaff"
rgbToHsl(r: number, g: number, b: number): { h: number, s: number, l: number }
Returns H in 0–360, S/L in 0–100.
rgbToHsl(255,102,0); // { h: 24, s: 100, l: 50 }
hslToRgb(h: number, s: number, l: number): { r: number, g: number, b: number }
hslToRgb(24,100,50); // { r: 255, g: 102, b: 0 }hexToCmyk(hex: string): { c: number, m: number, y: number, k: number }
Returns percentages 0–100.
hexToCmyk("#ff6600"); // { c:0, m:60, y:100, k:0 }
Alpha hex & RGBA
hexAToRgba(hexA: string): { r: number, g: number, b: number, a: number }Supports#RGBA/#RRGGBBAA.rgbaToHexA(r,g,b,a=1): string→#rrggbbaa
hexAToRgba("#00aaffcc"); // { r:0, g:170, b:255, a:0.8 }
rgbaToHexA(0,170,255,0.8); // "#00aaffcc"Accessibility & Contrast
luminance(hex: string): number
WCAG relative luminance (0–1).
luminance("#000"); // 0 luminance("#fff"); // 1
contrastRatio(a: string, b: string): number
WCAG contrast ratio (e.g.
4.53).contrastRatio("#ff6600", "#ffffff"); // → ~4.4
isAccessible(fg: string, bg: string, level = "AA", size = "normal"): boolean
level:"AA"or"AAA"size:"normal"or"large"(≥18pt or bold ≥14pt)isAccessible("#000","#fff","AA","normal"); // true
getContrastColor(bg: string): "#000000" | "#ffffff"
- Chooses black or white text for best contrast on the background.
ensureContrast(fg: string, bg: string, desired = 4.5, direction = "auto"): string
Adjusts lightness of
fgto meet the target ratio vsbg.direction:"auto" | "lighter" | "darker"ensureContrast("#888888", "#ffffff", 7); // returns a lighter grey meeting AAA
Color Adjustments
lighten(hex: string, percent: number): string
darken(hex: string, percent: number): string
Adjust by % of HSL lightness (clamped 0–100).
lighten("#ff6600", 15); // lighter orange darken("#ff6600", 20); // darker orange
saturate(hex: string, percent: number): string
desaturate(hex: string, percent: number): string
Adjust by % of HSL saturation.
desaturate("#ff6600", 30); // more muted
adjustHue(hex: string, degrees: number): string
Rotate hue in degrees (can be negative).
adjustHue("#ff6600", 180); // complementary
invert(hex: string): string
Simple channel inversion (RGB).
invert("#112233"); // "#eeddcc"
mix(aHex: string, bHex: string, weight = 0.5): string
Linear mix in RGB space,
weight0..1.mix("#ff6600", "#0066ff", 0.35);
Generators
generateShades(hex: string, count = 10, includeBase = true): string[]
- Darker variants (reduce lightness).
generateTints(hex: string, count = 10, includeBase = true): string[]
- Lighter variants (increase lightness).
generateTones(hex: string, count = 10, includeBase = true): string[]
- Less saturated variants (reduce saturation).
generateHueVariants(hex: string, steps = 12, includeBase = true): string[]
generateShadeScale(hex: string, options): string[]
Lab-based scale generator — creates perceptually even lightness ramps.
Options:
steps(number, default 10) – number of colors to generatedirection("darker" | "lighter" | "both", default "darker") – ramp directionincludeBase(boolean, default true) – whether to include the seed colorminL(number, default 8) – minimum lightness (Lab L)maxL(number, default 98) – maximum lightness (Lab L)easing(function, optional) – easing function(t)=>tto skew the distribution
Example:
generateShadeScale("#ff6600", { steps: 5, direction: "darker" }); // ["#ff6600", "#cc5200", "#993d00", "#662900", "#331400"] generateShadeScale("#ff6600", { steps: 7, direction: "both", includeBase: true }); // ["#331400","#662900","#993d00","#ff6600","#ff8533","#ffad66","#ffd6b3"]Evenly spaced hues around the wheel.
generateShades("#ff6600", 5);
// ["#ff6600", "#e65c00", "#cc5200", "#b34700", "#993d00", "#803300"]scale(startHex: string, endHex: string, steps = 5): string[]
Create a linear color ramp between two colors.
scale("#ff6600", "#0066ff", 7);
Palettes
complementary(hex: string): [string, string]
analogous(hex: string, spread = 30): [string, string, string]
triadic(hex: string): [string, string, string]
tetradic(hex: string): [string, string, string, string]
complementary("#ff6600"); // ["#ff6600", "#00ffff"]
analogous("#ff6600", 25); // ["#ff3300", "#ff6600", "#ff9900"]Parsing / Validation / Helpers
isValidHex(str: string): boolean
- Checks
#rgbor#rrggbb.
normalizeHex(str: string): string
Ensures
#rrggbb(expands#rgband lowercases).normalizeHex("0AF"); // "#00aaff"
parseColor(input: string): { format: "hex" | "rgb" | "hsl", value: any }
Parses
#hex,rgb(...),rgba(...),hsl(...),hsla(...).parseColor("rgba(0,170,255,0.8)"); // { format: "rgb", value: { r:0, g:170, b:255, a:0.8 } }
toCSS(hex: string, property = "color", format: "hex" | "rgb" | "hsl"): string
Returns a ready CSS declaration.
toCSS("#ff6600", "background-color", "rgb"); // "background-color: rgb(255, 102, 0);"
Lab & ΔE (Color Difference)
rgbToXyz(r,g,b): { X: number, Y: number, Z: number }
xyzToLab(X,Y,Z): { L: number, a: number, b: number }
rgbToLab(r,g,b): { L: number, a: number, b: number }
labToRgb(L,a,b): { r: number, g: number, b: number }
deltaE76(hexA: string, hexB: string): number
CIE76 ΔE (Euclidean in Lab). Lower = more similar.
deltaE76("#ff6600", "#ff6a00"); // small number
🧪 Examples
// Choose legible text color on dynamic backgrounds
const bg = "#00aaff";
const text = getContrastColor(bg); // "#000000"
// Force AAA contrast vs white
const chip = ensureContrast("#888888", "#ffffff", 7);
// Build a brand ramp
const ramp = scale("#ff6600", "#0066ff", 7);
// Generate a UI palette around a seed color
const [a, seed, b] = analogous("#6c5ce7", 20);
const shades = generateShades(seed, 6);⚙️ Notes
- Input normalization: Where helpful, functions clamp values to safe ranges (e.g.,
rgbToHexclamps 0–255, adjustors clamp HSL). - Color spaces: Adjustors operate via HSL for intuitive results (lighten/darken/saturate/desaturate).
- Performance: All utilities are tiny and synchronous; safe to run in render code for simple UIs.
🔧 Type Hints (optional)
If you use TypeScript, you can add your own ambient types or generate .d.ts. Quick minimal sample:
export function hexToRgb(hex: string): { r: number; g: number; b: number };
export function rgbToHex(r: number, g: number, b: number): string;
export function rgbToHsl(r: number, g: number, b: number): { h: number; s: number; l: number };
export function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number };
// ...and so on for the rest📄 License
MIT © ReadyTools
