import colorsys from math import pow from typing import Tuple from datetime import datetime # Function to calculate relative luminance of a color def relative_luminance(rgb: Tuple[int, int, int]) -> float: def adjust(c: float) -> float: c = c / 255.0 return c / 12.92 if c <= 0.03928 else pow((c + 0.055) / 1.055, 2.4) r, g, b = rgb return 0.2126 * adjust(r) + 0.7152 * adjust(g) + 0.0722 * adjust(b) # Function to calculate contrast ratio def contrast_ratio(foreground: Tuple[int, int, int], background: Tuple[int, int, int]) -> float: lum1 = relative_luminance(foreground) lum2 = relative_luminance(background) if lum1 > lum2: return (lum1 + 0.05) / (lum2 + 0.05) return (lum2 + 0.05) / (lum1 + 0.05) # Generate HTML table def generate_html(colors: list): html = '' # Helper function to determine text color def get_text_color(hex_color): rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) return "textwhite" if relative_luminance(rgb) < 0.5 else "textblack" # Helper function to determine text-shadow color def get_text_shadow(hex_color): rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) return "bgwhite" if relative_luminance(rgb) >= 0.5 else "bgblack" # Store passing combinations for summary table summary_data = {bg[1]: {"name": bg[0], "AAA": [], "AA": [], "AALarge": []} for bg in colors} # Top row html += '

Main Contrast Table

' html += '' html += '' for bg in colors: text_color = get_text_color(bg[1]) html += f'' html += '' # Rows for each foreground color for fg in colors: text_color = get_text_color(fg[1]) html += f'' for bg in colors: rgb_fg = tuple(int(fg[1].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) rgb_bg = tuple(int(bg[1].lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) ratio = round(contrast_ratio(rgb_fg, rgb_bg), 3) # Compliance check and store in summary data if ratio >= 7: summary_data[bg[1]]["AAA"].append((fg[0], fg[1], ratio)) elif ratio >= 4.5: summary_data[bg[1]]["AA"].append((fg[0], fg[1], ratio)) elif ratio >= 3: summary_data[bg[1]]["AALarge"].append((fg[0], fg[1], ratio)) compliance = ( "Pass - 100% (AAA ≥ 7.0)" if ratio >= 7 else "Pass - 80% (AA Normal ≥ 4.5)" if ratio >= 4.5 else "Pass - 60% (AA Large ≥ 3.0)" if ratio >= 3 else "Fail - 0% (< 3.0)" ) labelclass = ( "pass100" if ratio >= 7 else "pass80" if ratio >= 4.5 else "pass60" if ratio >= 3 else "fail" ) text_shadow = get_text_shadow(bg[1]) html += ( f'' ) html += '' html += '
BG →
FG ↓
{bg[0]}
{bg[1]}
{fg[0]}
{fg[1]}
' f'{fg[0]} over {bg[0]}' f'
{compliance} with ratio {ratio}' f'
' # Generate summary table html += '

Summary Table: Passing Combinations by Category

' html += '' html += '' html += '' for bg_hex, bg_data in summary_data.items(): html += f'' # AAA Column html += f'' # AA Column html += f'' # AA Large Column html += f'' html += '' html += '
BackgroundAAA ≥ 7.0AA Normal ≥ 4.5AA Large ≥ 3.0
{bg_data["name"]}
{bg_hex}
' for fg_name, fg_hex, ratio in sorted(bg_data["AAA"], key=lambda x: x[2], reverse=True): html += f'
{fg_name} (Ratio: {ratio})
' html += '
' for fg_name, fg_hex, ratio in sorted(bg_data["AA"], key=lambda x: x[2], reverse=True): html += f'
{fg_name} (Ratio: {ratio})
' html += '
' for fg_name, fg_hex, ratio in sorted(bg_data["AALarge"], key=lambda x: x[2], reverse=True): html += f'
{fg_name} (Ratio: {ratio})
' html += '
' return html # Color palette with names and hex codes colors = [ ("Pure White", "#FFFFFF"), ("Pure Black", "#000000"), ("Maroon", "#8C2531"), ("Black", "#222222"), ("Dark Gray", "#404040"), ("Medium Gray", "#7F7F7F"), ("Light Gray", "#BFBFBF"), ("Lightest Gray", "#F6F6F6"), ("Tan", "#ADA28C"), ("Dark Blue", "#002A3B"), ("Dark Green", "#225A41"), ("Blue", "#005E84"), ("Green", "#7E9A4B"), ("Orange", "#B2461F"), ("Yellow", "#D88A00"), ("Purple", "#3C2D57") ] html_output = generate_html(colors) # Generate timestamp for filename timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") filename = f"color_contrast_grid_{timestamp}.html" # Write to an HTML file with open(filename, "w") as file: file.write(html_output) print(f"HTML file '{filename}' generated.")