prototype(#53): palette candidates as runtime token swaps
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>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#!/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}")
|
||||
Reference in New Issue
Block a user