Custom theme `overrides` for diff colors are silently ignored (diff keys resolve against base theme, not merged theme)

Resolved 💬 1 comment Opened Jun 26, 2026 by chandlerc Closed Jun 26, 2026

Summary

Custom themes (~/.claude/themes/<slug>.json with { "name", "base", "overrides" }) correctly apply overrides for almost every color key, but diff color overrides have no effect. Overriding diffAdded / diffAddedDimmed / diffRemoved / diffRemovedDimmed / diffAddedWord / diffRemovedWord does nothing — the diff always renders with the base theme's diff palette. Non-diff overrides in the same file (e.g. claude, permission, text) apply correctly, so the theme is loading and merging fine; only the diff render path ignores the merged values.

Environment

  • Claude Code 2.1.187 (native installer build, ~/.local/share/claude/versions/2.1.187)
  • Linux (aarch64), tmux + terminal with truecolor
  • Theme selected via both /theme and settings.json "theme": "custom:<slug>" — same result

Repro

  1. Create ~/.claude/themes/difftest.json:

``json
{
"name": "Diff Test",
"base": "dark",
"overrides": {
"claude": "rgb(255,0,255)",
"diffAdded": "rgb(0,0,255)",
"diffRemoved": "rgb(255,128,0)"
}
}
``

  1. Restart Claude Code (the themes dir is scanned at startup; a mid-session-created dir/file isn't picked up until restart — minor separate annoyance).
  2. Select Diff Test in /theme.
  3. Confirm the claude override took effect (Claude's accent is now magenta).
  4. Trigger a file-edit diff preview (any Edit).

Expected: added lines render with a blue background (rgb(0,0,255)), removed lines orange (rgb(255,128,0)).

Actual: added/removed lines render with the base dark theme's green/red backgrounds. The diff* overrides are ignored, even though claude (same file) is honored.

Root cause (from inspecting the bundled binary — pointers, not exact identifiers)

The theme load + merge path is correct: the loader keeps an override when Object.hasOwn(nj(base), key) && isValidColor(value), and the merge ({...nj(base), ...overrides}-style) keeps the diff keys (the base dark object does contain them). So the merged active theme has the right diff values.

The problem is the diff renderer passes the color as a key name string, not a resolved literal:

// diff line renderer
I = i === "add"
  ? (r ? "diffAddedDimmed" : "diffAdded")
  : (r ? "diffRemovedDimmed" : "diffRemoved");
// ...
<R color={o ? "text" : undefined} backgroundColor={I} dimColor={r}> ... </R>

…and the low-level color resolver resolves bare key names against nj(base) (the static base theme), bypassing the merged overrides:

return (value) => {
  if (!value) return inherited;
  if (value.startsWith("rgb(") || value.startsWith("#")
      || value.startsWith("ansi256(") || value.startsWith("ansi:"))
    return apply(inherited, value);        // literal → used as-is
  return apply(inherited, nj(base)[value]); // KEY NAME → base theme only, no overrides
};

So:

  • Accent colors work because components read them from the merged theme context and pass rgb(...) literals, hitting the literal branch.
  • Diff colors break because the diff component passes raw key names ("diffAdded", "diffAddedWord", …), which hit the nj(base)[value] branch and never see the overrides.

Affected keys

diffAdded, diffAddedDimmed, diffRemoved, diffRemovedDimmed, diffAddedWord, diffRemovedWord (the last two are passed as color="diffAddedWord" etc., same bypass). More generally: any color passed to the resolver as a key name rather than a resolved literal ignores custom-theme overrides — the diff renderer is the visible case.

Suggested fix

Either:

  • Have the diff renderer resolve colors from the merged active theme (useTheme()) and pass rgb(...) literals, like other components; or
  • Make the low-level key-name resolver resolve against the merged active theme (base + overrides) instead of nj(base).

The second is the more general fix, since it closes the bypass for any other key-name color sites.

Impact / workaround

No workaround that achieves arbitrary diff colors: the diff palette is effectively locked to the chosen base theme (dark → green/red, dark-daltonized → blue/dark-red, dark-ansi → ANSI green/red). Users who want custom diff colors (e.g. to match a delta/git-pager Dracula scheme) can't, despite the keys existing and validating in the theme file.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