@@ -66,7 +75,82 @@
});
}
- function getContrastRatio(c1, c2) {
+ function parseColor(color) {
+ // Remove all whitespace and convert to lowercase
+ const originalColor = color; // Store original format
+ color = color.replace(/\s/g, '').toLowerCase();
+
+ // If it's just numbers, assume it's an error
+ if (/^\d+$/.test(color)) {
+ throw new Error(`Invalid color format: ${color}. Please use hex (#RGB or #RRGGBB) or rgb()/rgba() format.`);
+ }
+
+ // Add # prefix if it's a hex code without it
+ if (/^[0-9a-f]{3}$|^[0-9a-f]{6}$/.test(color)) {
+ color = '#' + color;
+ }
+
+ // Handle hex colors
+ if (color.startsWith('#')) {
+ // Validate hex format
+ if (!/^#([0-9a-f]{3}|[0-9a-f]{6})$/.test(color)) {
+ throw new Error(`Invalid hex color format: ${color}. Use #RGB or #RRGGBB format.`);
+ }
+
+ // Convert 3-digit hex to 6-digit
+ if (color.length === 4) {
+ color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
+ }
+ // Remove # and convert to RGB array
+ const hex = color.substring(1);
+ return {
+ r: parseInt(hex.slice(0, 2), 16),
+ g: parseInt(hex.slice(2, 4), 16),
+ b: parseInt(hex.slice(4, 6), 16),
+ a: 1,
+ originalFormat: originalColor
+ };
+ }
+
+ // Handle rgb/rgba colors
+ if (color.startsWith('rgb')) {
+ // Validate rgb/rgba format
+ if (!/^rgba?\(\d+(\.\d+)?(,\d+(\.\d+)?){2}(,\d*\.?\d+)?\)$/.test(color)) {
+ throw new Error(`Invalid rgb/rgba format: ${color}. Use rgb(r,g,b) or rgba(r,g,b,a) format.`);
+ }
+
+ const values = color.match(/[\d.]+/g);
+ const rgb = values.map(v => parseFloat(v));
+
+ // Validate rgb values are in range
+ if (rgb.slice(0, 3).some(v => v < 0 || v > 255)) {
+ throw new Error(`RGB values must be between 0 and 255: ${color}`);
+ }
+ if (rgb.length === 4 && (rgb[3] < 0 || rgb[3] > 1)) {
+ throw new Error(`Alpha value must be between 0 and 1: ${color}`);
+ }
+
+ return {
+ r: rgb[0],
+ g: rgb[1],
+ b: rgb[2],
+ a: rgb.length === 4 ? rgb[3] : 1,
+ originalFormat: originalColor
+ };
+ }
+
+ throw new Error(`Unsupported color format: ${color}. Please use hex (#RGB or #RRGGBB) or rgb()/rgba() format.`);
+ }
+
+ function colorToHex({r, g, b}) {
+ const toHex = (n) => {
+ const hex = Math.round(n).toString(16);
+ return hex.length === 1 ? '0' + hex : hex;
+ };
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
+ }
+
+ function getContrastRatio(color1, color2) {
function luminance(r, g, b) {
let a = [r, g, b].map(v => {
v /= 255;
@@ -74,10 +158,28 @@
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
- let rgb1 = c1.match(/\w\w/g).map(x => parseInt(x, 16));
- let rgb2 = c2.match(/\w\w/g).map(x => parseInt(x, 16));
- let lum1 = luminance(...rgb1);
- let lum2 = luminance(...rgb2);
+
+ // Parse colors and handle alpha compositing with white background
+ const parseAndComposite = (color) => {
+ const parsed = parseColor(color);
+ if (parsed.a === 1) return parsed;
+
+ // Composite with white background
+ const alpha = parsed.a;
+ return {
+ r: (parsed.r * alpha) + (255 * (1 - alpha)),
+ g: (parsed.g * alpha) + (255 * (1 - alpha)),
+ b: (parsed.b * alpha) + (255 * (1 - alpha)),
+ a: 1
+ };
+ };
+
+ const rgb1 = parseAndComposite(color1);
+ const rgb2 = parseAndComposite(color2);
+
+ const lum1 = luminance(rgb1.r, rgb1.g, rgb1.b);
+ const lum2 = luminance(rgb2.r, rgb2.g, rgb2.b);
+
return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05);
}
@@ -92,9 +194,13 @@
if (!accessible) classes.push("failing");
if (level === "AAA" && showGlow) classes.push("glowing-border");
+ // Parse colors but use original format for display
+ const parsed1 = parseColor(color1);
+ const parsed2 = parseColor(color2);
+
return `
-
- ${color1} on ${color2}
+
+ ${parsed1.originalFormat} on ${parsed2.originalFormat}
Ratio: ${contrast} ${accessible ? "✅" : "❌"}
WCAG: ${level}
@@ -103,29 +209,52 @@
function updateColors() {
const input = document.getElementById("colors").value;
- const colors = input.split(/[\n,]+/).map(c => c.trim()).filter(c => c);
+
+ // First split by newlines
+ const colorLines = input.split(/\n+/).map(line => line.trim()).filter(line => line);
+
+ // Then process each line to handle both comma-separated values and rgb/rgba formats
+ const colors = colorLines.flatMap(line => {
+ // Temporary replace commas in rgb/rgba values with a special marker
+ line = line.replace(/rgba?\([^)]+\)/g, match => match.replace(/,/g, '|'));
+
+ // Split by commas and restore the original commas in rgb/rgba values
+ return line.split(',')
+ .map(color => color.trim().replace(/\|/g, ','))
+ .filter(color => color);
+ });
+
const results = document.getElementById("results");
- const colorCombinations = colors.flatMap((color1, i) =>
- colors.slice(i + 1).map(color2 => {
- const contrast = getContrastRatio(color1, color2).toFixed(2);
- const level = getWCAGLevel(contrast);
- const accessible = level !== "Fail";
- return { color1, color2, contrast, level, accessible };
- })
- );
-
- const sortByContrast = document.getElementById("sortContrast").checked;
- if (sortByContrast) {
- colorCombinations.sort((a, b) => b.contrast - a.contrast);
- }
+ try {
+ const colorCombinations = colors.flatMap((color1, i) =>
+ colors.slice(i + 1).map(color2 => {
+ try {
+ const contrast = getContrastRatio(color1, color2).toFixed(2);
+ const level = getWCAGLevel(contrast);
+ const accessible = level !== "Fail";
+ return { color1, color2, contrast, level, accessible };
+ } catch (error) {
+ console.error(`Error processing colors ${color1} and ${color2}:`, error);
+ return null;
+ }
+ }).filter(combo => combo !== null)
+ );
- const colorBoxes = colorCombinations.map(({ color1, color2, contrast, level, accessible }) => {
- return createColorBox(color1, color2, contrast, level, accessible, false);
- });
+ const sortByContrast = document.getElementById("sortContrast").checked;
+ if (sortByContrast) {
+ colorCombinations.sort((a, b) => b.contrast - a.contrast);
+ }
- results.innerHTML = colorBoxes.join('');
- updateVisibility();
+ const colorBoxes = colorCombinations.map(({ color1, color2, contrast, level, accessible }) => {
+ return createColorBox(color1, color2, contrast, level, accessible, false);
+ });
+
+ results.innerHTML = colorBoxes.join('');
+ updateVisibility();
+ } catch (error) {
+ results.innerHTML = `
Error: ${error.message}
`;
+ }
}
function updateVisibility() {
--
cgit 1.4.1-2-gfad0