← Files Themely Design+Style GeneratorARCHIVED FILE
scripts/contrast_ratio.py
1.93 KB · Oct 2, 2026 · 00:06 UTC
#!/usr/bin/env python3
"""Calculate WCAG contrast for one pair of six-digit hexadecimal colors."""
from __future__ import annotations
import argparse
import json
import re
HEX_COLOR_PATTERN = re.compile(r"^#[0-9A-Fa-f]{6}$")
def parse_hex_color(value: str) -> str:
if not HEX_COLOR_PATTERN.fullmatch(value):
raise argparse.ArgumentTypeError(
"colors must use six-digit hexadecimal notation such as #1A2B3C"
)
return value.upper()
def relative_luminance(color: str) -> float:
channels = [int(color[index : index + 2], 16) / 255 for index in (1, 3, 5)]
linear_channels = [
channel / 12.92
if channel <= 0.04045
else ((channel + 0.055) / 1.055) ** 2.4
for channel in channels
]
red, green, blue = linear_channels
return 0.2126 * red + 0.7152 * green + 0.0722 * blue
def contrast_ratio(first_color: str, second_color: str) -> float:
lighter = max(
relative_luminance(first_color),
relative_luminance(second_color),
)
darker = min(
relative_luminance(first_color),
relative_luminance(second_color),
)
return (lighter + 0.05) / (darker + 0.05)
def main() -> None:
parser = argparse.ArgumentParser(
description="Calculate WCAG contrast for two hexadecimal colors."
)
parser.add_argument("foreground", type=parse_hex_color)
parser.add_argument("background", type=parse_hex_color)
arguments = parser.parse_args()
ratio = contrast_ratio(arguments.foreground, arguments.background)
result = {
"foreground": arguments.foreground,
"background": arguments.background,
"ratio": round(ratio, 4),
"aa_normal_text": ratio >= 4.5,
"aa_large_text": ratio >= 3,
"aaa_normal_text": ratio >= 7,
"aaa_large_text": ratio >= 4.5,
"non_text": ratio >= 3,
}
print(json.dumps(result, separators=(",", ":")))
if __name__ == "__main__":
main()
SHA-256: 97793c40a964e91a4e20a255f7f2314080ed14a154e2085e502f1d7c0f6c7c1c