Markdown inline code (codespans) ignore custom theme overrides — make the color themeable
Summary
Inline code in Claude's markdown output (` file names , variables , quoted values , etc.) is rendered with a color that **cannot be changed by a custom theme's overrides**. The renderer reads the color from the fixed *base* palette by name, bypassing the merged theme entirely. There is also no dedicated theme key for inline code — it reuses permission`.
Current behavior (v2.1.199)
The codespan handler resolves its color like this:
case "codespan": return xo("permission", t)(e.text)
// xo(key, t) -> eLe(text, mJ(t)[key], ...)
// mJ(name): switch(name){ case "dark-ansi": return <fixed palette>; ... }
t is the base theme name (e.g. "dark-ansi"), so the color comes from mJ("dark-ansi").permission, i.e. the hardcoded base value. A custom theme like:
{ "name": "mytheme", "base": "dark-ansi", "overrides": { "permission": "ansi:cyan" } }
loads correctly (the file is read at startup) and the override does apply to UI chrome such as permission prompts — but the markdown renderer never consults the merged theme, so inline code stays the base color.
Impact
- Inline code color is effectively un-themeable.
- It's tied to
permission, so it can't be distinguished from the permission-prompt accent color. - On terminals with a non-standard ANSI palette this is surprising — e.g. base
permission=ansi:blue, rendered bold, resolves to the terminal's intense-blue slot, which may not match the user's intent and can't be corrected in-app.
Requested change (either would resolve it)
- Pass the merged/override-applied theme to the markdown renderer so
overridesaffect codespans (and other markdown-rendered colors); and/or - Add a dedicated theme key (e.g.
codeSpan/inlineCode) so inline code can be colored independently ofpermission.
Environment
Claude Code 2.1.199, Linux, TERM=rxvt-unicode-256color, custom dark-ansi-based theme.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Confirming this on 2.1.211 (macOS, custom theme based on
light), with one additional detail: for custom themes the fall-through is worse than "base palette" — it resolves to the dark table regardless of the theme's declaredbase.In the 2.1.211 bundle the chain is (identifiers differ from the 2.1.199 report but the structure is identical):
there is the theme name. For a custom theme ("theme": "custom:rose-pine-dawn") the name matches no case, soHVreturns the dark table and inline code renders as dark'spermission=rgb(177,185,249)— a bright periwinkle — even though the theme declares"base": "light"and overrides"permission". On a light background this is the most visually broken markdown element in an otherwise fully-overridden theme.So there are two layered problems:
switchdefault, so custom themes don't even inherit their declared base's value — they always get dark's.No workaround exists on the user side: the color is emitted as a truecolor SGR sequence, so terminal palette remapping can't intercept it either.
Repro:
Set
"theme": "custom:repro", have Claude emit `inline code: permission prompts render#907aa9, inline code rendersrgb(177,185,249)`.Confirming this is still present in v2.1.220 on macOS (issue is currently labelled
platform:linux, but it's platform-independent — the colour is baked in by the renderer, not negotiated with the terminal).Two things that might help whoever picks this up.
The merged palette is already available at the call site
The theme context provider exposes both forms side by side:
The markdown path takes the first one — callers do
const [cXf] = is()(which returnscurrentTheme) and pass it down throughyvr(content, cXf, highlight)→f$(token, t)→to("permission", t). Soto()receives a base name and re-derives the palette withtY(t), discarding the overrides thatresolvedThemealready applied.to()also already accepts literal colour strings:so threading
resolvedThemethrough instead ofcurrentTheme(and lettingto()usetdirectly when it's already a palette object rather than a name) looks sufficient.The blast radius is one key
In 2.1.220,
permissionis the only theme key still resolved inside the markdown renderer — every other bit of markdown styling inf$()goes throughbold/dim/italic/strikethrough, which carry no colour. So this is narrower than the older #52465, whose other slots (text,error,warning,success, …) appear to have been fixed already; codespans are the last one on that path.Workaround for anyone hitting this now
Since the colour comes from
tY(base), thebaseyou declare does still control it. Picking an ANSI base (dark-ansi/light-ansi) makespermissionresolve toansi:blueBright, which defers to the terminal's own palette instead of hardcoding the built-indarkperiwinklergb(177,185,249). That's not themeable per-se, but for anyone generating Claude Code themes from an existing terminal colour scheme it at least keeps inline code inside the scheme's palette. An rgb base (dark/light) cannot be corrected at all.Agreed on the dedicated
inlineCodekey in the request above — sharingpermissionwith the permission-prompt accent means you can't give inline code its own colour even once overrides work.Still reproduces on 2.1.260 (issue was filed against 2.1.199), on a different base preset and a different terminal — so this isn't specific to dark-ansi or to unusual ANSI palettes.
Environment: Claude Code 2.1.260, WSL2 (Ubuntu) on Windows Terminal, TERM=xterm-256color, COLORTERM=truecolor, custom theme with "base": "dark".
The code path is unchanged, only the minified identifier differs:
// 2.1.199 (original report)
case "codespan": return xo("permission", t)(e.text)
// 2.1.260
case "codespan": return ut("permission", t)(e.text)
Isolating it: I set 57 of the 72 documented tokens to #FF0000 in an active custom theme (confirmed selected via /theme → Using custom theme "Vesper"). Every other piece of UI text turned red; inline code kept its
default blue. So the merged theme is applied correctly everywhere except this path — which matches the diagnosis in the original report.
One detail that might help narrow it down: sampling the rendered inline code from a screenshot gives ≈ #B3DCFF. With "base": "dark" I'd expect permission = rgb(177,185,249) (#B1B9F9) from the bundled palette, but the
green channel is well off. The closest value I can find in the 2.1.260 bundle is rgb(183,224,255) (#B7E0FF), which appears as permissionShimmer in one of the presets. I'm not confident enough to call this a second
bug — it may just be bold rendering — but if the codespan path resolves against a preset or variant other than the one the theme declares, that would be worth checking alongside the main fix.
Either requested change in the original post would resolve it for me; a dedicated inlineCode token would be the more useful of the two, since inline code appears constantly in normal output while permission is a
dialog accent, and the two shouldn't have to share a color.