interface ColorRGB { r: number; g: number; b: number; } interface ContrastResult { passes: boolean; ratio: number; requiredRatio: number; } function hexToRgb(hex: string): ColorRGB { const cleanHex = hex.replace('#', ''); const r = parseInt(cleanHex.substring(0, 2), 16); const g = parseInt(cleanHex.substring(2, 4), 16); const b = parseInt(cleanHex.substring(4, 6), 16); return { r, g, b }; } function getLuminance(rgb: ColorRGB): number { const { r, g, b } = rgb; const values = [r, g, b].map(v => { const normalized = v / 255; return normalized <= 0.03928 ? normalized / 12.92 : Math.pow((normalized + 0.055) / 1.055, 2.4); }); return values[0]! * 0.2126 + values[1]! * 0.7152 + values[2]! * 0.0722; } export function calculateContrastRatio(foreground: string, background: string): number { const fgRgb = hexToRgb(foreground); const bgRgb = hexToRgb(background); const fgLuminance = getLuminance(fgRgb); const bgLuminance = getLuminance(bgRgb); const lighter = Math.max(fgLuminance, bgLuminance); const darker = Math.min(fgLuminance, bgLuminance); return (lighter + 0.05) / (darker + 0.05); } export function meetsWCAGStandard( foreground: string, background: string, level: 'AA' | 'AAA' = 'AA', textSize: 'normal' | 'large' = 'normal' ): ContrastResult { const ratio = calculateContrastRatio(foreground, background); const requiredRatio = level === 'AAA' ? (textSize === 'large' ? 4.5 : 7) : (textSize === 'large' ? 3 : 4.5); return { passes: ratio >= requiredRatio, ratio, requiredRatio, }; }