ColorAssembly
Api

Color Analysis

Complete reference for color analysis and accessibility functions

The analyze API provides color analysis, accessibility checking, and validation functions. All functions are available via api.analyze.

Luminance & Brightness

relativeLuminance(rgb: RGB): number

Calculate WCAG relative luminance (0-1).

const white = { r: 255, g: 255, b: 255 };
const luminance = api.analyze.relativeLuminance(white);
console.log(luminance); // 1.0
 
const black = { r: 0, g: 0, b: 0 };
console.log(api.analyze.relativeLuminance(black)); // 0.0

Returns: number (0-1)

brightness(rgb: RGB): number

Simple brightness calculation (0-255).

const rgb = { r: 128, g: 128, b: 128 };
const brightness = api.analyze.brightness(rgb);
console.log(brightness); // 128

Formula: (r + g + b) / 3

perceivedBrightness(rgb: RGB): number

Perceptually-weighted brightness (0-255).

const rgb = { r: 255, g: 0, b: 0 };
const perceived = api.analyze.perceivedBrightness(rgb);
console.log(perceived); // ~76.245

Formula: Uses weighted coefficients (0.299R + 0.587G + 0.114B)

isLight(rgb: RGB): boolean

Check if color is light (luminance > 0.5).

const white = { r: 255, g: 255, b: 255 };
console.log(api.analyze.isLight(white)); // true
 
const darkBlue = { r: 0, g: 0, b: 100 };
console.log(api.analyze.isLight(darkBlue)); // false

isDark(rgb: RGB): boolean

Check if color is dark (luminance ≤ 0.5).

const black = { r: 0, g: 0, b: 0 };
console.log(api.analyze.isDark(black)); // true

lightOrDark(rgb: RGB): string

Returns "light" or "dark".

const rgb = { r: 200, g: 200, b: 200 };
console.log(api.analyze.lightOrDark(rgb)); // "light"

WCAG Contrast Analysis

contrastRatio(fg: RGB, bg: RGB): number

Calculate WCAG 2.1 contrast ratio (1-21).

const black = { r: 0, g: 0, b: 0 };
const white = { r: 255, g: 255, b: 255 };
const ratio = api.analyze.contrastRatio(black, white);
console.log(ratio); // 21 (maximum contrast)
 
const blue = { r: 99, g: 102, b: 241 };
const bgWhite = { r: 255, g: 255, b: 255 };
console.log(api.analyze.contrastRatio(blue, bgWhite)); // ~4.65

WCAG Requirements:

  • AA Normal Text: 4.5:1
  • AA Large Text: 3:1
  • AAA Normal Text: 7:1
  • AAA Large Text: 4.5:1

checkContrast(fg: RGB, bg: RGB): WCAGContrastResult

Full WCAG compliance report.

const fg = { r: 255, g: 255, b: 255 };
const bg = { r: 99, g: 102, b: 241 };
const result = api.analyze.checkContrast(fg, bg);
 
console.log(result);
// {
//   ratio: 4.65,
//   normalTextAA: true,
//   normalTextAAA: false,
//   largeTextAA: true,
//   largeTextAAA: true,
//   uiComponentsAA: true,
//   level: "AA"
// }

Return Type:

interface WCAGContrastResult {
  ratio: number;
  normalTextAA: boolean;
  normalTextAAA: boolean;
  largeTextAA: boolean;
  largeTextAAA: boolean;
  uiComponentsAA: boolean;
  level: "AA" | "AAA" | "Fail";
}

meetsContrastAA(fg: RGB, bg: RGB): boolean

Check if meets AA normal text (4.5:1).

const passes = api.analyze.meetsContrastAA(
  { r: 0, g: 0, b: 0 },
  { r: 255, g: 255, b: 255 }
);
console.log(passes); // true

meetsContrastAAA(fg: RGB, bg: RGB): boolean

Check if meets AAA normal text (7:1).

