{"record":{"id":"0552e8a523ae7467","repo":"Hmbown/CodeWhale","slug":"grayscale-colors-are-all-resolvable-rgb","errorCode":null,"errorMessage":"grayscale colors are all resolvable RGB","messagePattern":"grayscale colors are all resolvable RGB","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/palette/src/tests.rs","lineNumber":1142,"sourceCode":"    }\n    // A painted community theme never gets the exemption.\n    assert!(!theme_uses_terminal_owned_surfaces(&TOKYO_NIGHT_UI_THEME));\n}\n\n#[test]\nfn high_contrast_grayscale_theme_clears_body_floor_on_every_surface() {\n    // The picker tagline claims \"Color-minimal high contrast\" — hold the\n    // grayscale theme to the full body-text floor on every surface, not the\n    // 3:1 secondary-chrome floor.\n    let theme = GRAYSCALE_UI_THEME;\n    for fg in [theme.text_body, theme.text_soft, theme.text_muted] {\n        for bg in [\n            theme.surface_bg,\n            theme.panel_bg,\n            theme.composer_bg,\n            theme.elevated_bg,\n        ] {\n            let ratio = contrast_ratio(fg, bg).expect(\"grayscale colors are all resolvable RGB\");\n            assert!(\n                ratio >= AA_BODY_CONTRAST,\n                \"grayscale {fg:?} on {bg:?} is {ratio}:1, below the {AA_BODY_CONTRAST}:1 floor\",\n            );\n        }\n    }\n}\n\n#[test]\nfn violation_report_names_pair_and_ratio() {\n    let mut bad = TOKYO_NIGHT_UI_THEME;\n    bad.text_muted = bad.surface_bg;\n    let violations = theme_contrast_violations(&bad);\n    // The muted-on-surface color fails against all four text surfaces.\n    let muted_pairs: Vec<_> = violations\n        .iter()\n        .filter(|violation| violation.pair.starts_with(\"text_muted on \"))\n        .collect();","sourceCodeStart":1124,"sourceCodeEnd":1160,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/palette/src/tests.rs#L1124-L1160","documentation":"This is a Rust `Option::expect` panic reading 'grayscale colors are all resolvable RGB': `contrast_ratio(fg, bg)` returned None for a grayscale theme surface, aborting the high-contrast-theme test. The library throws it when contrast_ratio cannot parse one of the theme's colors into RGB — e.g. an unresolved named color, an unparseable hex string, or a non-RGB color representation. Because a grayscale theme is by definition pure RGB values, None here signals the theme builder produced a malformed color.","triggerScenarios":"Calling `contrast_ratio(fg, bg)` where either argument is a Color that fails RGB resolution: a hex string with the wrong length, a color still stored as a symbolic name, or a theme field populated with None/placeholder before resolution. Any grayscale-theme surface/foreground pair that hits that path panics.","commonSituations":"Theme definitions referencing named colors that require a terminal-query step to resolve; a refactor changing surface_bg fields from parsed Rgb to raw strings; copy-paste typos in grayscale hex values like '#1111' instead of '#111111'.","solutions":["Log/inspect which color failed resolution; fix the theme definition so every surface_bg and foreground is a concrete, parseable RGB value.","Make contrast_ratio return Result<Rgb-parse error> so failures name the offending color instead of panicking on Option.","Validate theme colors at construction time (parse once in the theme builder) so invalid values are rejected where they are created, not in contrast tests."],"exampleFix":"// before\nlet ratio = contrast_ratio(fg, bg).expect(\"grayscale colors are all resolvable RGB\");\n// after\nlet ratio = contrast_ratio(fg, bg)\n    .unwrap_or_else(|| panic!(\"unresolvable RGB in grayscale theme: fg={fg:?} bg={bg:?}\"));","handlingStrategy":"validation","validationCode":"fn all_colors_resolvable(theme: &Theme) -> Result<(), String> {\n    [theme.surface_bg, theme.panel_bg, theme.composer_bg, theme.elevated_bg]\n        .iter()\n        .chain(std::iter::once(&theme.text_default))\n        .map(|c| c.to_rgb().ok_or_else(|| format!(\"unresolvable RGB: {c:?}\")))\n        .collect::<Result<Vec<_>, _>>()\n        .map(|_| ())\n}","typeGuard":"fn is_resolvable_rgb(color: &Color) -> bool {\n    color.to_rgb().is_some()\n}","tryCatchPattern":"let ratio = contrast_ratio(fg, bg).unwrap_or_else(|| {\n    panic!(\"contrast_ratio returned None: fg={fg:?} bg={bg:?} is not a resolvable RGB\")\n});","preventionTips":["Parse theme colors to Rgb once at theme construction; reject unresolvable values there.","Validate grayscale hex strings with a regex (#RRGGBB) before storing them in a theme.","Never rely on runtime terminal queries to resolve colors used by contrast checks — resolve or fail early."],"tags":["rust","option-unwrap-panic","accessibility","color-parsing"],"backgroundTag":"internal-invariant-violation","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}