Extension webviews don't receive --vscode-* theme CSS variables — themed extension UI breaks (e.g. Claude Code panel)

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

In upstream VS Code, extension webviews receive the workbench theme tokens as CSS custom properties (–vscode-foreground, --vscode-input-border, --vscode-charts-*, --vscode-widget-border, etc.) via the default injected stylesheet, so extension webview CSS written as var(–vscode-…) themes correctly. In Cursor, extension webviews do not appear to receive these variables (or receive only a partial set), so any extension CSS referencing them resolves to nothing unless the extension ships literal fallback values.

Observed impact in the Claude Code chat panel (before the extension added literal hex fallbacks in ext <=2.1.19x): markdown table borders were invisible (–vscode-inlineChatInput-border, --vscode-input-border, --vscode-widget-border all unresolved), charts rendered colorless (–vscode-charts-orange|blue|green|yellow|purple|red unresolved), and code-block backgrounds were missing. The same extension versions rendered correctly in upstream VS Code.

The extension has since shipped hardcoded fallbacks (anthropics/claude-code#71646), which masks the symptom for that one extension — but the root cause is host-side, and every other extension webview that relies on documented VS Code webview theming is affected the same way. workbench.colorCustomizations also does not reach these webviews, so users have no workaround.

Steps to Reproduce

  1. Install any extension whose webview styles use var(–vscode-…) without literal fallbacks (Claude Code <=2.1.19x is a concrete example).
  2. Open the webview panel.
  3. Run “Developer: Open Webview Developer Tools” and inspect the webview document root.
  4. Check computed values for e.g. --vscode-input-border or --vscode-charts-blue.
  5. Compare with upstream VS Code: there the variables are defined from the active theme; in Cursor they are absent/unresolved.

Expected Behavior

Cursor injects the standard VS Code theme-token custom properties into extension webviews, matching upstream VS Code webview theming behavior — so extension UI (tables, charts, code blocks, focus rings) themes correctly without every extension needing to ship hardcoded fallback colors.

Operating System

Windows 10/11

Version Information

IDE:
Version: 3.9.16 (Windows 11 Pro)
Example extension: anthropic.claude-code 2.1.207 (also reproduced on 2.1.19x and earlier)

Additional Information

Related upstream reports on the extension side: anthropics/claude-code#71646 (fallbacks shipped as mitigation) and anthropics/claude-code#76989 (prompt-input focus ring ignoring focusBorder — same missing-token family). A host-side fix in Cursor would resolve this class for all extensions at once rather than each extension hardcoding colors that ignore the user’s theme.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, thanks for the detailed report. It’s rare to see this level of work.

I went through it point by point, and it looks a bit more complex than just a host-side gap. Webview theming in Cursor goes through the same pipeline as upstream VS Code. All --vscode-* tokens that resolve for the active theme get injected. The key detail is that some tokens in your list are null-default, which means they only get emitted if the active theme explicitly sets them:

  • input.border and widget.border are registered with { dark: null, light: null }. On default themes (dark_modern, light_modern, cursor-*), input.border is defined (#3C3C3C) and gets injected normally.
  • inlineChatInput.border is a token specific to inline chat. From what I can tell, standard built-in themes don’t set it, so it’s very likely unresolved in upstream VS Code too. If you have a case where it resolves in clean VS Code, send it over and I’ll take a separate look.
  • charts.* have non-null defaults, so they always get injected.

So CSS fallback values in the extension (like Claude Code does) aren’t a hack on top of a Cursor bug. It’s exactly what VS Code recommends in the webview theming guide, like var(--vscode-input-border, transparent), because not every token is guaranteed.

To confirm whether there’s an actual Cursor-specific gap, we need an apples-to-apples comparison:

  1. Set the same active theme in Cursor and in VS Code. Start with a default theme, then try your custom one.
  2. In both, use Developer: Open Webview Developer Tools and inspect the internal extension frame, not the outer preload shell.
  3. Send the output of getComputedStyle(document.documentElement).getPropertyValue('--vscode-input-border') (and a couple other tokens from your list) from both editors.

If the token resolves in VS Code on the same theme but is empty in Cursor, that’s a real gap, and I’ll open this up with your repro. Let me know what the comparison shows.

Thanks — you were right, and a live test settled it without needing the side-by-side. On Cursor 3.9.16 / extension 2.1.209, setting workbench.colorCustomizations: { "input.border": "#FF0000", "inlineChatInput.border": "#FF0000" } propagated into the Claude Code webview immediately (tables, tool-call blocks, and the prompt input border all repainted live, no reload). So token injection into extension webviews is working, including colorCustomizations overrides — I withdraw the report.

For the record, the original symptom traced to the extension referencing --vscode-inlineChatInput-border with no CSS fallback — a null-default token that no standard theme sets, so it was unresolved exactly as you predicted it would be in stock VS Code too. Anthropic fixed it with a fallback chain (inlineChatInput.borderinput.borderwidget.border → literal) in v2.1.207. The residual faint table borders on the default theme are Cursor Dark’s own input.border: #F0F0F013 (~7% alpha white) resolving as designed — not an injection gap.

Thanks for the pointer to the null-default registrations — that was the missing piece. This can be closed.

Glad the live test cleared things up. The breakdown in Post #6 is spot on: input.border / widget.border / inlineChatInput.border are null-default tokens, so they only get emitted if the active theme defines them, and that matches upstream VS Code behavior. CSS fallbacks in an extension, like Anthropic did in 2.1.207, are a documented best practice, not a hack around a bug.

Thanks for the thorough write-up. It was nice digging into this together. I’m closing the thread. If you find a true apples-to-apples case where the token resolves in stock VS Code but is empty in Cursor with the same theme, start a new thread and I’ll take a separate look.