Fast color parsing and manipulation library with comprehensive conversion and accessibility features
—
Static methods for calculating color contrast ratios and determining readability according to WCAG (Web Content Accessibility Guidelines). These functions help ensure your color choices meet accessibility standards for users with visual impairments.
Calculates the contrast ratio between two colors using WCAG formulas.
/**
* Calculates the contrast ratio between two colors
* Uses WCAG formula: (L1 + 0.05) / (L2 + 0.05) where L1 is lighter, L2 is darker
* @param color1 - First color (any valid tinycolor input)
* @param color2 - Second color (any valid tinycolor input)
* @returns number representing contrast ratio (1-21, where 21 is maximum contrast)
*/
tinycolor.readability(color1: any, color2: any): number;Usage Examples:
import tinycolor from "tinycolor2";
// Calculate contrast ratios
const whiteBlack = tinycolor.readability("white", "black");
const whiteGray = tinycolor.readability("#ffffff", "#808080");
const blueWhite = tinycolor.readability("#0066cc", "white");
console.log("White/Black contrast:", whiteBlack); // 21 (maximum contrast)
console.log("White/Gray contrast:", whiteGray); // ~2.28
console.log("Blue/White contrast:", blueWhite); // ~4.89
// Check various background/text combinations
const backgrounds = ["#ffffff", "#f8f9fa", "#e9ecef", "#343a40", "#000000"];
const textColors = ["#000000", "#495057", "#6c757d", "#ffffff"];
backgrounds.forEach(bg => {
console.log(`Background: ${bg}`);
textColors.forEach(text => {
const ratio = tinycolor.readability(bg, text);
console.log(` Text ${text}: ${ratio.toFixed(2)}`);
});
});Tests if two colors meet WCAG readability guidelines.
/**
* Tests if color combination meets WCAG guidelines
* @param color1 - First color (any valid tinycolor input)
* @param color2 - Second color (any valid tinycolor input)
* @param wcag2 - WCAG parameters object (optional)
* @returns boolean indicating if combination meets standards
*/
tinycolor.isReadable(color1: any, color2: any, wcag2?: {level?: "AA" | "AAA", size?: "small" | "large"}): boolean;WCAG Standards:
Usage Examples:
// Test with default settings (AA level, small text)
const isReadable1 = tinycolor.isReadable("#000000", "#ffffff");
console.log("Black on white:", isReadable1); // true
const isReadable2 = tinycolor.isReadable("#777777", "#ffffff");
console.log("Gray on white:", isReadable2); // false (insufficient contrast)
// Test with specific WCAG levels
const combinations = [
{ bg: "#ffffff", text: "#000000" },
{ bg: "#ffffff", text: "#666666" },
{ bg: "#ffffff", text: "#999999" },
{ bg: "#2c3e50", text: "#ffffff" },
{ bg: "#3498db", text: "#ffffff" }
];
combinations.forEach(combo => {
const aaSmall = tinycolor.isReadable(combo.bg, combo.text, {level: "AA", size: "small"});
const aaLarge = tinycolor.isReadable(combo.bg, combo.text, {level: "AA", size: "large"});
const aaaSmall = tinycolor.isReadable(combo.bg, combo.text, {level: "AAA", size: "small"});
const aaaLarge = tinycolor.isReadable(combo.bg, combo.text, {level: "AAA", size: "large"});
console.log(`${combo.text} on ${combo.bg}:`);
console.log(` AA Small: ${aaSmall}, AA Large: ${aaLarge}`);
console.log(` AAA Small: ${aaaSmall}, AAA Large: ${aaaLarge}`);
});
// Validate user interface colors
function validateUIColors(backgroundColor, textColor, level = "AA") {
const tests = {
smallText: tinycolor.isReadable(backgroundColor, textColor, {level, size: "small"}),
largeText: tinycolor.isReadable(backgroundColor, textColor, {level, size: "large"}),
ratio: tinycolor.readability(backgroundColor, textColor)
};
return {
passes: tests.smallText,
passesLarge: tests.largeText,
ratio: tests.ratio,
level: level
};
}Finds the most readable color from a list of options for a given background.
/**
* Finds the most readable color from a list for the given background
* @param baseColor - Background color (any valid tinycolor input)
* @param colorList - Array of potential text colors
* @param args - Optional arguments object
* @returns tinycolor instance of the most readable color
*/
tinycolor.mostReadable(baseColor: any, colorList: any[], args?: {includeFallbackColors?: boolean, level?: "AA" | "AAA", size?: "small" | "large"}): tinycolor;Usage Examples:
// Find best text color for various backgrounds
const textOptions = ["#000000", "#333333", "#666666", "#999999", "#ffffff"];
const darkBg = "#2c3e50";
const lightBg = "#ecf0f1";
const colorfulBg = "#e74c3c";
const bestForDark = tinycolor.mostReadable(darkBg, textOptions);
const bestForLight = tinycolor.mostReadable(lightBg, textOptions);
const bestForColorful = tinycolor.mostReadable(colorfulBg, textOptions);
console.log("Best text for dark background:", bestForDark.toHexString());
console.log("Best text for light background:", bestForLight.toHexString());
console.log("Best text for colorful background:", bestForColorful.toHexString());
// With WCAG level requirements
const bestAAA = tinycolor.mostReadable("#3498db", ["#ffffff", "#000000"], {
level: "AAA",
size: "small"
});
// With fallback colors (includes black and white if needed)
const bestWithFallback = tinycolor.mostReadable("#ff6b6b", ["#ffcc02", "#06ffa5"], {
includeFallbackColors: true,
level: "AA"
});
// Smart text color selection
function getOptimalTextColor(backgroundColor, preferredColors = ["#000000", "#ffffff"]) {
const bgColor = tinycolor(backgroundColor);
// Start with preferred colors
let bestColor = tinycolor.mostReadable(bgColor, preferredColors);
// If no preferred color works, expand options
if (!tinycolor.isReadable(bgColor, bestColor)) {
const expandedOptions = [
"#000000", "#333333", "#666666", "#999999", "#cccccc", "#ffffff"
];
bestColor = tinycolor.mostReadable(bgColor, expandedOptions, {
includeFallbackColors: true
});
}
return bestColor.toHexString();
}function analyzeColorAccessibility(backgroundColor, textColor) {
const bgColor = tinycolor(backgroundColor);
const txtColor = tinycolor(textColor);
if (!bgColor.isValid() || !txtColor.isValid()) {
return { error: "Invalid color input" };
}
const ratio = tinycolor.readability(bgColor, txtColor);
const compliance = {
"AA-small": ratio >= 4.5,
"AA-large": ratio >= 3.0,
"AAA-small": ratio >= 7.0,
"AAA-large": ratio >= 4.5
};
const suggestions = [];
if (!compliance["AA-small"]) {
// Suggest better alternatives
const alternatives = ["#000000", "#ffffff"];
const better = tinycolor.mostReadable(bgColor, alternatives);
suggestions.push(`Try ${better.toHexString()} for better contrast`);
}
return {
ratio: ratio,
compliance: compliance,
suggestions: suggestions,
grade: compliance["AAA-small"] ? "AAA" :
compliance["AA-small"] ? "AA" :
compliance["AA-large"] ? "AA (large text only)" : "Fail"
};
}
// Usage
const analysis = analyzeColorAccessibility("#3498db", "#ffffff");
console.log(analysis);function validateDesignSystem(colorPalette) {
const results = {};
// Test all background/text combinations
Object.keys(colorPalette.backgrounds || {}).forEach(bgName => {
const bgColor = colorPalette.backgrounds[bgName];
results[bgName] = {};
Object.keys(colorPalette.text || {}).forEach(textName => {
const textColor = colorPalette.text[textName];
const ratio = tinycolor.readability(bgColor, textColor);
const isAA = tinycolor.isReadable(bgColor, textColor);
const isAAA = tinycolor.isReadable(bgColor, textColor, {level: "AAA"});
results[bgName][textName] = {
ratio: ratio,
AA: isAA,
AAA: isAAA,
status: isAAA ? "AAA" : isAA ? "AA" : "FAIL"
};
});
});
return results;
}
// Example color palette
const myPalette = {
backgrounds: {
primary: "#2c3e50",
secondary: "#3498db",
light: "#ecf0f1",
white: "#ffffff"
},
text: {
primary: "#2c3e50",
secondary: "#7f8c8d",
light: "#bdc3c7",
white: "#ffffff"
}
};
const validation = validateDesignSystem(myPalette);function makeReadable(backgroundColor, textColor, targetLevel = "AA", targetSize = "small") {
const bgColor = tinycolor(backgroundColor);
let txtColor = tinycolor(textColor);
// If already readable, return as-is
if (tinycolor.isReadable(bgColor, txtColor, {level: targetLevel, size: targetSize})) {
return txtColor.toHexString();
}
// Try to adjust the text color to meet requirements
const bgLuminance = bgColor.getLuminance();
const shouldBeDark = bgLuminance > 0.5;
// Start with pure black or white based on background
const startColor = shouldBeDark ? "#000000" : "#ffffff";
let adjustedColor = tinycolor(startColor);
// If pure black/white doesn't work, gradually adjust towards original color
if (!tinycolor.isReadable(bgColor, adjustedColor, {level: targetLevel, size: targetSize})) {
// This is rare, but handle edge cases
const alternatives = ["#000000", "#ffffff", "#333333", "#cccccc"];
adjustedColor = tinycolor.mostReadable(bgColor, alternatives, {
includeFallbackColors: true,
level: targetLevel,
size: targetSize
});
}
return adjustedColor.toHexString();
}
// Usage
const readableText = makeReadable("#ff6b6b", "#ffcc02", "AA", "small");
console.log("Adjusted text color:", readableText);function checkColorBlindAccessibility(color1, color2) {
const c1 = tinycolor(color1);
const c2 = tinycolor(color2);
// Convert to different color spaces for analysis
const rgb1 = c1.toRgb();
const rgb2 = c2.toRgb();
// Simulate common color blindness types (simplified)
const protanopia = {
// Red-blind simulation (simplified)
color1: tinycolor({r: rgb1.g, g: rgb1.g, b: rgb1.b}),
color2: tinycolor({r: rgb2.g, g: rgb2.g, b: rgb2.b})
};
const deuteranopia = {
// Green-blind simulation (simplified)
color1: tinycolor({r: rgb1.r, g: rgb1.r, b: rgb1.b}),
color2: tinycolor({r: rgb2.r, g: rgb2.r, b: rgb2.b})
};
return {
normal: tinycolor.readability(c1, c2),
protanopia: tinycolor.readability(protanopia.color1, protanopia.color2),
deuteranopia: tinycolor.readability(deuteranopia.color1, deuteranopia.color2),
// Add tritanopia and other types as needed
};
}Install with Tessl CLI
npx tessl i tessl/npm-tinycolor2