Throwaway. Tokenises the theme's literal colours so candidate palettes can be swapped in the browser, adds three candidates (Plum Noir one-metal, two-metal pewter, two-metal plus a champagne inset), a WCAG checker, a seeder and the screenshots they are judged on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
79 lines
2.2 KiB
Python
79 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Throwaway WCAG 2.x contrast check for the #53 palette candidates.
|
|
|
|
Same method as the #50 headroom research: relative luminance, (L1+.05)/(L2+.05),
|
|
alpha colours flattened over their real backdrop first (source-over, sRGB).
|
|
Self-checks against WCAG's published values before printing anything.
|
|
"""
|
|
|
|
|
|
def _srgb(c):
|
|
c /= 255
|
|
return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
|
|
|
|
|
|
def rgb(h):
|
|
h = h.lstrip("#")
|
|
return tuple(int(h[i : i + 2], 16) for i in (0, 2, 4))
|
|
|
|
|
|
def lum(h):
|
|
r, g, b = rgb(h)
|
|
return 0.2126 * _srgb(r) + 0.7152 * _srgb(g) + 0.0722 * _srgb(b)
|
|
|
|
|
|
def flatten(fg, alpha, bg):
|
|
f, b = rgb(fg), rgb(bg)
|
|
return "#%02x%02x%02x" % tuple(round(f[i] * alpha + b[i] * (1 - alpha)) for i in range(3))
|
|
|
|
|
|
def ratio(a, b):
|
|
la, lb = lum(a), lum(b)
|
|
hi, lo = max(la, lb), min(la, lb)
|
|
return (hi + 0.05) / (lo + 0.05)
|
|
|
|
|
|
def _selfcheck():
|
|
assert round(ratio("#000000", "#ffffff"), 1) == 21.0
|
|
assert round(ratio("#767676", "#ffffff"), 2) == 4.54
|
|
assert round(ratio("#949494", "#ffffff"), 2) == 3.03
|
|
assert ratio("#959595", "#ffffff") < 3.0
|
|
assert flatten("#ffffff", 0.5, "#000000") == "#808080"
|
|
|
|
|
|
_selfcheck()
|
|
|
|
# candidate surfaces (the invented ones need proving; the rest come from #50)
|
|
SURFACES = {
|
|
"bg #150E13": "#150E13",
|
|
"bg-deep #0C0810": "#0C0810",
|
|
"panel #21161C": "#21161C",
|
|
"panel-2 #180F16": "#180F16",
|
|
"panel-3 / Plum Noir #351E28": "#351E28",
|
|
"plum-soft #4A2A38": "#4A2A38",
|
|
"paper / champagne #EFE5D2": "#EFE5D2",
|
|
}
|
|
|
|
INKS = {
|
|
"Bone #E6E0D6": "#E6E0D6",
|
|
"Ash #B9B2A6": "#B9B2A6",
|
|
"Dust #9A9086": "#9A9086",
|
|
"Ledger Ink #D8C28A": "#D8C28A",
|
|
"Tarnished Gold #C2A35A": "#C2A35A",
|
|
"Old Gilt #A8893F": "#A8893F",
|
|
"Gold Lit #E0C878": "#E0C878",
|
|
"Pewter #9EA7B3": "#9EA7B3",
|
|
"Ink-on-light #351E28": "#351E28",
|
|
"Red #8E3438": "#8E3438",
|
|
"Stamp #A84A4E": "#A84A4E",
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
w = max(len(k) for k in INKS)
|
|
for sname, s in SURFACES.items():
|
|
print(f"\n== {sname}")
|
|
for iname, i in INKS.items():
|
|
r = ratio(i, s)
|
|
tag = "AA" if r >= 4.5 else ("large/non-text" if r >= 3.0 else "FAIL")
|
|
print(f" {iname:<{w}} {r:6.2f} {tag}")
|