151 lines · 6.5 KB
Raw Download
1
import colorsys
2
from math import pow
3
from typing import Tuple
4
from datetime import datetime
5
6
# Function to calculate relative luminance of a color
7
def relative_luminance(rgb: Tuple[int, int, int]) -> float:
8
    def adjust(c: float) -> float:
9
        c = c / 255.0
10
        return c / 12.92 if c <= 0.03928 else pow((c + 0.055) / 1.055, 2.4)
11
12
    r, g, b = rgb
13
    return 0.2126 * adjust(r) + 0.7152 * adjust(g) + 0.0722 * adjust(b)
14
15
# Function to calculate contrast ratio
16
def contrast_ratio(foreground: Tuple[int, int, int], background: Tuple[int, int, int]) -> float:
17
    lum1 = relative_luminance(foreground)
18
    lum2 = relative_luminance(background)
19
    if lum1 > lum2:
20
        return (lum1 + 0.05) / (lum2 + 0.05)
21
    return (lum2 + 0.05) / (lum1 + 0.05)
22
23
# Generate HTML table
24
def generate_html(colors: list):
25
    html = '<style>* {font-family: sans-serif; text-shadow: none;} .textwhite {color: white;} .textblack {color: black;} .bgwhite.result:hover {text-shadow: 1px 1px 1px #000000;} .bgblack.result:hover {text-shadow: 1px 1px 1px #ffffff;} .label {padding: 0.5em; display: block;} .pass100 {color: #000000; background-color: #9cffac;} .pass80 {color: #000000; background-color: #f2ff9c;} .pass60 {color: #000000; background-color: #ffdb9c} .fail {color: #000000; background-color: #ff9c9c;}</style>'
26
    
27
    # Helper function to determine text color
28
    def get_text_color(hex_color):
29
        rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
30
        return "textwhite" if relative_luminance(rgb) < 0.5 else "textblack"
31
32
    # Helper function to determine text-shadow color
33
    def get_text_shadow(hex_color):
34
        rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
35
        return "bgwhite" if relative_luminance(rgb) >= 0.5 else "bgblack"
36
37
    # Store passing combinations for summary table
38
    summary_data = {bg[1]: {"name": bg[0], "AAA": [], "AA": [], "AALarge": []} for bg in colors}
39
40
    # Top row
41
    html += '<h2>Main Contrast Table</h2>'
42
    html += '<table border="0" style="border-collapse: collapse;">'
43
    html += '<tr><th style="color: black;">BG &#8594;<br>FG &#8595;</th>'
44
    for bg in colors:
45
        text_color = get_text_color(bg[1])
46
        html += f'<th class="{text_color}" style="background-color: {bg[1]};">{bg[0]}<br>{bg[1]}</th>'
47
    html += '</tr>'
48
    
49
    # Rows for each foreground color
50
    for fg in colors:
51
        text_color = get_text_color(fg[1])
52
        html += f'<tr><td class="{text_color}" style="background-color: {fg[1]};">{fg[0]}<br>{fg[1]}</td>'
53
        for bg in colors:
