Themes custom WPF UI in Visual Studio extensions to match the current IDE color theme (Light, Dark, Blue, High Contrast). Covers VSIX Community Toolkit, VisualStudio.Extensibility, and legacy VSSDK approaches.
How this skill is triggered — by the user, by Claude, or both
Slash command
/vs-extensibility-skills:theming-extension-uiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Any custom WPF UI (tool windows, dialogs, user controls) must respect the active Visual Studio color theme. Without theming, your UI will look broken in Dark or High Contrast themes. Visual Studio ships Light, Dark, Blue, and High Contrast themes — your extension should work in all of them.
Any custom WPF UI (tool windows, dialogs, user controls) must respect the active Visual Studio color theme. Without theming, your UI will look broken in Dark or High Contrast themes. Visual Studio ships Light, Dark, Blue, and High Contrast themes — your extension should work in all of them.
Rule #1: Never hard-code hex or RGB colors. Always use theme-aware resource keys or the toolkit's auto-theming.
Proper theming is a requirement for VS Marketplace quality — extensions with hard-coded colors look broken in any theme other than the one the developer tested with. VS provides multiple levels of theming support: the Toolkit's UseVsTheme for zero-effort auto-theming, EnvironmentColors resource keys for fine-grained control, and the VSColorTheme.ThemeChanged event for runtime updates.
When to use this vs. alternatives:
Themes.UseVsThemeThe simplest approach. A single attached property applies official VS styling to all standard WPF controls in the visual tree.
NuGet package: Community.VisualStudio.Toolkit
Key namespace: Community.VisualStudio.Toolkit
<UserControl x:Class="MyExtension.MyToolWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
toolkit:Themes.UseVsTheme="True">
<StackPanel Margin="10">
<TextBlock Text="Hello, themed world!" />
<TextBox Margin="0,5" />
<Button Content="Click Me" />
</StackPanel>
</UserControl>
toolkit:Themes.UseVsTheme="True" automatically styles all child controls (buttons, text boxes, labels, list boxes, etc.) using the current VS theme. When the user switches themes, the UI updates immediately — no reload needed.
For standalone dialog windows, use Microsoft.VisualStudio.PlatformUI.DialogWindow as the base and apply the same attribute:
<platform:DialogWindow
x:Class="MyExtension.MyDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:platform="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
toolkit:Themes.UseVsTheme="True"
Title="My Dialog"
Width="400"
Height="300">
<StackPanel Margin="10">
<TextBlock Text="This dialog is themed." />
</StackPanel>
</platform:DialogWindow>
UseVsTheme="True" handles standard WPF controls (buttons, text boxes, labels, list boxes, etc.), but it does not cover every scenario. You will need explicit EnvironmentColors or VsBrushes bindings when:
UseVsTheme doesn't restyleSince toolkit extensions run in-process, they have full access to all the VSSDK theming classes described in the next section. A typical toolkit control combines both:
<UserControl x:Class="MyExtension.MyToolWindowControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
xmlns:platformUI="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:shell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
toolkit:Themes.UseVsTheme="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header with explicit Info Bar background -->
<Border Grid.Row="0"
Background="{DynamicResource {x:Static platformUI:EnvironmentColors.InfoBarBackgroundBrushKey}}"
Padding="8">
<TextBlock Text="Status"
Foreground="{DynamicResource {x:Static platformUI:EnvironmentColors.InfoBarTextBrushKey}}" />
</Border>
<!-- Separator using environment border color -->
<Border Grid.Row="0" VerticalAlignment="Bottom" Height="1"
Background="{DynamicResource {x:Static platformUI:EnvironmentColors.ToolWindowBorderBrushKey}}" />
<!-- Main content — standard controls auto-themed by UseVsTheme -->
<StackPanel Grid.Row="1" Margin="10">
<TextBlock Text="Search:" />
<TextBox Margin="0,5" />
<ListBox Margin="0,5" />
<Button Content="Run" HorizontalAlignment="Left" />
</StackPanel>
</Grid>
</UserControl>
In code-behind, toolkit extensions can also use VSColorTheme to read colors and respond to theme changes:
using Microsoft.VisualStudio.PlatformUI;
// Read a themed color
var bg = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey);
// React to theme switches
VSColorTheme.ThemeChanged += (e) => RefreshCustomDrawing();
The VSSDK provides several key classes for theme integration. These classes are also available to VSIX Community Toolkit extensions — the toolkit is built on top of the VSSDK.
NuGet package: Microsoft.VisualStudio.SDK
Key namespaces: Microsoft.VisualStudio.PlatformUI, Microsoft.VisualStudio.Shell
EnvironmentColors provides ResourceKey properties for every standard VS UI color. Use them as DynamicResource bindings in XAML so colors update automatically on theme changes.
<UserControl xmlns:platformUI="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
<Grid Background="{DynamicResource {x:Static platformUI:EnvironmentColors.ToolWindowBackgroundBrushKey}}">
<TextBlock
Text="Themed text"
Foreground="{DynamicResource {x:Static platformUI:EnvironmentColors.ToolWindowTextBrushKey}}" />
</Grid>
</UserControl>
Commonly used EnvironmentColors keys:
| Key | Usage |
|---|---|
ToolWindowBackgroundBrushKey | Tool window background |
ToolWindowTextBrushKey | Tool window text |
ToolWindowBorderBrushKey | Tool window borders |
CommandBarMenuBackgroundGradientBrushKey | Menu backgrounds |
CommandBarTextActiveBrushKey | Active command text |
PanelHyperlinkBrushKey | Hyperlink text |
AccentMediumBrushKey | Accent borders |
ControlEditHintTextBrushKey | Watermark/hint text |
SystemButtonFaceColorKey | Dialog button faces |
Color vs Brush keys: Most properties come in pairs — *ColorKey (returns a Color) and *BrushKey (returns a SolidColorBrush). Use BrushKey for XAML bindings.
VsBrushes is an older static class that exposes ResourceKey objects for environment brushes. It predates EnvironmentColors but is still functional. Prefer EnvironmentColors for new code as it has a wider set of keys.
<UserControl xmlns:shell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0">
<Border Background="{DynamicResource {x:Static shell:VsBrushes.ToolWindowBackgroundKey}}">
<TextBlock
Text="Using VsBrushes"
Foreground="{DynamicResource {x:Static shell:VsBrushes.ToolWindowTextKey}}" />
</Border>
</UserControl>
Commonly used VsBrushes keys:
| Key | Usage |
|---|---|
ToolWindowBackgroundKey | Tool window background |
ToolWindowTextKey | Tool window text |
WindowKey | General window background |
WindowTextKey | General window text |
CaptionTextKey | Title/caption text |
ToolWindowBorderKey | Tool window border |
CommandBarGradientBeginKey | Command bar gradient start |
EnvironmentBackgroundKey | IDE background |
If you need System.Drawing.Color values (e.g., for WinForms controls or custom painting), use VsColors:
using Microsoft.VisualStudio.Shell;
System.Drawing.Color bgColor = VsColors.GetThemedGDIColor(
EnvironmentColors.ToolWindowBackgroundColorKey);
VSColorTheme provides cached System.Drawing.Color lookups and a ThemeChanged event for responding to theme switches in code-behind:
using Microsoft.VisualStudio.PlatformUI;
public MyControl()
{
InitializeComponent();
VSColorTheme.ThemeChanged += OnThemeChanged;
ApplyThemeColors();
}
private void OnThemeChanged(ThemeChangedEventArgs e)
{
ApplyThemeColors();
}
private void ApplyThemeColors()
{
var bgColor = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey);
var fgColor = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowTextColorKey);
this.Background = new SolidColorBrush(Color.FromArgb(bgColor.A, bgColor.R, bgColor.G, bgColor.B));
this.Foreground = new SolidColorBrush(Color.FromArgb(fgColor.A, fgColor.R, fgColor.G, fgColor.B));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
VSColorTheme.ThemeChanged -= OnThemeChanged;
}
base.Dispose(disposing);
}
Bind to the environment font so your UI respects the user's font settings:
<TextBlock
FontFamily="{DynamicResource {x:Static shell:VsFonts.EnvironmentFontFamilyKey}}"
FontSize="{DynamicResource {x:Static shell:VsFonts.EnvironmentFontSizeKey}}"
Text="Matching VS environment font" />
Or from a ResourceDictionary:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontFamily"
Value="{DynamicResource {x:Static shell:VsFonts.EnvironmentFontFamilyKey}}" />
<Setter Property="FontSize"
Value="{DynamicResource {x:Static shell:VsFonts.EnvironmentFontSizeKey}}" />
</Style>
For scenarios requiring the most direct access (e.g., custom rendering):
IVsUIShell5 shell5 = (IVsUIShell5)ServiceProvider.GetService(typeof(SVsUIShell));
Guid category = environmentColorCategory;
uint rgbaColor = shell5.GetThemedColor(ref category, "ToolWindowBackground", (uint)__THEMEDCOLORTYPE.TCT_Background);
byte[] components = BitConverter.GetBytes(rgbaColor);
var color = System.Drawing.Color.FromArgb(components[3], components[0], components[1], components[2]);
Out-of-process extensions use Remote UI for tool windows and dialogs. Remote UI runs WPF in the Visual Studio process while your extension logic runs out-of-process.
Remote UI XAML automatically inherits Visual Studio theme colors when you use standard WPF controls. The VS host applies its theme styles to the Remote UI content.
[VisualStudioContribution]
internal class MyToolWindow : ToolWindow
{
public MyToolWindow(VisualStudioExtensibility extensibility)
: base(extensibility)
{
Title = "My Tool Window";
}
public override ToolWindowConfiguration ToolWindowConfiguration => new()
{
Placement = ToolWindowPlacement.DocumentWell,
};
public override async Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
{
return new MyToolWindowControl();
}
}
In the Remote UI XAML, standard WPF controls receive VS theme styling automatically. For explicit theme color binding, you can reference VS theme resources via the styles the host injects.
Note: Fine-grained
EnvironmentColorsorVsBrushesbindings are not available in Remote UI XAML. If you need pixel-level color control, consider using an in-process extension component or designing your UI so that standard WPF control styling is sufficient.
| Need | Class | Returns |
|---|---|---|
| Auto-theme all controls in XAML | toolkit:Themes.UseVsTheme="True" | N/A (applied to tree) |
WPF DynamicResource brush bindings | EnvironmentColors.*BrushKey | ResourceKey |
WPF DynamicResource color bindings | EnvironmentColors.*ColorKey | ResourceKey |
| Legacy WPF brush resource keys | VsBrushes.*Key | ResourceKey |
System.Drawing.Color in code | VSColorTheme.GetThemedColor() | System.Drawing.Color |
| WPF Color in code | IVsUIShell5.GetThemedColor() | uint (RGBA) |
| Theme change notification | VSColorTheme.ThemeChanged | Event |
| Environment font | VsFonts.EnvironmentFontFamilyKey / EnvironmentFontSizeKey | ResourceKey |
#, Color.FromRgb, or Brushes. in your XAML/code and replace with EnvironmentColors or VsBrushes resource keys.Themes.UseVsTheme doesn't affect custom controls: UseVsTheme only styles standard WPF controls (Button, TextBox, etc.). Custom controls need explicit EnvironmentColors bindings.{DynamicResource} bindings (not {StaticResource}) so WPF re-evaluates when the theme changes.SystemColors resource keys. VS automatically falls back to system colors in High Contrast mode for EnvironmentColors.VsFonts.EnvironmentFontFamilyKey and VsFonts.EnvironmentFontSizeKey as DynamicResource bindings.Do NOT hard-code hex colors, named brushes (
Brushes.White), orSystem.Drawing.Color— useEnvironmentColors,VsBrushes, orThemes.UseVsTheme.
Do NOT use
{StaticResource}for theme colors — use{DynamicResource}so UI updates on theme switch without restart.
Do NOT use
System.Windows.MessageBoxfor dialogs — not parented to VS and won't be themed. UseVS.MessageBoxorVsShellUtilities.ShowMessageBox.
Do NOT test only in Light theme — always verify in Dark and High Contrast before publishing.
npx claudepluginhub madskristensen/vs-agent-plugins --plugin vs-extensibility-skillsAdds Fluent design system color support to VS theme (.vstheme) files, including Shell/ShellInternal categories and FallbackId setup.
Designs WinUI 3 UIs and reviews XAML for correctness, covering layout planning, control selection, Fluent Design, theming (Light/Dark/HighContrast), typography, spacing, brushes, accessibility, and data binding.
Guides designing and applying Visage theme tokens and JUCE LookAndFeel colors via theme-designer HTML tool, JSON schema, Python C++ codegen, Palette API, ARGB format, and OverrideId scoping.