nextlevelbuilder/ui-ux-pro-max-skill · error · ValueError

invalid hex color '{value}'

Error message

invalid hex color '{value}'

What it means

validate_data.py's WCAG contrast helper _relative_luminance() requires an opaque six-digit hex color (#RRGGBB). It fullmatch-es the value against HEX_COLOR (`#[0-9A-Fa-f]{6}`) before slicing channels at offsets 1/3/5; anything else — 3-digit shorthand, missing '#', named colors, 8-digit hex with alpha, empty string — raises this ValueError. It is called via contrast_ratio() on color CSV columns (validate_data.py:393).

Source

Thrown at src/ui-ux-pro-max/scripts/validate_data.py:150

    "threejs": {"threejs.org", "github.com", "www.npmjs.com", "www.w3.org"},
    "laravel": {"laravel.com"},
}
REQUIRED_UX_GUIDANCE = {
    "Focus Not Obscured (Minimum)": "Web",
    "Focus Not Obscured (Enhanced)": "Web",
    "Focus Appearance": "Web",
    "Dragging Movements": "All",
    "Target Size (Minimum)": "Web",
    "Consistent Help": "All",
    "Redundant Entry": "All",
    "Accessible Authentication (Minimum)": "All",
    "Auto-Rotating Content Controls": "All",
}


def _relative_luminance(value):
    if not HEX_COLOR.fullmatch(value or ""):
        raise ValueError(f"invalid hex color '{value}'")
    channels = [int(value[index:index + 2], 16) / 255 for index in (1, 3, 5)]
    linear = [channel / 12.92 if channel <= 0.04045
              else ((channel + 0.055) / 1.055) ** 2.4 for channel in channels]
    return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]


def contrast_ratio(foreground, background):
    """Return WCAG contrast for two opaque six-digit sRGB colors."""
    first, second = _relative_luminance(foreground), _relative_luminance(background)
    return (max(first, second) + 0.05) / (min(first, second) + 0.05)


def _read_rows(filepath):
    with open(filepath, "r", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        return reader.fieldnames or [], list(reader)

View on GitHub (pinned to a38d04c3d5)

Solutions

  1. Find the offending row: the error prints the bad value; grep the color CSVs for it (`rg "FFF'|white|rgba" src/ui-ux-pro-max/data/colors.csv`).
  2. Rewrite the value as full six-digit hex with the leading '#', e.g. '#FFFFFF' not '#FFF' or 'FFF'.
  3. Strip alpha information: '#FFFFFFFF' is invalid — use the 6-digit RGB form; the contrast model assumes opaque colors.
  4. Re-run `python3 src/ui-ux-pro-max/scripts/validate_data.py` to confirm all rows pass, then `npm run sync:assets` in cli/ to mirror the fix.

Example fix

# before (colors.csv)
Dark Theme,Primary,#111,Background,#FFF

# after (colors.csv)
Dark Theme,Primary,#111111,Background,#FFFFFF
Defensive patterns

Strategy: validation

Validate before calling

import re
HEX_COLOR = re.compile(r"#[0-9A-Fa-f]{6}")
def is_opaque_hex(value):
    return bool(value) and HEX_COLOR.fullmatch(value) is not None

# before calling contrast_ratio on CSV rows:
for fg, bg in pairs:
    if not (is_opaque_hex(fg) and is_opaque_hex(bg)):
        raise ValueError(f"row has non-6-digit-hex color: {fg!r}, {bg!r}")

Type guard

def is_opaque_hex(value) -> bool:
    return isinstance(value, str) and bool(re.fullmatch(r"#[0-9A-Fa-f]{6}", value))

Prevention

When it happens

Trigger: A colors.csv row has a Primary/Background/Text column containing e.g. 'FFF' (no #), '#FFF' (3-digit), 'rgba(...)' , 'white', an empty cell, or a value with trailing whitespace — contrast_ratio(row[foreground], row[background]) passes it straight to _relative_luminance and the fullmatch fails.

Common situations: Adding new palettes to colors.csv with hand-typed shorthand hex; copy-pasting colors from design tools that emit 3-digit or 8-digit hex; leaving a column blank for a palette variant.

Related errors


AI-assisted analysis of nextlevelbuilder/ui-ux-pro-max-skill@a38d04c3d5 (2026-08-14). Data as JSON: /api/errors/e43ef150615a1730. Report an issue: GitHub.