54
            rgb_fg = tuple(int(fg[1].lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
55
            rgb_bg = tuple(int(bg[1].lstrip('#')[i:i+2], 16) for i in (0, 2, 4))
56
            ratio = round(contrast_ratio(rgb_fg, rgb_bg), 3)
57
58
            # Compliance check and store in summary data
59
            if ratio >= 7:
60
                summary_data[bg[1]]["AAA"].append((fg[0], fg[1], ratio))
61
            elif ratio >= 4.5:
62
                summary_data[bg[1]]["AA"].append((fg[0], fg[1], ratio))
63
            elif ratio >= 3:
64
                summary_data[bg[1]]["AALarge"].append((fg[0], fg[1], ratio))
65
            
66
            compliance = (
67
                "Pass - 100% (AAA &ge; 7.0)" if ratio >= 7 else
68
                "Pass - 80% (AA Normal &ge; 4.5)" if ratio >= 4.5 else
69
                "Pass - 60% (AA Large &ge; 3.0)" if ratio >= 3 else
70
                "Fail - 0% (< 3.0)"
71
            )
72
            labelclass = (
73
                "pass100" if ratio >= 7 else
74
                "pass80" if ratio >= 4.5 else
75
                "pass60" if ratio >= 3 else
76
                "fail"
77
            )
78
            text_shadow = get_text_shadow(bg[1])
79
            html += (
80
                f'<td style="background-color: {bg[1]}; color: {fg[1]}; padding: 1em;">'
81
                f'<strong class="result {text_shadow}">{fg[0]} over {bg[0]}</strong>'
82
                f'<br><span class="label {labelclass}">{compliance} with ratio {ratio}</span>'
83
                f'</td>'
84
            )
85
        html += '</tr>'
86
    
87
    html += '</table>'
88
    
89
    # Generate summary table
90
    html += '<h2>Summary Table: Passing Combinations by Category</h2>'
91
    html += '<table border="1" style="border-collapse: collapse;">'
92
    html += '<tr><th style="color: black;">Background</th>'
93
    html += '<th>AAA &ge; 7.0</th><th>AA Normal &ge; 4.5</th><th>AA Large &ge; 3.0</th></tr>'
94
95
    for bg_hex, bg_data in summary_data.items():
96
        html += f'<tr><td style="background-color: {bg_hex}; padding: 1em;" class="{get_text_color(bg_hex)}">{bg_data["name"]}<br>{bg_hex}</td>'
97
        
98
        # AAA Column
99
        html += f'<td style="background-color: {bg_hex}">'
100
        for fg_name, fg_hex, ratio in sorted(bg_data["AAA"], key=lambda x: x[2], reverse=True):
101
            html += f'<div style="background-color: {bg_hex}; color: {fg_hex}; padding: 0.5em;">{fg_name} (Ratio: {ratio})</div>'
102
        html += '</td>'
103
        
104
        # AA Column
105
        html += f'<td style="background-color: {bg_hex}">'
106
        for fg_name, fg_hex, ratio in sorted(bg_data["AA"], key=lambda x: x[2], reverse=True):
107
            html += f'<div style="background-color: {bg_hex}; color: {fg_hex}; padding: 0.5em;">{fg_name} (Ratio: {ratio})</div>'
108
        html += '</td>'
109
        
110
        # AA Large Column
111
        html += f'<td style="background-color: {bg_hex}">'
112
        for fg_name, fg_hex, ratio in sorted(bg_data["AALarge"], key=lambda x: x[2], reverse=True):
113
            html += f'<div style="background-color: {bg_hex}; color: {fg_hex}; padding: 0.5em; font-size: 1.5em;">{fg_name} (Ratio: {ratio})</div>'
114
        html += '</td>'
115
        
116
        html += '</tr>'
117
118
    html += '</table>'
119
    return html
120
121
# Color palette with names and hex codes
122
colors = [
123
    ("Pure White", "#FFFFFF"),
124
    ("Pure Black", "#000000"),
125
    ("Maroon", "#8C2531"),
126
    ("Black", "#222222"),
127
    ("Dark Gray", "#404040"),
128
    ("Medium Gray", "#7F7F7F"),
129
    ("Light Gray", "#BFBFBF"),
130
    ("Lightest Gray", "#F6F6F6"),
131
    ("Tan", "#ADA28C"),
132
    ("Dark Blue", "#002A3B"),
133
    ("Dark Green", "#225A41"),
134
    ("Blue", "#005E84"),
135
    ("Green", "#7E9A4B"),
136
    ("Orange", "#B2461F"),
137
    ("Yellow", "#D88A00"),
138
    ("Purple", "#3C2D57")
139
]
140
141
html_output = generate_html(colors)
142
143
# Generate timestamp for filename
144
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
145
filename = f"color_contrast_grid_{timestamp}.html"
146
147
# Write to an HTML file
148
with open(filename, "w") as file:
149
    file.write(html_output)
150
151
print(f"HTML file '{filename}' generated.")