{"record":{"id":"e43ef150615a1730","repo":"nextlevelbuilder/ui-ux-pro-max-skill","slug":"invalid-hex-color-value","errorCode":null,"errorMessage":"invalid hex color '{value}'","messagePattern":"invalid hex color '(.+?)'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/ui-ux-pro-max/scripts/validate_data.py","lineNumber":150,"sourceCode":"    \"threejs\": {\"threejs.org\", \"github.com\", \"www.npmjs.com\", \"www.w3.org\"},\n    \"laravel\": {\"laravel.com\"},\n}\nREQUIRED_UX_GUIDANCE = {\n    \"Focus Not Obscured (Minimum)\": \"Web\",\n    \"Focus Not Obscured (Enhanced)\": \"Web\",\n    \"Focus Appearance\": \"Web\",\n    \"Dragging Movements\": \"All\",\n    \"Target Size (Minimum)\": \"Web\",\n    \"Consistent Help\": \"All\",\n    \"Redundant Entry\": \"All\",\n    \"Accessible Authentication (Minimum)\": \"All\",\n    \"Auto-Rotating Content Controls\": \"All\",\n}\n\n\ndef _relative_luminance(value):\n    if not HEX_COLOR.fullmatch(value or \"\"):\n        raise ValueError(f\"invalid hex color '{value}'\")\n    channels = [int(value[index:index + 2], 16) / 255 for index in (1, 3, 5)]\n    linear = [channel / 12.92 if channel <= 0.04045\n              else ((channel + 0.055) / 1.055) ** 2.4 for channel in channels]\n    return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]\n\n\ndef contrast_ratio(foreground, background):\n    \"\"\"Return WCAG contrast for two opaque six-digit sRGB colors.\"\"\"\n    first, second = _relative_luminance(foreground), _relative_luminance(background)\n    return (max(first, second) + 0.05) / (min(first, second) + 0.05)\n\n\ndef _read_rows(filepath):\n    with open(filepath, \"r\", encoding=\"utf-8\") as f:\n        reader = csv.DictReader(f)\n        return reader.fieldnames or [], list(reader)\n\n","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/blob/a38d04c3d5c298c851dbe5e6ee1965ee3de42cb5/src/ui-ux-pro-max/scripts/validate_data.py#L132-L168","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["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`).","Rewrite the value as full six-digit hex with the leading '#', e.g. '#FFFFFF' not '#FFF' or 'FFF'.","Strip alpha information: '#FFFFFFFF' is invalid — use the 6-digit RGB form; the contrast model assumes opaque colors.","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."],"exampleFix":"# before (colors.csv)\nDark Theme,Primary,#111,Background,#FFF\n\n# after (colors.csv)\nDark Theme,Primary,#111111,Background,#FFFFFF","handlingStrategy":"validation","validationCode":"import re\nHEX_COLOR = re.compile(r\"#[0-9A-Fa-f]{6}\")\ndef is_opaque_hex(value):\n    return bool(value) and HEX_COLOR.fullmatch(value) is not None\n\n# before calling contrast_ratio on CSV rows:\nfor fg, bg in pairs:\n    if not (is_opaque_hex(fg) and is_opaque_hex(bg)):\n        raise ValueError(f\"row has non-6-digit-hex color: {fg!r}, {bg!r}\")","typeGuard":"def is_opaque_hex(value) -> bool:\n    return isinstance(value, str) and bool(re.fullmatch(r\"#[0-9A-Fa-f]{6}\", value))","tryCatchPattern":null,"preventionTips":["Mandate #RRGGBB in contributing docs for all color columns; reject shorthand at review.","Run validate_data.py after every colors.csv edit, before sync:assets.","Design-tool exports: normalize 3-digit to 6-digit (#abc -> #aabbcc) in your ingestion script."],"tags":["validation","wcag","color","csv","regex"],"backgroundTag":null,"analyzedSha":"a38d04c3d5c298c851dbe5e6ee1965ee3de42cb5","analyzedAt":"2026-08-14T18:51:02.321Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}