← Files Themely Design+Style GeneratorARCHIVED FILE

scripts/contrast_ratio.py

1.93 KB · Oct 2, 2026 · 00:06 UTC

↓ Download file

#!/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