69 lines
2.7 KiB
Python
69 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""WCAG AA contrast ratio verification for period palettes."""
|
|
|
|
def srgb_to_linear(c):
|
|
s = c / 255.0
|
|
return s / 12.92 if s <= 0.04045 else ((s + 0.055) / 1.055) ** 2.4
|
|
|
|
def relative_luminance(hex_color):
|
|
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
|
|
return 0.2126 * srgb_to_linear(r) + 0.7152 * srgb_to_linear(g) + 0.0722 * srgb_to_linear(b)
|
|
|
|
def contrast_ratio(h1, h2):
|
|
l1, l2 = relative_luminance(h1), relative_luminance(h2)
|
|
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)
|
|
|
|
def rgb_distance(h1, h2):
|
|
r1, g1, b1 = int(h1[0:2], 16), int(h1[2:4], 16), int(h1[4:6], 16)
|
|
r2, g2, b2 = int(h2[0:2], 16), int(h2[2:4], 16), int(h2[4:6], 16)
|
|
return ((r1-r2)**2 + (g1-g2)**2 + (b1-b2)**2) ** 0.5
|
|
|
|
# ============ EDIT THESE FOR YOUR PALETTE ============
|
|
DAY_TEXT = "3D2B1F"
|
|
NIGHT_TEXT = "D0D8E0"
|
|
|
|
periods = {
|
|
"DEJEUNER": ("FFA726", "FFE0B2", DAY_TEXT),
|
|
"DOUCHE": ("4DB6AC", "E0F2F1", DAY_TEXT),
|
|
"AVANT-MIDI": ("AED581", "F1F8E9", DAY_TEXT),
|
|
"DINER": ("FFD54F", "FFF9C4", DAY_TEXT),
|
|
"APRES-MIDI": ("FFB74D", "FFE0B2", DAY_TEXT),
|
|
"SOUPER": ("FF8A65", "FFCCBC", DAY_TEXT),
|
|
"SOIREE": ("311B92", "4527A0", NIGHT_TEXT),
|
|
"DODO": ("1A237E", "0D1520", NIGHT_TEXT),
|
|
}
|
|
|
|
period_order = ["DEJEUNER", "DOUCHE", "AVANT-MIDI", "DINER", "APRES-MIDI", "SOUPER", "SOIREE", "DODO"]
|
|
|
|
print("=" * 75)
|
|
print(f"{'Period':<14} {'Text':<8} {'Bg Top':<10} {'Ratio':>6} {'Bg Bot':<10} {'Ratio':>6} {'WCAG':>7}")
|
|
print("=" * 75)
|
|
|
|
all_pass = True
|
|
for name, (top, bottom, text) in periods.items():
|
|
r_top = contrast_ratio(text, top)
|
|
r_bot = contrast_ratio(text, bottom)
|
|
aa_top = "AA+ " if r_top >= 4.5 else "AA-lg" if r_top >= 3.0 else "FAIL!"
|
|
aa_bot = "AA+ " if r_bot >= 4.5 else "AA-lg" if r_bot >= 3.0 else "FAIL!"
|
|
if r_top < 3.0 or r_bot < 3.0:
|
|
all_pass = False
|
|
print(f"{name:<14} #{text} #{top} {r_top:>5.1f}:1 {aa_top} #{bottom} {r_bot:>5.1f}:1 {aa_bot}")
|
|
|
|
print()
|
|
print("=" * 75)
|
|
print("Adjacent period distinction (RGB distance)")
|
|
print("=" * 75)
|
|
|
|
for i in range(len(period_order) - 1):
|
|
curr, next_p = period_order[i], period_order[i+1]
|
|
_, curr_bot, _ = periods[curr]
|
|
next_top, _, _ = periods[next_p]
|
|
dist = rgb_distance(curr_bot, next_top)
|
|
l1, l2 = relative_luminance(curr_bot), relative_luminance(next_top)
|
|
lum_diff = abs(l1 - l2)
|
|
status = "OK" if dist > 80 or lum_diff > 0.15 else "PROCHE" if dist > 40 else "TROP PROCHE!"
|
|
print(f" {curr:<12} bas #{curr_bot} → {next_p:<12} haut #{next_top} dist={dist:5.0f} Δlum={lum_diff:.3f} [{status}]")
|
|
|
|
print()
|
|
print("WCAG AA (4.5:1 normal / 3:1 large):", "ALL PASS" if all_pass else "ISSUES FOUND!")
|