const passes = api.analyze.meetsContrastAAA(
  { r: 0, g: 0, b: 0 },
  { r: 200, g: 200, b: 200 }
);
console.log(passes); // false

meetsLargeTextAA(fg: RGB, bg: RGB): boolean

Check if meets AA large text (3:1).

const passes = api.analyze.meetsLargeTextAA(fg, bg);

meetsLargeTextAAA(fg: RGB, bg: RGB): boolean

Check if meets AAA large text (4.5:1).

const passes = api.analyze.meetsLargeTextAAA(fg, bg);

contrastWithWhite(rgb: RGB): number

Shorthand for contrast against white.

const blue = { r: 0, g: 0, b: 255 };
const ratio = api.analyze.contrastWithWhite(blue);
console.log(ratio); // ~8.59

contrastWithBlack(rgb: RGB): number

Shorthand for contrast against black.

const yellow = { r: 255, g: 255, b: 0 };
const ratio = api.analyze.contrastWithBlack(yellow);
console.log(ratio); // ~19.56

suggestTextColor(bg: RGB): RGB

Get optimal text color (black or white) for background.

const darkBg = { r: 30, g: 30, b: 30 };
const textColor = api.analyze.suggestTextColor(darkBg);
console.log(textColor); // { r: 255, g: 255, b: 255 }
 
const lightBg = { r: 240, g: 240, b: 240 };
const textColor2 = api.analyze.suggestTextColor(lightBg);
console.log(textColor2); // { r: 0, g: 0, b: 0 }

findAccessibleShade(color: RGB, bg: RGB, targetContrast: number): RGB

Find accessible variant of color that meets target contrast.

const color = { r: 100, g: 100, b: 255 };
const bg = { r: 255, g: 255, b: 255 };
const accessible = api.analyze.findAccessibleShade(color, bg, 4.5);
console.log(accessible);
// Returns darker/lighter version meeting 4.5:1 contrast

APCA Contrast (Modern)

APCA (Accessible Perceptual Contrast Algorithm) is a modern alternative to WCAG contrast.

apcaContrast(text: RGB, bg: RGB): number

Calculate APCA Lc value.

const text = { r: 0, g: 0, b: 0 };
const bg = { r: 255, g: 255, b: 255 };
const lc = api.analyze.apcaContrast(text, bg);
console.log(lc); // ~106 (high contrast)

Returns: Lc value (-108 to +108)

  • Positive: dark text on light background
  • Negative: light text on dark background

checkApca(text: RGB, bg: RGB): APCAResult

Full APCA analysis.

const result = api.analyze.checkApca(text, bg);
console.log(result);
// {
//   lc: 106,
//   absLc: 106,
//   fontSizeNormal: 14,
//   fontSizeBold: 12,
//   meetsMinimum: true,
//   meetsBodyText: true,
//   meetsLargeText: true,
//   meetsNonText: true
// }

meetsApcaBodyText(text: RGB, bg: RGB): boolean

Check if meets APCA body text requirement (Lc 75+).

const passes = api.analyze.meetsApcaBodyText(text, bg);

meetsApcaLargeText(text: RGB, bg: RGB): boolean

Check if meets APCA large text requirement (Lc 60+).

const passes = api.analyze.meetsApcaLargeText(text, bg);

meetsApcaNonText(text: RGB, bg: RGB): boolean

Check if meets APCA non-text/UI requirement (Lc 45+).

const passes = api.analyze.meetsApcaNonText(text, bg);

Color Difference (Delta-E)

deltaE2000(color1: RGB, color2: RGB): number

Calculate CIE Delta-E 2000 color difference.

const red1 = { r: 255, g: 0, b: 0 };
const red2 = { r: 250, g: 5, b: 5 };
const difference = api.analyze.deltaE2000(red1, red2);
console.log(difference); // ~3.2

Interpretation:

  • < 1.0: Not perceptible
  • 1.0-2.0: Perceptible on close observation
  • 2.0-10.0: Perceptible at a glance
  • 10.0-50.0: Different colors
  • > 50.0: Opposite colors

