Core API
Documentation for the Color class and its methods in Saturon, a JavaScript color engine.
The Color class is the core of Saturon. It provides a powerful and type-safe API for creating, manipulating, and converting colors across various models and syntaxes, from standard rgb and hsl to modern oklab and oklch. This page details the Color class structure, starting with its constructor and primary methods for instantiation. For a complete list of supported color formats, see Supported Syntaxes. For an overview of how colors are represented as JavaScript objects, see Architecture. For practical usage, see Guides.
Class Structure
The Color class is generic, parameterized by a ColorModel type that represents the color space (e.g., rgb, hsl, oklab). It stores the color's model and coordinates, ensuring type safety and validation.
export class Color<M extends ColorModel = ColorModel> {
model: M;
coords: number[];
}model: The color space identifier (e.g.,"hsl","oklab","rec2020","xyz-d65"), validated against the internalcolorModelsregistry.coords: An array of 3 or 4 numbers representing the color's channel values (e.g.,[hue, saturation, lightness]forhsl) and an optional alpha channel (defaulting to 1).
The class is designed to work with Saturon's internal architecture, where all color types resolve to ColorConverter objects in the colorTypes registry, and color models are defined in colorModels for manipulation.
Constructor
The Color constructor creates a new Color instance by directly specifying a color model and its coordinates. It's ideal for scenarios where you know the exact model and values, such as programmatically generating colors or working with specific color spaces.
Syntax
new Color<M extends ColorModel>(model: M, coords: number[] = [0, 0, 0, 0])model: A string identifying the color model (e.g.,"rgb","hsl","oklab"). Must exist in thecolorModelsregistry.coords: An array of 3 or 4 numbers representing the channel values (e.g.,[r, g, b]forrgb) and an optional alpha channel. Defaults to[0, 0, 0, 0].- Returns: A new
Colorinstance. - Throws:
- If
modelis not incolorModels. - If
coordslength is not 3 or 4.
- If
Behavior
- Validates the
modelagainst thecolorModelsregistry using a strictincheck. - Ensures the
coordsarray has exactly 3 or 4 elements. - Creates a shallow copy of
coordsto prevent external mutations. - If
coordshas 3 elements, appends an alpha value of1. - Assigns the normalized
coordsandmodelto the instance.
Example
import { Color } from "saturon";
// Create an HSL color (hue: 266ยฐ, saturation: 87%, lightness: 20%)
const color = new Color("hsl", [266, 87, 20]);
console.log(color.to("rgb")); // rgb(33 15 89)
// Create an RGB color with alpha
const colorWithAlpha = new Color("rgb", [255, 87, 51, 0.8]);
console.log(colorWithAlpha.to("hex-color")); // #ff5733ccNote on accessing coords property
-
The
coordsproperty can technically accept any JavaScriptnumbervalues โ includingNaN,Infinity, or-Infinity. When accessed directly usingcoords, these values are returned exactly as provided. For normalized and CSS-safe output, use thetoArray()method instead. It converts special numeric values according to CSS conventions:NaNโ treated as the CSS keywordnone(converted to 0)Infinityโ treated ascalc(infinity)(converted to the maximum representable value in the model)-Infinityโ treated ascalc(-infinity)(converted to the minimum representable value in the model)
-
If no
coordsare provided, Saturon initializes the color with all zero channel values. This ensures a valid color object (e.g., black forrgb, or the zero-point for other models). This is useful for placeholders, programmatically generating colors, or avoidingnull/undefinedchecks.
Static Color.from()
The Color.from() static method creates a Color instance by parsing a CSS color string. It supports the full CSS Color Module Level 5 <color> syntax, including named colors, hex, rgb, hsl, hwb, lab, lch, oklab, oklch, color-mix(), and more. The method includes specific TypeScript overloads for common formats to ensure type safety.
Syntax
static from(color: NamedColor): Color<"rgb">;
static from(color: `#${string}`): Color<"rgb">;
static from(color: `rgb(${string})` | `rgba(${string})`): Color<"rgb">;
static from(color: `hsl(${string})` | `hsla(${string})`): Color<"hsl">;
static from(color: `hwb(${string})`): Color<"hwb">;
static from(color: `lab(${string})`): Color<"lab">;
static from(color: `lch(${string})`): Color<"lch">;
static from(color: `oklab(${string})`): Color<"oklab">;
static from(color: `oklch(${string})`): Color<"oklch">;
static from<T extends ColorModel = ColorModel>(color: string): Color<T>;color: A string representing a CSS color (e.g.,"red","#ff5733","hsl(50 100% 30%)","color-mix(in hsl, red, blue)").- Returns: A new
Colorinstance in the appropriate model (e.g.,"rgb"for named colors and hex,"hsl"forhsl()). - Throws: If the input string is invalid or unsupported.
Behavior
- Cleans the input string (e.g., trims whitespace, normalizes case).
- Iterates through the
colorTypesregistry, using each type'sisValid()method to identify the color format. - If valid, parses the string using the type's
parse()method to get coordinates. - For
<color-function>types (e.g.,hsl,oklab) incolorModels, creates aColorinstance in that model with the parsed coordinates. - For other
<color>types (e.g.,hex-color,color-mix), converts coordinates to the type's bridge space (e.g.,rgb,xyz-d65) usingtoBridge()and creates aColorinstance in the bridge model. - Throws an error if no matching type is found.
Example
import { Color } from "saturon";
// Named color
const namedColor = Color.from("red");
console.log(namedColor.toString("rgb")); // rgb(255 0 0)
console.log(namedColor.model); // rgb
// Hex color
const hexColor = Color.from("#ff5733");
console.log(hexColor.to("hsl")); // hsl(11 100 60)
console.log(hexColor.coords); // [255, 87, 51, 1]
// HSL color
const hslColor = Color.from("hwb(50 10% 30%)");
console.log(hslColor.toObject()); // { h: 50, w: 10, b: 30, alpha: 1 }
console.log(hslColor.toArray); // [50, 10, 30, 1]Static Color.type()
The Color.type() static method identifies the type of a given CSS color string, returning the corresponding ColorType (e.g., "hex-color", "rgb", "color-mix") from the colorTypes registry, or undefined if unrecognized. The method supports a strict mode to ensure the string not only matches a type's syntax but also produces a valid Color instance through round-trip conversion.
Syntax
static type(color: string, strict = false): ColorType | undefinedcolor: A string representing a CSS color (e.g.,"lime","#ff5733","hsl(50 100% 30%)").strict: Optional boolean (defaultfalse). Iftrue, validates that the string can be parsed and converted to a validColorinstance in the type's bridge space.- Returns: The
ColorTypestring (e.g.,"hex-color","rgb","color-mix") if recognized, orundefinedif the string is invalid or unsupported. - Throws: Does not throw; returns
undefinedfor invalid inputs in strict mode.
Behavior
- Cleans the input string (e.g., trims whitespace, normalizes case).
- Iterates through the
colorTypesregistry, using each type'sisValid()method to check if the string matches. - If a match is found and
strictisfalse, returns the type immediately. - In
strictmode, parses the string, and attempts to create aColorinstance to ensure validity. Returnsundefinedif parsing or conversion fails. - Returns
undefinedif no type matches.
Example
import { Color } from "saturon";
// Identify basic color types
console.log(Color.type("#ff5733")); // hex-color
console.log(Color.type("red")); // named-color
console.log(Color.type("hsl(50 100% 30%)")); // hsl
console.log(Color.type("color-mix(in hsl, red, blue)")); // color-mix
// Strict mode validation
console.log(Color.type("hsl(50 100% 30%)", true)); // hsl
console.log(Color.type("hsl(invalid)", true)); // undefined
// Invalid input
console.log(Color.type("invalid")); // undefinedStatic Color.isValid()
The Color.isValid() static method checks whether a given color string is valid according to the CSS Color Module Level 5 grammar, optionally for a specific ColorType. It's a lightweight way to validate inputs before creating Color instances.
Syntax
static isValid(color: string, type?: ColorType | string): booleancolor: A string representing a CSS color (e.g.,"red","rgb(255 87 51)","color-mix(in oklch, red, blue)").type: Optional string specifying aColorType(e.g.,"rgb","hex-color","rec2020") to validate against. If omitted, checks against all types incolorTypes.- Returns:
trueif the color string is valid for the specified type (or any type iftypeis omitted),falseotherwise. - Throws: Does not throw; returns
falsefor invalid inputs.
Behavior
- If
typeis omitted, delegates toColor.from(), returningtrueif aColorinstance can be created successfully. - If
typeis specified, cleans the input string (trims and lowercases), usescolorTypes[type].isValid()to check syntax, then parses the string, and attempts to create aColorinstance. Returnstrueif the instance is valid. - Returns
falseif parsing, conversion, or instantiation fails.
Example
import { Color } from "saturon";
// Validate any color type
console.log(Color.isValid("red")); // true
console.log(Color.isValid("#ff5733")); // true
console.log(Color.isValid("hsl(50 100% 30%)")); // true
console.log(Color.isValid("invalid")); // false
// Validate specific type
console.log(Color.isValid("rgb(255 87 51)", "rgb")); // true
console.log(Color.isValid("rgb(255 87 51)", "hsl")); // false
console.log(Color.isValid("hsl(invalid)", "hsl")); // falseStatic Color.random()
The Color.random() static method generates a random Color instance, ideal for generative design, testing, or creating dynamic color palettes. It allows fine-grained control over the randomness through the RandomOptions configuration, enabling you to specify the color model, constrain channel values, apply biases, or use Gaussian distributions for controlled variation. The method leverages the colorModels registry to ensure valid component ranges and model-specific behavior.
Syntax
static random<M extends ColorModel = ColorModel>(options: RandomOptions<M> = {}): Color<M>options: An optional object configuring the random color generation:model: The color model (e.g.,"rgb","hsl","oklch"). Defaults to a random model fromcolorModelsif not specified.limits: An object specifying[min, max]ranges for each component (e.g.,{ l: [0.4, 0.6] }foroklchlightness).bias: An object mapping components to easing functions (e.g.,{ l: (x: number) => x * x }) to bias random values.base: An object setting base values for components when using Gaussian randomization (e.g.,{ h: 120 }for hue).deviation: An object specifying standard deviations for Gaussian randomization (e.g.,{ h: 10 }for hue variation).
- Returns: A new
Colorinstance with randomly generated coordinates in the specified or randomly selected model. - Throws: If an invalid component is specified in
options(e.g.,hforrgb).
Behavior
- Selects a random model from
colorModelsifoptions.modelis not provided. - Retrieves the
componentsdefinition fromcolorModels[model]and creates a set of valid component names, includingalpha. - Validates components in
limits,bias,base, anddeviationagainst the model's components, throwing an error for invalid components. - Generates random values for each component based on the model's
ComponentDefinition:- For angles (e.g., hue in
hsl), uses[0, 360]. - For percentages (e.g., saturation in
hsl), uses[0, 100]. - For custom ranges (e.g.,
rinrgb), uses the defined[min, max].
- For angles (e.g., hue in
- If
baseanddeviationare provided, uses Gaussian randomization (Box-Muller transform) for natural variation around the base value. - Applies
limitsto constrain ranges (e.g.,l: [0.4, 0.6]foroklchlightness). - Applies
biasfunctions to skew randomness (e.g., favoring lighter colors). - Clamps values to model-specific ranges and creates a new
Colorinstance with the generated coordinates.
Example
import { Color } from "saturon";
// Generate a random color (any model)
const randomColor = Color.random();
console.log(randomColor.to("rgb")); // e.g., rgb(123 45 89)
// Specify a model
const hslColor = Color.random({ model: "hsl" });
console.log(hslColor.toString()); // e.g., hsl(200 75 50)
// Constrain ranges with limits
const constrainedColor = Color.random({
model: "oklch",
limits: { l: [0.4, 0.6], c: [0, 0.2], h: [200, 240] },
});
console.log(constrainedColor.to("rgb")); // e.g., rgb(100 120 140)
// Apply bias for lighter colors
const biasedColor = Color.random({
model: "hsl",
bias: { l: (x) => x * x }, // Favors higher lightness
});
console.log(biasedColor.toString()); // e.g., hsl(150 80 75)
// Gaussian randomization around a base hue
const gaussianColor = Color.random({
model: "hsl",
base: { h: 120 },
deviation: { h: 10 },
});
console.log(gaussianColor.toString()); // e.g., hsl(115 100 50)Instance Color.to()
The to() method converts the current Color instance to a specified output format, returning a CSS color string. The method allows customization through FormattingOptions, such as legacy syntax, gamut mapping, precision, and unit inclusion, making it ideal for generating CSS-compatible strings for display, styling, or logging.
Syntax
to(type: OutputType | string, options?: FormattingOptions): stringtype: A string specifying the target color format (e.g.,"rgb","hsl","hex-color","color-mix"). Case-insensitive, but must match a type incolorTypes.options: An optional object configuring the output format:legacy: Boolean (defaultfalse). Iftrue, uses legacy syntax for formats likergb()orhsl()(e.g.,hsla(240, 100%, 50%, 0.5)).fit: String (default"clip"). Specifies the gamut mapping method ("clip","chroma-reduction","css-gamut-map") for out-of-gamut colors. Can be changed in the default configuration viaconfigure(see API: config).precision: Number,nullorundefined(defaultundefined). Sets the decimal precision for numeric values in the output string. Ifnull, uses full precision without rounding. Ifundefined, uses the type's default precision.units: Boolean (defaultfalse). Iftrue, includes the optional unit suffixes (e.g.,degfor angles,%for percentages).
- Returns: A string representing the color in the specified format.
- Throws:
- If
typeis not incolorTypes. - If
typelacks afromBridgeorformatfunction (e.g.,currentColor).
- If
Behavior
- Normalizes the
typeto lowercase for case-insensitive matching. - Retrieves the
ColorConverterfor the targettypefromcolorTypes. - If the target
typematches the instance's currentmodel, formats the current coordinates directly using the type'sformat()function. - If
typeis a color model (incolorModels), converts the instance to that model usingin()and formats the resulting coordinates. - For other types (e.g.,
hex-color,color-mix), converts to the type's bridge space usingin(), appliesfromBridge()to get native coordinates, and formats the result. - Returns the formatted string according to the specified
options.
Example
import { Color } from "saturon";
// Convert to different formats
const color = Color.from("hsl(50 100% 30%)");
console.log(color.to("rgb")); // rgb(153 128 0)
console.log(color.to("hex-color")); // #998000
// Use legacy syntax
console.log(color.to("rgb", { legacy: true })); // rgb(153, 128, 0)
// Constrain to sRGB gamut with different fit methods
const wideColor = Color.from("color(display-p3 1 0 0)");
console.log(wideColor.to("rgb", { fit: "clip" })); // rgb(255 0 0)
console.log(wideColor.to("rgb", { fit: "css-gamut-map" })); // rgb(255 0 0)
// Include units and custom precision
console.log(color.to("hsl", { units: true, precision: 2 })); // hsl(50deg 100% 30%)Instance Color.in()
The in() method converts the current Color instance to a specified color model, returning a new Color instance in that model. It's essential for manipulating colors in a specific color space (e.g., hsl for hue adjustments, oklab for perceptual uniformity) before using methods like with() or toObject(). The method uses a graph-based pathfinding algorithm to find the shortest conversion path through bridge spaces.
Syntax
in<T extends ColorModel = ColorModel>(model: T | string): Color<T>model: A string specifying the target color model (e.g.,"rgb","oklab","xyz-d65"). Case-insensitive, but must exist in thecolorModelsregistry.- Returns: A new
Colorinstance with coordinates converted to the specified model, preserving the alpha channel. - Throws:
- If the target
modelis not incolorModels. - If no conversion path exists between the current and target models.
- If no valid conversion is found between intermediate models.
- If the target
Behavior
- Normalizes the
modelstring (trims and lowercases). - If the target model matches the current model, returns a new
Colorinstance with a copy of the current coordinates for immutability. - Builds a conversion graph (cached in
cache.get("graph")) fromcolorModels, mapping models to their bridge spaces. - Uses a breadth-first search (BFS) to find the shortest path from the current model to the target model, caching the path in
cache.get("paths")for performance. - Traverses the path, applying
toBridge()orfromBridge()fromcolorModelsto convert coordinates between models via bridge spaces. - Preserves the alpha channel (defaults to 1 if undefined) in the resulting
Colorinstance.
Example
import { Color } from "saturon";
// Convert from RGB to HSL
const rgbColor = new Color("rgb", [255, 87, 51]);
const hslColor = rgbColor.in("hsl");
console.log(hslColor.toString()); // hsl(15 100 60)
console.log(hslColor.with({ l: (l) => l / 2 })); // [15, 100, 30, 1]
// Convert to OKLCH
const oklchColor = rgbColor.in("oklch");
console.log(oklchColor.toObject()); // { l: 0.627, c: 0.257, h: 29.2, alpha: 1 }
console.log(oklchColor.toArray()); // [0.627, 0.257, 29.2, 1]Instance Color.toString()
The toString() method formats the current Color instance as a CSS color string in its current model. It directly uses the format function from the colorTypes registry for the instance's model, making it a lightweight alternative to to() when no model conversion is needed. This method is ideal for quick string output in the instance's native model, with customizable formatting options like legacy syntax or precision.
Syntax
toString(options: FormattingOptions = {}): stringoptions: An optional object configuring the output format:legacy: Boolean (defaultfalse). Iftrue, uses legacy syntax for formats likergb()orhsl()(e.g.,hsla(240, 100%, 50%, 0.5)).fit: String (default"clip"). Specifies the gamut mapping method ("clip","chroma-reduction","css-gamut-map") for out-of-gamut colors. Can be changed in the default configuration viaconfigure(see API: config).precision: Number,nullorundefined(defaultundefined). Sets the decimal precision for numeric values in the output string. Ifnull, uses full precision without rounding. Ifundefined, uses the type's default precision.units: Boolean (defaultfalse). Iftrue, includes the optional unit suffixes (e.g.,degfor angles,%for percentages).
- Returns: A string representing the color in the instance's current model.
- Throws: Does not throw; assumes the model has a defined
formatfunction incolorTypes.
Behavior
- Retrieves the
formatfunction fromcolorTypes[this.model]. - Applies the
formatfunction to the instance'scoords, passing the providedoptions(e.g.,legacy,fit,precision,units). - Returns the formatted string in the current model.
Example
import { Color } from "saturon";
// Format in current model (HSL)
const hslColor = Color.from("hsl(50 100% 30%)");
console.log(hslColor.toString()); // hsl(50 100 30)
console.log(hslColor.toString({ legacy: true })); // hsl(50, 100%, 30%)
console.log(hslColor.toString({ units: true, precision: 2 })); // hsl(50deg 100% 30%)Instance Color.toObject()
The toObject() method converts the Color instance to an object representation, mapping each component (e.g., r, g, b for rgb) and the alpha channel to their numeric values. It's useful for accessing structured component values for data processing or serialization, respecting the instance's current model.
Syntax
toObject(options: GetOptions = {}): { [key in Component<M> | "alpha"]: number }options: An optional object configuring the coordinate retrieval:fit: String (default"none"). Specifies the gamut mapping method ("none","clip","chroma-reduction","css-gamut-map") for out-of-gamut colors.precision: Number,nullorundefined(defaultundefined). Sets the decimal precision for numeric values in the output string. Ifnull, uses full precision without rounding. Ifundefined, uses the type's default precision.
- Returns: An object with keys for each component in the current model and
"alpha", mapped to their numeric values. - Throws: If the current model lacks defined components in
colorModels.
Behavior
- Retrieves the
componentsdefinition fromcolorModels[model]. - Creates a
fullComponentsobject by adding analphacomponent (index: 3,value: [0, 1],precision: 3). - Calls
toArray(options)to get normalized or fitted coordinates. - Maps each component's
indexto its corresponding value in the coordinates array, includingalpha. - Returns an object with component names as keys and numeric values.
Example
import { Color } from "saturon";
const rgbColor = Color.from("rgb(92 7 151)");
console.log(rgbColor.toObject()); // { r: 92, g: 7, b: 151, alpha: 1 }Note
toObject() internally relies on toArray() to retrieve component values. This means it inherits toArray()'s
robust handling of special numeric cases, such as NaN and Infinity, ensuring consistent and reliable output.
Instance Color.toArray()
The toArray() method converts the Color instance to an array of numeric coordinates, optionally normalizing and fitting values to the model's gamut. It's useful for accessing raw coordinate values for calculations or external systems, with robust handling of edge cases like NaN or Infinity.
Syntax
toArray(options: GetOptions = {}): number[]options: An optional object configuring the coordinate retrieval:fit: String (default"none"). Specifies the gamut mapping method ("none","clip","chroma-reduction","css-gamut-map") for out-of-gamut colors.precision: Number,nullorundefined(defaultundefined). Sets the decimal precision for numeric values in the output string. Ifnull, uses full precision without rounding. Ifundefined, uses the type's default precision.
- Returns: An array of 4 numbers: the first 3 are the model's component values (e.g.,
[r, g, b]forrgb), and the last is the alpha value. - Throws: If the current model lacks defined components in
colorModels(e.g.,Model rgb does not have defined components).
Behavior
- Retrieves the
componentsdefinition fromcolorModels[model]. - Creates a
defsobject by adding analphacomponent (index: 3,value: [0, 1],precision: 3). - Normalizes the first 3 coordinates based on component definitions:
- For angles (e.g., hue in
hsl), uses[0, 360]. - For percentages (e.g., saturation in
hsl), uses[0, 100]. - For custom ranges (e.g.,
rinrgb), uses the defined[min, max]. - Handles edge cases:
NaNโ0,Infinityโmax,-Infinityโmin.
- For angles (e.g., hue in
- If
fitis not"none", applies gamut mapping to the first 3 coordinates using the internalfit()function and the model'stargetGamut. - Returns an array of 4 numbers: the normalized or fitted coordinates plus the alpha value.
Example
import { Color } from "saturon";
const rgbColor = Color.from("rgb(25 187 71)");
console.log(rgbColor.toArray()); // [55, 187, 51, 1]Instance Color.with()
The with() method creates a new Color instance with updated or replaced component values in the current model, offering flexible ways to modify colors. It supports direct component updates, functional updates for dynamic calculations, bulk updates via functions, and direct or functional coordinate array replacements. This method is ideal for adjusting specific components (e.g., changing hue in hsl) or applying transformations (e.g., desaturating a color) while preserving immutability.
Syntax
with(
values:
| Partial<{ [K in Component<M> | "alpha"]: number | ((prev: number) => number) }>
| ((components: { [K in Component<M> | "alpha"]: number }) =>
| Partial<{ [K in Component<M> | "alpha"]: number }>
| (number | undefined)[])
| (number | undefined)[]
): Color<M>values: One of the following:- Object: A partial object with component names (e.g.,
r,g,bforrgb) oralpha, mapped to numbers or updater functions (e.g.,{ r: 128 },{ r: (r) => r * 2 }). - Function: A function receiving an object of current component values and returning either a partial object of updated values or an array of new coordinates (e.g.,
({ r, g, b }) => ({ r: r * 0.5 }),({ r, g, b }) => [r, g, b, 1]). - Array: An array of 3 or 4 numbers (or
undefined) to replace the coordinates directly (e.g.,[255, 87, 51, 1]).
- Object: A partial object with component names (e.g.,
- Returns: A new
Colorinstance in the same model with updated coordinates. - Throws: If the current model lacks defined components in
colorModels.
Behavior
- Retrieves the
componentsdefinition fromcolorModels[model]and adds analphacomponent (index: 3,value: [0, 1],precision: 3). - Gets the current coordinates using
toArray()for reference. - Handles the
valuesinput based on its type:- Function: Calls the function with an object of current component values and processes the result as either a partial object or an array.
- Array: Normalizes each coordinate using model-specific ranges (e.g.,
[0, 360]for angles,[0, 100]for percentages,[min, max]for custom ranges). Handles edge cases:NaNโ0,Infinityโmax,-Infinityโmin,undefinedโ current value. Preserves the current alpha if not specified. - Object: Updates specified components, applying numbers directly or evaluating updater functions against current values. Normalizes values using model-specific ranges and handles edge cases similarly.
- Creates a new
Colorinstance with the updated coordinates, preserving the current alpha if not updated.
Example
import { Color } from "saturon";
// Direct component update (object)
const rgbColor = Color.from("rgb(255 87 51)");
const updatedRgb = rgbColor.with({ g: 128 });
console.log(updatedRgb.to("rgb")); // rgb(255 128 51)
// Functional component update
const brighterRgb = rgbColor.with({ r: (r) => r * 0.8 });
console.log(brighterRgb.toString()); // rgb(204 87 51)
// Functional bulk update (object)
const hslColor = Color.from("hsl(50 100% 30%)");
const adjustedHsl = hslColor.with(({ h, s, l }) => ({
h: h + 10,
s: s * (l / 100),
}));
console.log(adjustedHsl.toString()); // hsl(60 30 30)
// Direct coordinate array replacement
const newRgb = rgbColor.with([, 150, 200]);
console.log(newRgb.toString()); // rgb(255 150 200)
// Functional coordinate update (array)
const mutedRgb = rgbColor.with(({ r, g, b }) => [r * 0.5, g * 0.5, b * 0.5, 1]);
console.log(mutedRgb.toString()); // rgb(128 44 26)
// Handle edge cases
const edgeCase = rgbColor.with([NaN, Infinity, -Infinity]);
console.log(edgeCase.toString()); // rgb(0 255 0)Instance Color.mix()
The mix() method creates a new Color instance by interpolating between the current color and another color, specified as a Color instance or a CSS color string. It supports customizable interpolation through MixOptions, including hue interpolation methods, easing functions, blending amount, and gamma correction. This method is ideal for creating gradients, transitions, or blended colors in the current model, with robust handling of alpha channels and perceptual uniformity.
Syntax
mix(other: Color<ColorModel> | string, options: MixOptions = {}): Color<M>other: The color to mix with, either aColorinstance or a CSS color string.options: An optional object configuring the mixing process:hue: String (default"shorter"). Specifies the hue interpolation method for models with a hue component (e.g.,hsl,oklch):"shorter": Interpolates along the shortest arc on the color wheel."longer": Interpolates along the longer arc."increasing": Interpolates toward increasing hue values (e.g., adds 360ยฐ if needed)."decreasing": Interpolates toward decreasing hue values (e.g., subtracts 360ยฐ if needed).
amount: Number (default0.5). The interpolation factor between 0 (current color) and 1 (other color), clamped to[0, 1].easing: String or function (default"linear"). Specifies an easing function (e.g.,"linear","ease-out-cubic") from theEASINGSregistry or a custom function that maps[0, 1]to[0, 1].gamma: Number (default1.0). Applies gamma correction to the eased interpolation factor for non-linear blending.
- Returns: A new
Colorinstance in the current model representing the mixed color. - Throws:
- If the current model lacks defined components in
colorModels). - If an invalid
hueinterpolation method is provided.
- If the current model lacks defined components in
Behavior
- Retrieves the
componentsdefinition fromcolorModels[model]and adds analphacomponent (index: 3,value: [0, 1],precision: 3). - Gets the current coordinates using
toArray()and converts theothercolor to the current model usingin()(parsing strings viaColor.from()). - Clamps
amountto[0, 1]and applies the easing function (fromEASINGSor custom) to compute the interpolation factortt, adjusted bygammaviaMath.pow(ease(t), 1 / gamma). - Handles hue interpolation for components marked as
angle(e.g.,hinhsl) using the specifiedhuemethod (shorter,longer,increasing,decreasing). - For non-hue components, performs linear interpolation:
start + (end - start) * tt. - If either color has an alpha less than 1, applies premultiplied alpha blending:
- Multiplies each component by its respective alpha before interpolation.
- Interpolates the premultiplied values and alpha separately.
- Divides the mixed components by the mixed alpha (if non-zero) to un-premultiply.
- Returns a new
Colorinstance with the mixed coordinates and alpha, preserving the current model.
Example
import { Color } from "saturon";
// Mix two colors in HSL (default shorter hue path)
const hslColor = Color.from("hsl(50 100% 50%)");
const mixedHsl = hslColor.mix("hsl(200 100% 50%)");
console.log(mixedHsl.toString()); // hsl(125 100 50)
// Mix with longer hue path
const longerMix = hslColor.mix("hsl(200 100% 50%)", { hue: "longer" });
console.log(longerMix.toString()); // hsl(305 100 50)
// Mix with custom amount and easing
const easedMix = hslColor.mix("hsl(200 100% 50%)", { amount: 0.75, easing: "ease-out-cubic" });
console.log(easedMix.toString()); // hsl(198 100 50)
// Mix with gamma correction
const gammaMix = hslColor.mix("hsl(200 100% 50%)", { gamma: 2.2 });
console.log(gammaMix.toString()); // hsl(159 100 50)
// Mix with alpha blending
const transparentColor = Color.from("rgba(255 0 0 / 0.5)");
const mixedAlpha = transparentColor.mix("rgba(0 255 0 / 0.7)");
console.log(mixedAlpha.toString()); // rgb(106 149 0 / 0.6)
// Mix with Color instance
const otherColor = Color.from("rgb(0 255 0)");
const rgbMix = transparentColor.mix(otherColor, { amount: 0.3 });
console.log(rgbMix.toString()); // rgb(137 118 0 / 0.65)Instance Color.within()
The within() method fits the current Color instance into a specified color space's gamut, returning a new Color instance in the original model. It ensures colors are displayable within the target gamut (e.g., srgb, display-p3) using the specified fitting method, which is useful for rendering colors on devices with limited color ranges or ensuring compatibility with CSS.
Syntax
within(gamut: ColorSpace, method: FitMethod = "clip"): Color<M>gamut: A string specifying the target color space (e.g.,"srgb","display-p3","rec2020"). Must exist in thecolorSpacesregistry.method: A string specifying the gamut mapping method (default"clip"). Options include:"clip": Clamps out-of-gamut values to the nearest valid value."chroma-reduction": Reduces chroma to fit within the gamut while preserving hue and lightness."css-gamut-map": Applies the CSS Color Module Level 4 gamut mapping algorithm.
- Returns: A new
Colorinstance in the current model with coordinates fitted to the specified gamut. - Throws: If the
gamutis not incolorSpaces.
Behavior
- Normalizes the
gamutstring (trims and lowercases) and validates it against thecolorSpacesregistry. - Converts the color to the target gamut's model using
in(gamut)and retrieves fitted coordinates viatoArray({ fit: method }). - Converts the fitted coordinates back to the original model using
in(this.model). - Returns a new
Colorinstance with the fitted coordinates, preserving the original model.
Example
import { Color } from "saturon";
// Fit a wide-gamut color to sRGB
const wideColor = Color.from("color(display-p3 1 0 0)");
const srgbColor = wideColor.within("srgb", "clip");
console.log(srgbColor.to("rgb")); // rgb(255 0 0)Instance Color.contrast()
The contrast() method calculates the WCAG 2.1 contrast ratio between the current Color instance and another color, specified as a Color instance or CSS color string. The ratio, ranging from 1 to 21, indicates the perceptual difference between two colors, useful for ensuring text accessibility in UI design. Higher ratios indicate better readability.
Syntax
contrast(other: Color<ColorModel> | string): numberother: The color to compare against, either aColorinstance or a CSS color string.- Returns: A number representing the WCAG 2.1 contrast ratio, from 1 (no contrast) to 21 (maximum contrast).
- Throws: None; invalid inputs are handled by
Color.from().
Behavior
- Converts the
othercolor to aColorinstance usingColor.from()if a string is provided. - Converts both colors to the
xyz-d65model usingin("xyz-d65")to extract their relative luminance (Yvalue). - Computes the contrast ratio as
(max(L1, L2) + 0.05) / (min(L1, L2) + 0.05), per WCAG 2.1 specifications. - Returns the ratio, which can be used to assess accessibility (e.g., โฅ4.5 for normal text, โฅ3 for large text).
Example
import { Color } from "saturon";
// Calculate contrast between white and black
const white = Color.from("white");
const black = Color.from("black");
console.log(white.contrast(black)); // 21 (maximum contrast)
// Contrast for text accessibility
const textColor = Color.from("rgb(50 50 50)");
const bgColor = Color.from("#ffffff");
console.log(textColor.contrast(bgColor)); // e.g., 12.8 (accessible for normal text)
// Contrast with another Color instance
const blue = Color.from("blue");
console.log(textColor.contrast(blue)); // e.g., 1.5 (not accessible for normal text)
// Contrast with a color string
console.log(white.contrast("red")); // e.g., 4 (below threshold for normal text)Note on the WCAG contrast algorithm
The W3C CSS Color Level 5 specification cautions against
relying solely on the WCAG 2.1 ยง1.4.3 contrast ratio algorithm when determining whether to use light or dark colors.
This is because the WCAG 2.1 method has several known limitations โ like poor hue handling and perceptual
differences. That said, the contrast() method remains suitable for most accessibility use cases, especially for
verifying compliance with WCAG 2.1 ยง1.4.3 (Contrast โ Minimum)
requirements (e.g., AA-level contrast for normal or large text), which are still widely referenced in accessibility
standards and legal guidelines.
Instance Color.deltaEOK()
The deltaEOK() method calculates the color difference (ฮEOK) between the current Color instance and another color, using Euclidean distance in the OKLAB color space. This method leverages OKLAB's perceptual uniformity to provide a straightforward, reliable measure of color similarity, where smaller values indicate more similar colors. The result is scaled to approximate a Just Noticeable Difference (JND) of ~2, making it useful for comparing colors in design or quality control.
Syntax
deltaEOK(other: Color<ColorModel> | string): numberother: The color to compare against, either aColorinstance or a CSS color string.- Returns: A non-negative number representing the ฮEOK value, scaled by 100 to align with OKLAB's lightness range (0-1) and approximate a JND of ~2.
- Throws: None; invalid inputs are handled by
Color.from().
Behavior
- Converts both the current color and
otherto theoklabmodel usingin("oklab"). - Extracts the
L,a, andbcoordinates from both colors'toArray()results. - Computes the Euclidean distance:
sqrt((L1 - L2)^2 + (a1 - a2)^2 + (b1 - b2)^2). - Scales the distance by 100 to normalize it relative to OKLAB's lightness range and align with JND expectations.
- Returns the scaled ฮEOK value.
Example
import { Color } from "saturon";
// Compare similar colors
const color1 = Color.from("rgb(255 87 51)");
const color2 = Color.from("rgb(255 100 60)");
console.log(color1.deltaEOK(color2)); // e.g., 2.1 (small difference)
// Compare dissimilar colors
const color3 = Color.from("blue");
console.log(color1.deltaEOK(color3)); // e.g., 52.8 (large difference)Instance Color.deltaE76()
The deltaE76() method calculates the color difference (ฮE76) between the current Color instance and another color, using the CIE76 formula (Euclidean distance in LAB color space). This is a simple, older method for measuring color similarity, where smaller values indicate more similar colors. It's less perceptually accurate than newer methods but widely used for its simplicity.
Syntax
deltaE76(other: Color<ColorModel> | string): numberother: The color to compare against, either aColorinstance or a CSS color string.- Returns: A non-negative number representing the ฮE76 value.
- Throws: None; invalid inputs are handled by
Color.from().
Behavior
- Converts both the current color and
otherto thelabmodel usingin("lab"). - Extracts the
L,a, andbcoordinates from both colors'toArray()results. - Computes the Euclidean distance:
sqrt((L1 - L2)^2 + (a1 - a2)^2 + (b1 - b2)^2). - Returns the ฮE76 value.
Example
import { Color } from "saturon";
// Compare similar colors
const color1 = Color.from("rgb(255 87 51)");
const color2 = Color.from("rgb(255 100 60)");
console.log(color1.deltaE76(color2)); // e.g., 5.8 (small difference)
// Compare dissimilar colors
const color3 = Color.from("blue");
console.log(color1.deltaE76(color3)); // e.g., 170.9 (large difference)Instance Color.deltaE94()
The deltaE94() method calculates the color difference (ฮE94) between the current Color instance and another color, using the CIE94 formula. This method improves on CIE76 by applying weighting factors for chroma and hue, offering better perceptual accuracy for color similarity comparisons.
Syntax
deltaE94(other: Color<ColorModel> | string): numberother: The color to compare against, either aColorinstance or a CSS color string.- Returns: A non-negative number representing the ฮE94 value.
- Throws: None; invalid inputs are handled by
Color.from().
Behavior
- Converts both the current color and
otherto thelabmodel usingin("lab"). - Extracts the
L,a, andbcoordinates from both colors'toArray()results. - Computes the differences:
ฮL = L1 - L2,ฮA = a1 - a2,ฮB = b1 - b2. - Calculates chroma values:
C1 = sqrt(a1^2 + b1^2),C2 = sqrt(a2^2 + b2^2),ฮC = C1 - C2. - Computes the hue difference:
ฮH = sqrt(max(0, ฮA^2 + ฮB^2 - ฮC^2)). - Applies weighting factors:
sC = 1 + 0.045 * C1,sH = 1 + 0.015 * C1, with constantskL = 1,kC = 1,kH = 1,K1 = 0.045,K2 = 0.015. - Returns the ฮE94 value:
sqrt((ฮL / kL)^2 + (ฮC / (kC * sC))^2 + (ฮH / (kH * sH))^2).
Example
import { Color } from "saturon";
// Compare similar colors
const color1 = Color.from("rgb(255 87 51)");
const color2 = Color.from("rgb(255 100 60)");
console.log(color1.deltaE94(color2)); // e.g., 2.4 (small difference)
// Compare dissimilar colors
const color3 = Color.from("blue");
console.log(color1.deltaE94(color3)); // e.g., 78.4 (large difference)Instance Color.deltaE2000()
The deltaE2000() method calculates the color difference (ฮE2000) between the current Color instance and another color, using the CIEDE2000 formula. This is the most perceptually accurate method provided, accounting for complex interactions between hue, chroma, and lightness, making it ideal for precise color comparisons in professional applications.
Syntax
deltaE2000(other: Color<ColorModel> | string): numberother: The color to compare against, either aColorinstance or a CSS color string.- Returns: A non-negative number representing the ฮE2000 value.
- Throws: None; invalid inputs are handled by
Color.from().
Behavior
- Converts both the current color and
otherto thelabmodel usingin("lab"). - Extracts the
L,a, andbcoordinates from both colors'toArray()results. - Applies the CIEDE2000 formula, which includes:
- Chroma calculation:
C1 = sqrt(a1^2 + b1^2),C2 = sqrt(a2^2 + b2^2). - Hue angle calculation:
h1 = atan2(b1, (1 + G) * a1),h2 = atan2(b2, (1 + G) * a2), withG = 0.5 * (1 - sqrt(Cbar^7 / (Cbar^7 + 25^7))). - Lightness, chroma, and hue differences:
ฮL,ฮC,ฮH, adjusted for hue interactions. - Weighting factors:
SL,SC,SH, andRTfor rotational hue correction, incorporating trigonometric adjustments and exponential terms.
- Chroma calculation:
- Returns the ฮE2000 value:
sqrt((ฮL / SL)^2 + (ฮC / SC)^2 + (ฮH / SH)^2 + RT * (ฮC / SC) * (ฮH / SH)).
Example
import { Color } from "saturon";
// Compare similar colors
const color1 = Color.from("rgb(255 87 51)");
const color2 = Color.from("rgb(255 100 60)");
console.log(color1.deltaE2000(color2)); // e.g., 2.2(small difference)
// Compare dissimilar colors
const color3 = Color.from("blue");
console.log(color1.deltaE2000(color3)); // e.g., 57.9 (large difference)Instance Color.equals()
The equals() method checks numeric equality between this Color instance and another color (string or instance) within a floating-point tolerance. It compares raw coordinates โ either in the same model or in a common bridge space (xyz-d65) โ making it ideal for testing, serialization, or conversion verification.
Syntax
equals(other: Color<ColorModel> | string, epsilon?: number): booleanother: AColorinstance or CSS color string to compare.epsilon: Optional tolerance (default1e-5). Allows small floating-point differences.- Returns:
trueif numerically equal within tolerance,falseotherwise.
Behavior
- If both colors are in the same model, compares
coordsdirectly. - If models differ, converts both to
xyz-d65(no gamut mapping) and compares. - Uses
Math.abs(a - b) <= epsilonper channel (including alpha).
Example
import { Color } from "saturon";
const color = Color.from("rgb(255 87 51)");
console.log(color.equals("#ff5733")); // true
console.log(color.equals("rgb(255 88 51)", 1e-3)); // falseNot a perceptual comparison
The equals() method checks numeric equality, not visual similarity. It compares raw channel values in the underlying color space (or xyz-d65 if different), without accounting for how humans actually perceive color differences.
Use this method for:
- Verifying precision in color conversions or round-trips
- Comparing serialized or computed values
- Writing deterministic tests
If you need to compare how similar two colors look to the human eye, use one of the ฮE methods instead:
deltaEOKโ modern, perceptually uniform (OKLAB-based)deltaE76โ basic Euclidean LAB distancedeltaE94โ improved LAB weightingdeltaE2000โ most accurate, perceptually tuned
Instance Color.inGamut()
The inGamut() method determines whether the current color lies within the valid range of a specified color space (gamut), such as srgb, display-p3, or rec2020.
Syntax
inGamut(gamut: ColorSpace | string, epsilon?: number): booleangamut: Target color space name (e.g.,"srgb","display-p3").epsilon: Optional tolerance (default1e-5) for boundary checks.- Returns:
trueif all components are within valid ranges,falseotherwise. - Throws: If
gamutis not registered incolorSpaces.
Behavior
- Normalizes
gamutname (trim + lowercase). - Converts the color to the target model via
in(gamut). - Checks each component against its defined range.
- Returns
trueif all values are within bounds (inclusive with epsilon).
Example
import { Color } from "saturon";
const p3Color = Color.from("color(display-p3 1 0 0)");
const srgbSafe = Color.from("rgb(255 0 0)");
console.log(p3Color.inGamut("srgb")); // false
console.log(srgbSafe.inGamut("srgb")); // true
console.log(p3Color.inGamut("display-p3")); // true
// With tolerance
console.log(p3Color.inGamut("srgb", 1e-6)); // false