A comprehensive TypeScript/Deno library for color manipulation, parsing, conversion, and accessibility analysis. Built with WCAG standards in mind, contrastrast helps you create accessible color combinations and analyze contrast ratios.
Features:
- 🎨 Multi-format parsing: Supports HEX, RGB, and HSL color formats
- ♿ WCAG compliance: Built-in WCAG 2.1 contrast ratio calculations and compliance checking
- 📊 Color analysis: Luminance, brightness, and accessibility calculations
- 🔄 Format conversion: Convert between HEX, RGB, and HSL formats
- ⚡ TypeScript: Full TypeScript support with comprehensive type definitions
Install contrastrast by running one of the following commands:
npm install --save contrastrast
yarn add contrastrast
pnpm install --save contrastrast
deno add jsr:@amuench/contrastrastimport { Contrastrast } from "contrastrast";
// Parse any color format, by default will throw an error if the color string is invalid
const color = new Contrastrast("#1a73e8");
// Check if the color is light or dark
console.log(color.isLight()); // false
console.log(color.isDark()); // true
// Get contrast ratio with another color
const ratio = color.contrastRatio("#ffffff"); // 4.5
// Check WCAG compliance
const meetsAA = color.meetsWCAG("#ffffff", "background", "AA"); // true
// Convert between formats
console.log(color.toHex()); // "#1a73e8"
console.log(color.toRgbString()); // "rgb(26, 115, 232)"
console.log(color.toHslString()); // "hsl(218, 80%, 51%)"The library exports the following TypeScript types:
type RGBValues = {
r: number; // Red (0-255)
g: number; // Green (0-255)
b: number; // Blue (0-255)
};
type HSLValues = {
h: number; // Hue (0-360)
s: number; // Saturation (0-100)
l: number; // Lightness (0-100)
};type ParseOptions = {
throwOnError: boolean; // Whether to throw on invalid colors
fallbackColor?: string; // Fallback color when throwOnError is false
};
type ContrastOptions = {
returnDetails?: boolean; // Return detailed WCAG analysis instead of just ratio
};type ContrastResult = {
ratio: number;
passes: {
AA_NORMAL: boolean; // WCAG AA normal text (4.5:1)
AA_LARGE: boolean; // WCAG AA large text (3:1)
AAA_NORMAL: boolean; // WCAG AAA normal text (7:1)
AAA_LARGE: boolean; // WCAG AAA large text (4.5:1)
};
};type WCAGContrastLevel = "AA" | "AAA";
type WCAGTextSize = "normal" | "large";Create a new Contrastrast instance from any supported color string.
const color1 = new Contrastrast("#ff0000");
const color2 = new Contrastrast("rgb(255, 0, 0)");
const color3 = new Contrastrast("hsl(0, 100%, 50%)");
// With error handling
const safeColor = new Contrastrast("invalid-color", {
throwOnError: false,
fallbackColor: "#000000",
});Static method alias for the constructor. Also accepts the same parseOpts
configuration object.
const color = Contrastrast.parse("#1a73e8");
// With error handling
const safeColor = Contrastrast.parse("invalid-color", {
throwOnError: false,
fallbackColor: "#ffffff",
});Create from hex color (with or without #). Supports 3 and 6 digit codes.
const red1 = Contrastrast.fromHex("#ff0000");
const red2 = Contrastrast.fromHex("ff0000");
const shortRed = Contrastrast.fromHex("#f00");Contrastrast.fromRgb(r: number, g: number, b: number): Contrastrast / Contrastrast.fromRgb(rgb: RGBValues): Contrastrast
Create from RGB values.
const red1 = Contrastrast.fromRgb(255, 0, 0);
const red2 = Contrastrast.fromRgb({ r: 255, g: 0, b: 0 });Contrastrast.fromHsl(h: number, s: number, l: number): Contrastrast / Contrastrast.fromHsl(hsl: HSLValues): Contrastrast
Create from HSL values.
const red1 = Contrastrast.fromHsl(0, 100, 50);
const red2 = Contrastrast.fromHsl({ h: 0, s: 100, l: 50 });Convert to hex format.
const color = new Contrastrast("rgb(255, 0, 0)");
console.log(color.toHex()); // "#ff0000"
console.log(color.toHex(false)); // "ff0000"Get RGB values as an object.
const rgb = color.toRgb(); // { r: 255, g: 0, b: 0 }Convert to RGB string format.
const rgbString = color.toRgbString(); // "rgb(255, 0, 0)"Get HSL values as an object.
const hsl = color.toHsl(); // { h: 0, s: 100, l: 50 }Convert to HSL string format.
const hslString = color.toHslString(); // "hsl(0, 100%, 50%)"Calculate WCAG 2.1 relative luminance (0-1).
const black = new Contrastrast("#000000");
const white = new Contrastrast("#ffffff");
console.log(black.luminance()); // 0
console.log(white.luminance()); // 1Calculate perceived brightness using AERT formula (0-255).
const color = new Contrastrast("#1a73e8");
const brightness = color.brightness(); // ~102.4Determine if color is light or dark based on AERT brightness threshold (124).
const lightColor = new Contrastrast("#ffffff");
const darkColor = new Contrastrast("#000000");
console.log(lightColor.isLight()); // true
console.log(darkColor.isDark()); // trueCalculate WCAG 2.1 contrast ratio between colors.
const bgColor = new Contrastrast("#1a73e8");
const ratio = bgColor.contrastRatio("#ffffff"); // 4.5textContrast(comparisonColor: Contrastrast | string, role?: "foreground" | "background", options?: ContrastOptions): number | ContrastResult
Calculate contrast with detailed WCAG compliance analysis options.
// Simple ratio
const ratio = bgColor.textContrast("#ffffff"); // 4.5
// Detailed analysis
const result = bgColor.textContrast("#ffffff", "background", {
returnDetails: true,
});
// {
// ratio: 4.5,
// passes: {
// AA_NORMAL: true,
// AA_LARGE: true,
// AAA_NORMAL: false,
// AAA_LARGE: true
// }
// }meetsWCAG(comparisonColor: Contrastrast | string, role: "foreground" | "background", level: "AA" | "AAA", textSize?: "normal" | "large"): boolean
Check WCAG compliance for specific requirements.
const bgColor = new Contrastrast("#1a73e8");
// Check different WCAG levels
const meetsAA = bgColor.meetsWCAG("#ffffff", "background", "AA"); // true
const meetsAAA = bgColor.meetsWCAG("#ffffff", "background", "AAA"); // false
const meetsAALarge = bgColor.meetsWCAG("#ffffff", "background", "AA", "large"); // trueCompare colors for equality.
const color1 = new Contrastrast("#ff0000");
const color2 = new Contrastrast("rgb(255, 0, 0)");
console.log(color1.equals(color2)); // trueBoth the constructor and parse method accept optional configuration for error
handling:
interface ParseOptions {
throwOnError: boolean; // Whether to throw on invalid colors (default: true)
fallbackColor?: string; // Fallback color when throwOnError is false (default: "#000000")
}
// Safe parsing with fallback
const safeColor = Contrastrast.parse("invalid-color", {
throwOnError: false,
fallbackColor: "#333333",
});
// Will throw on invalid color (default behavior)
const strictColor = new Contrastrast("invalid-color"); // throws Error#ff0000orff0000#f00orf00(short format)
rgb(255, 0, 0)rgb(100, 200, 230)
hsl(0, 100%, 50%)hsl(217, 90%, 61%)
import { Contrastrast } from "contrastrast";
interface ColorCardProps {
backgroundColor: string;
children: React.ReactNode;
}
const ColorCard: React.FC<ColorCardProps> = ({ backgroundColor, children }) => {
const bgColor = new Contrastrast(backgroundColor);
const textColor = bgColor.isLight() ? "#000000" : "#ffffff";
return (
<div style={{ backgroundColor, color: textColor }}>
{children}
</div>
);
};import { Contrastrast } from "contrastrast";
function validateColorCombination(background: string, foreground: string): {
isValid: boolean;
level: string;
ratio: number;
} {
const bgColor = new Contrastrast(background);
const ratio = bgColor.contrastRatio(foreground);
const meetsAAA = bgColor.meetsWCAG(foreground, "background", "AAA");
const meetsAA = bgColor.meetsWCAG(foreground, "background", "AA");
return {
isValid: meetsAA,
level: meetsAAA ? "AAA" : meetsAA ? "AA" : "FAIL",
ratio,
};
}
const result = validateColorCombination("#1a73e8", "#ffffff");
console.log(result); // { isValid: true, level: "AA", ratio: 4.5 }In addition to the Contrastrast class methods, contrastrast exports standalone utility functions for when you need to work with colors without creating class instances.
textContrast(foreground: Contrastrast | string, background: Contrastrast | string, options?: ContrastOptions): number | ContrastResult
Calculate contrast ratio between two colors with optional detailed WCAG analysis.
import { textContrast } from "contrastrast";
// Simple ratio calculation
const ratio = textContrast("#000000", "#ffffff"); // 21
// Detailed WCAG analysis
const analysis = textContrast("#1a73e8", "#ffffff", { returnDetails: true });
// {
// ratio: 4.5,
// passes: {
// AA_NORMAL: true,
// AA_LARGE: true,
// AAA_NORMAL: false,
// AAA_LARGE: true
// }
// }Calculate the WCAG 2.1 contrast ratio between any two colors.
import { contrastRatio } from "contrastrast";
const ratio1 = contrastRatio("#1a73e8", "#ffffff"); // 4.5
const ratio2 = contrastRatio("rgb(255, 0, 0)", "hsl(0, 0%, 100%)"); // 3.998
// Works with mixed formats
const ratio3 = contrastRatio("#000", "rgb(255, 255, 255)"); // 21Both functions accept color strings in any supported format (HEX, RGB, HSL) or Contrastrast instances.
For backward compatibility, contrastrast still exports the legacy v0.3.x API, but these methods are deprecated and will be removed in v2.0.
textContrastForBGColor(bgColorString: string, options?: Partial<ContrastrastOptions>): "dark" | "light"
new Contrastrast(bgColor).isLight() ? "dark" : "light"
or the new class methods instead.
import { textContrastForBGColor } from "contrastrast";
// Legacy usage (deprecated)
const textColor = textContrastForBGColor("#1a73e8"); // "light"
// Recommended v1.0+ approach
const bgColor = new Contrastrast("#1a73e8");
const textColor = bgColor.isLight() ? "dark" : "light"; // "light"Migration Guide:
- Replace
textContrastForBGColor(color)withnew Contrastrast(color).isLight() ? "dark" : "light" - For more sophisticated analysis, use the new WCAG-compliant methods like
meetsWCAG()ortextContrast() - The legacy
ContrastrastOptionsare replaced byParseOptionsfor error handling
Happy for any and all contributions. This project uses Deno for development with the following commands:
deno test- Run testsdeno lint- Lint codedeno fmt- Format codedeno task build:npm- Test building the NPM distribution
Please note I prefer git commits formatted with
gitmoji-cli.