analyzeColorDifference(c1: RGB, c2: RGB): DeltaEResult

Detailed color difference analysis.

const result = api.analyze.analyzeColorDifference(color1, color2);
console.log(result);
// {
//   deltaE: 3.2,
//   description: "Perceptible at a glance",
//   perceptible: true
// }

areColorsSimilar(c1: RGB, c2: RGB): boolean

Check if colors are similar (ΔE < 10).

const similar = api.analyze.areColorsSimilar(
  { r: 255, g: 0, b: 0 },
  { r: 250, g: 5, b: 5 }
);
console.log(similar); // true

areColorsIdentical(c1: RGB, c2: RGB): boolean

Check if colors are identical (ΔE < 1).

const identical = api.analyze.areColorsIdentical(color1, color2);

Adaptive Colors

adaptiveTextColor(bg: RGB): RGB

Get optimal text color using APCA.

const bg = { r: 99, g: 102, b: 241 };
const textColor = api.analyze.adaptiveTextColor(bg);
console.log(textColor); // { r: 255, g: 255, b: 255 }

More sophisticated than suggestTextColor - uses APCA algorithm.

ensureContrast(fg: RGB, bg: RGB, minContrast: number): RGB

Adjust foreground to meet minimum contrast.

const fg = { r: 150, g: 150, b: 150 };
const bg = { r: 255, g: 255, b: 255 };
const adjusted = api.analyze.ensureContrast(fg, bg, 4.5);
console.log(adjusted);
// Returns darker gray meeting 4.5:1

generateContrastingColors(bg: RGB, count: number, minContrast: number): RGB[]

Generate multiple colors with guaranteed contrast.

const bg = { r: 255, g: 255, b: 255 };
const colors = api.analyze.generateContrastingColors(bg, 5, 4.5);
console.log(colors);
// Returns array of 5 colors, each meeting 4.5:1 with bg

blendWithBackground(fg: RGB, bg: RGB, opacity: number): RGB

Blend foreground with background using alpha.

const fg = { r: 255, g: 0, b: 0 };
const bg = { r: 255, g: 255, b: 255 };
const blended = api.analyze.blendWithBackground(fg, bg, 0.5);
console.log(blended); // { r: 255, g: 127, b: 127 }

isSafeForColorBlind(c1: RGB, c2: RGB): boolean

Check if color pair is distinguishable for color blind users.

const safe = api.analyze.isSafeForColorBlind(
  { r: 255, g: 0, b: 0 },
  { r: 0, g: 255, b: 0 }
);
console.log(safe); // false (red-green not safe)

generateAccessiblePairs(base: RGB, count: number): AccessibleColorPair[]

Generate accessible foreground/background pairs.

const pairs = api.analyze.generateAccessiblePairs(
  { r: 99, g: 102, b: 241 },
  3
);
console.log(pairs);
// [
//   {
//     foreground: { r: 255, g: 255, b: 255 },
//     background: { r: 99, g: 102, b: 241 },
//     contrast: 4.65,
//     apcaContrast: -72.5
//   },
//   ...
// ]

Validation

isValidHex(hex: string): boolean

Quick hex validation.

console.log(api.analyze.isValidHex("#FF0000")); // true
console.log(api.analyze.isValidHex("#F00")); // true
console.log(api.analyze.isValidHex("FF0000")); // true
console.log(api.analyze.isValidHex("invalid")); // false

Accepts: #RGB, #RRGGBB, #RRGGBBAA, with or without #

validateHex(hex: string): ValidationResult

Detailed hex validation.

const result = api.analyze.validateHex("#FF0000");
console.log(result);
// {
//   valid: true,
//   message: "Valid hex color",
//   format: "hex6"
// }
 
const invalid = api.analyze.validateHex("invalid");
console.log(invalid);
// {
//   valid: false,
//   message: "Invalid hex format",
//   format: null
// }

validateColorString(str: string): ValidationResult

Validate any color string format.

const result = api.analyze.validateColorString("rgb(255, 0, 0)");

normalizeHex(hex: string): string

Normalize hex to standard format.

console.log(api.analyze.normalizeHex("F00")); // "#FF0000"
console.log(api.analyze.normalizeHex("FF0000")); // "#FF0000"
console.log(api.analyze.normalizeHex("#ff0000")); // "#FF0000"

isGrayscale(rgb: RGB): boolean

Check if color is grayscale.

console.log(api.analyze.isGrayscale({ r: 128, g: 128, b: 128 })); // true
console.log(api.analyze.isGrayscale({ r: 128, g: 129, b: 128 })); // false

isNearGrayscale(rgb: RGB, tolerance: number): boolean

Check if color is near grayscale within tolerance.

const nearGray = api.analyze.isNearGrayscale(
  { r: 128, g: 130, b: 127 },
  5
);
console.log(nearGray); // true (within 5 units)

colorType(rgb: RGB): string

Classify color type.

console.log(api.analyze.colorType({ r: 0, g: 0, b: 0 })); // "black"
console.log(api.analyze.colorType({ r: 255, g: 255, b: 255 })); // "white"
console.log(api.analyze.colorType({ r: 128, g: 128, b: 128 })); // "grayscale"
console.log(api.analyze.colorType({ r: 255, g: 0, b: 0 })); // "chromatic"

Returns: "black", "white", "grayscale", or "chromatic"

On this page

Luminance & BrightnessrelativeLuminance(rgb: RGB): numberbrightness(rgb: RGB): numberperceivedBrightness(rgb: RGB): numberisLight(rgb: RGB): booleanisDark(rgb: RGB): booleanlightOrDark(rgb: RGB): stringWCAG Contrast AnalysiscontrastRatio(fg: RGB, bg: RGB): numbercheckContrast(fg: RGB, bg: RGB): WCAGContrastResultmeetsContrastAA(fg: RGB, bg: RGB): booleanmeetsContrastAAA(fg: RGB, bg: RGB): booleanmeetsLargeTextAA(fg: RGB, bg: RGB): booleanmeetsLargeTextAAA(fg: RGB, bg: RGB): booleancontrastWithWhite(rgb: RGB): numbercontrastWithBlack(rgb: RGB): numbersuggestTextColor(bg: RGB): RGBfindAccessibleShade(color: RGB, bg: RGB, targetContrast: number): RGBAPCA Contrast (Modern)apcaContrast(text: RGB, bg: RGB): numbercheckApca(text: RGB, bg: RGB): APCAResultmeetsApcaBodyText(text: RGB, bg: RGB): booleanmeetsApcaLargeText(text: RGB, bg: RGB): booleanmeetsApcaNonText(text: RGB, bg: RGB): booleanColor Difference (Delta-E)deltaE2000(color1: RGB, color2: RGB): numberanalyzeColorDifference(c1: RGB, c2: RGB): DeltaEResultareColorsSimilar(c1: RGB, c2: RGB): booleanareColorsIdentical(c1: RGB, c2: RGB): booleanAdaptive ColorsadaptiveTextColor(bg: RGB): RGBensureContrast(fg: RGB, bg: RGB, minContrast: number): RGBgenerateContrastingColors(bg: RGB, count: number, minContrast: number): RGB[]blendWithBackground(fg: RGB, bg: RGB, opacity: number): RGBisSafeForColorBlind(c1: RGB, c2: RGB): booleangenerateAccessiblePairs(base: RGB, count: number): AccessibleColorPair[]ValidationisValidHex(hex: string): booleanvalidateHex(hex: string): ValidationResultvalidateColorString(str: string): ValidationResultnormalizeHex(hex: string): stringisGrayscale(rgb: RGB): booleanisNearGrayscale(rgb: RGB, tolerance: number): booleancolorType(rgb: RGB): string