VS Code Theme Customization and Creation
How to customize VS Code's appearance with token colors, workbench theming, and semantic highlighting, plus building and publishing your own theme extension.
VS Code Theme Customization and Creation
Developers spend 8+ hours a day staring at their editor. The colors matter more than aesthetics — they affect readability, eye strain, and how quickly you can scan code. The default themes work, but customizing them to match your preferences (or building your own from scratch) gives you an editor that feels genuinely yours.
I have built custom themes for teams and personal use. The theming system is powerful once you understand the two layers: workbench colors (UI chrome) and token colors (syntax highlighting). This guide covers both, from simple tweaks to publishing a complete theme.
Prerequisites
- VS Code installed
- Basic understanding of JSON
- Familiarity with CSS color formats (hex, RGB, HSL)
- A color picker tool is helpful but not required
Understanding VS Code's Theme System
VS Code themes have two independent layers:
- Workbench colors — the UI itself: sidebar, title bar, tabs, status bar, panels, borders
- Token colors — syntax highlighting: keywords, strings, comments, functions, variables
You can customize both without creating a full theme extension, directly in your settings.json.
Quick Customizations in Settings
Overriding Workbench Colors
// settings.json
{
"workbench.colorCustomizations": {
// Title bar
"titleBar.activeBackground": "#1a1a2e",
"titleBar.activeForeground": "#e0e0e0",
// Sidebar
"sideBar.background": "#16213e",
"sideBar.foreground": "#d4d4d4",
"sideBarTitle.foreground": "#4fc3f7",
// Editor
"editor.background": "#0f0f23",
"editor.foreground": "#cccccc",
"editor.lineHighlightBackground": "#1a1a3e",
"editor.selectionBackground": "#264f78",
// Status bar
"statusBar.background": "#0a3d62",
"statusBar.foreground": "#ffffff",
"statusBar.debuggingBackground": "#c0392b",
// Activity bar (left icon bar)
"activityBar.background": "#0f0f23",
"activityBar.foreground": "#4fc3f7",
// Tab bar
"tab.activeBackground": "#1a1a2e",
"tab.inactiveBackground": "#0f0f23",
"tab.activeBorderTop": "#4fc3f7",
// Terminal
"terminal.background": "#0f0f23",
"terminal.foreground": "#cccccc",
"terminal.ansiGreen": "#4ec9b0",
"terminal.ansiRed": "#f44747",
"terminal.ansiYellow": "#dcdcaa",
"terminal.ansiBlue": "#569cd6",
// Git decorations
"gitDecoration.modifiedResourceForeground": "#e2c08d",
"gitDecoration.untrackedResourceForeground": "#73c991",
"gitDecoration.deletedResourceForeground": "#c74e39",
// Minimap
"minimap.background": "#0f0f23",
// Brackets
"editorBracketHighlight.foreground1": "#ffd700",
"editorBracketHighlight.foreground2": "#da70d6",
"editorBracketHighlight.foreground3": "#179fff"
}
}
Overriding Token Colors (Syntax Highlighting)
{
"editor.tokenColorCustomizations": {
// Global overrides (apply to all themes)
"comments": "#6a9955",
"strings": "#ce9178",
"keywords": "#569cd6",
"functions": "#dcdcaa",
"variables": "#9cdcfe",
"numbers": "#b5cea8",
"types": "#4ec9b0",
// TextMate scope overrides for fine control
"textMateRules": [
{
"scope": "comment",
"settings": {
"foreground": "#6a9955",
"fontStyle": "italic"
}
},
{
"scope": "keyword.control",
"settings": {
"foreground": "#c586c0"
}
},
{
"scope": "entity.name.function",
"settings": {
"foreground": "#dcdcaa",
"fontStyle": "bold"
}
},
{
"scope": "variable.parameter",
"settings": {
"foreground": "#9cdcfe",
"fontStyle": "italic"
}
},
{
"scope": "string.quoted",
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "constant.numeric",
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe",
"fontStyle": "italic"
}
},
{
"scope": "support.function",
"settings": {
"foreground": "#dcdcaa"
}
},
{
"scope": "punctuation.definition.tag",
"settings": {
"foreground": "#808080"
}
}
]
}
}
Per-Theme Overrides
Apply customizations only to specific themes:
{
"workbench.colorCustomizations": {
"[Dark+ (default dark)]": {
"editor.background": "#1a1a1a",
"statusBar.background": "#333333"
},
"[Monokai]": {
"editor.background": "#272822",
"statusBar.background": "#414339"
}
},
"editor.tokenColorCustomizations": {
"[Dark+ (default dark)]": {
"comments": "#608b4e"
}
}
}
Finding Token Scopes
To customize a specific syntax element, you need to know its TextMate scope. Use the Scope Inspector:
- Open a file with the code you want to style
Ctrl+Shift+P→ "Developer: Inspect Editor Tokens and Scopes"- Click on any token to see its scope
The inspector shows:
Token: function
Scope: storage.type.function.js
Foreground: #569cd6 (from Dark+ theme)
Use the most specific scope for targeted styling, or a broader scope for wider changes:
// Specific: only JavaScript function keyword
"storage.type.function.js"
// Broader: all storage types (var, function, class)
"storage.type"
// Broadest: all storage tokens
"storage"
Semantic Highlighting
VS Code 1.44+ supports semantic highlighting, which is language-aware. Unlike TextMate scopes (which are pattern-based), semantic tokens come from the language server and understand the actual code structure.
{
"editor.semanticTokenColorCustomizations": {
"enabled": true,
"rules": {
"parameter": {
"foreground": "#9cdcfe",
"fontStyle": "italic"
},
"property.declaration": {
"foreground": "#4fc1ff"
},
"function.declaration": {
"foreground": "#dcdcaa",
"fontStyle": "bold"
},
"variable.readonly": {
"foreground": "#4fc1ff"
},
"type": {
"foreground": "#4ec9b0"
},
"enum": {
"foreground": "#4ec9b0"
},
"interface": {
"foreground": "#4ec9b0",
"fontStyle": "italic"
},
"namespace": {
"foreground": "#4ec9b0"
}
}
}
}
Semantic tokens override TextMate tokens when both exist. The language server provides richer information — it knows the difference between a function declaration and a function call, between a parameter and a local variable.
Building a Theme Extension
Scaffolding
npm install -g yo generator-code
yo code
Choose:
? What type of extension? New Color Theme
? Do you want to import or convert an existing theme? No, start fresh
? What's the name? Midnight Ocean
? What's the identifier? midnight-ocean
? What's the description? A dark theme with ocean-inspired colors
? What's the base theme? Dark
Theme File Structure
midnight-ocean/
themes/
midnight-ocean-color-theme.json # The theme definition
package.json # Extension manifest
README.md
CHANGELOG.md
icon.png # Theme icon (128x128)
Package.json for Themes
{
"name": "midnight-ocean",
"displayName": "Midnight Ocean",
"description": "A dark theme with ocean-inspired colors",
"version": "1.0.0",
"engines": { "vscode": "^1.80.0" },
"categories": ["Themes"],
"contributes": {
"themes": [
{
"label": "Midnight Ocean",
"uiTheme": "vs-dark",
"path": "./themes/midnight-ocean-color-theme.json"
},
{
"label": "Midnight Ocean Soft",
"uiTheme": "vs-dark",
"path": "./themes/midnight-ocean-soft-color-theme.json"
}
]
},
"publisher": "your-publisher-name",
"icon": "icon.png",
"repository": {
"type": "git",
"url": "https://github.com/yourname/midnight-ocean"
}
}
The uiTheme value determines the base:
vs— light themevs-dark— dark themehc-black— high contrast darkhc-light— high contrast light
Complete Theme Definition
{
"name": "Midnight Ocean",
"type": "dark",
"semanticHighlighting": true,
"colors": {
// Editor
"editor.background": "#0b1929",
"editor.foreground": "#d4d4d4",
"editor.lineHighlightBackground": "#0d2137",
"editor.selectionBackground": "#1b4b73",
"editor.wordHighlightBackground": "#1b4b7350",
"editor.findMatchBackground": "#515c6a",
"editor.findMatchHighlightBackground": "#ea5c0050",
"editorCursor.foreground": "#4fc3f7",
"editorWhitespace.foreground": "#1e3a5c",
"editorIndentGuide.background1": "#1e3a5c",
"editorIndentGuide.activeBackground1": "#3a6b94",
"editorLineNumber.foreground": "#2a4f6f",
"editorLineNumber.activeForeground": "#4fc3f7",
"editorRuler.foreground": "#1e3a5c",
// Brackets
"editorBracketMatch.background": "#1b4b7350",
"editorBracketMatch.border": "#4fc3f7",
"editorBracketHighlight.foreground1": "#ffd700",
"editorBracketHighlight.foreground2": "#da70d6",
"editorBracketHighlight.foreground3": "#4fc3f7",
// Gutter
"editorGutter.addedBackground": "#4ec9b0",
"editorGutter.modifiedBackground": "#e2c08d",
"editorGutter.deletedBackground": "#f44747",
// Sidebar
"sideBar.background": "#091422",
"sideBar.foreground": "#8badc4",
"sideBarTitle.foreground": "#4fc3f7",
"sideBarSectionHeader.background": "#0b1929",
// Activity bar
"activityBar.background": "#071018",
"activityBar.foreground": "#4fc3f7",
"activityBar.inactiveForeground": "#3a6b94",
"activityBarBadge.background": "#007acc",
// Title bar
"titleBar.activeBackground": "#071018",
"titleBar.activeForeground": "#8badc4",
"titleBar.inactiveBackground": "#071018",
// Status bar
"statusBar.background": "#0a2744",
"statusBar.foreground": "#8badc4",
"statusBar.debuggingBackground": "#c0392b",
"statusBar.noFolderBackground": "#1e3a5c",
// Tabs
"tab.activeBackground": "#0b1929",
"tab.activeForeground": "#ffffff",
"tab.inactiveBackground": "#091422",
"tab.inactiveForeground": "#5a8ba8",
"tab.activeBorderTop": "#4fc3f7",
"tab.border": "#091422",
// Terminal
"terminal.background": "#0b1929",
"terminal.foreground": "#d4d4d4",
"terminal.ansiBlack": "#0b1929",
"terminal.ansiRed": "#f44747",
"terminal.ansiGreen": "#4ec9b0",
"terminal.ansiYellow": "#dcdcaa",
"terminal.ansiBlue": "#569cd6",
"terminal.ansiMagenta": "#c586c0",
"terminal.ansiCyan": "#4fc3f7",
"terminal.ansiWhite": "#d4d4d4",
// Input
"input.background": "#0d2137",
"input.border": "#1e3a5c",
"input.foreground": "#d4d4d4",
"input.placeholderForeground": "#3a6b94",
"focusBorder": "#4fc3f7",
// Dropdown
"dropdown.background": "#0d2137",
"dropdown.border": "#1e3a5c",
// Lists
"list.activeSelectionBackground": "#1b4b73",
"list.hoverBackground": "#0d2137",
"list.inactiveSelectionBackground": "#0d2137",
// Git
"gitDecoration.modifiedResourceForeground": "#e2c08d",
"gitDecoration.untrackedResourceForeground": "#4ec9b0",
"gitDecoration.deletedResourceForeground": "#f44747",
// Peek view
"peekView.border": "#4fc3f7",
"peekViewEditor.background": "#091422",
"peekViewResult.background": "#091422",
"peekViewTitle.background": "#0b1929"
},
"tokenColors": [
{
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "#3a6b94",
"fontStyle": "italic"
}
},
{
"scope": ["string", "string.quoted"],
"settings": {
"foreground": "#ce9178"
}
},
{
"scope": "string.regexp",
"settings": {
"foreground": "#d16969"
}
},
{
"scope": ["constant.numeric", "constant.language"],
"settings": {
"foreground": "#b5cea8"
}
},
{
"scope": ["keyword", "storage.type", "storage.modifier"],
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "keyword.control",
"settings": {
"foreground": "#c586c0"
}
},
{
"scope": "keyword.operator",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "#dcdcaa"
}
},
{
"scope": "entity.name.type",
"settings": {
"foreground": "#4ec9b0"
}
},
{
"scope": "variable",
"settings": {
"foreground": "#9cdcfe"
}
},
{
"scope": "variable.parameter",
"settings": {
"foreground": "#9cdcfe",
"fontStyle": "italic"
}
},
{
"scope": "entity.name.tag",
"settings": {
"foreground": "#569cd6"
}
},
{
"scope": "entity.other.attribute-name",
"settings": {
"foreground": "#9cdcfe",
"fontStyle": "italic"
}
},
{
"scope": "punctuation",
"settings": {
"foreground": "#808080"
}
},
{
"scope": "meta.embedded",
"settings": {
"foreground": "#d4d4d4"
}
},
{
"scope": "markup.heading",
"settings": {
"foreground": "#569cd6",
"fontStyle": "bold"
}
},
{
"scope": "markup.bold",
"settings": {
"fontStyle": "bold"
}
},
{
"scope": "markup.italic",
"settings": {
"fontStyle": "italic"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "#ce9178"
}
}
],
"semanticTokenColors": {
"parameter": {
"foreground": "#9cdcfe",
"italic": true
},
"property.declaration": "#4fc1ff",
"function.declaration": {
"foreground": "#dcdcaa",
"bold": true
},
"variable.readonly": "#4fc1ff",
"type": "#4ec9b0",
"interface": {
"foreground": "#4ec9b0",
"italic": true
}
}
}
Testing Your Theme
Press F5 to launch the Extension Development Host with your theme active. Make changes to the theme JSON file and the preview updates live.
Use the Developer Tools (Ctrl+Shift+I) to inspect elements and find the correct color keys.
Publishing
npm install -g @vscode/vsce
vsce login your-publisher
vsce package # Creates .vsix file
vsce publish # Publishes to marketplace
Common Issues and Troubleshooting
Color customization has no effect
The color key name is wrong. VS Code silently ignores unknown keys:
Fix: Use VS Code's IntelliSense in settings.json — it autocompletes valid color keys. Or check the Theme Color Reference.
Token colors do not apply to expected tokens
The TextMate scope you are targeting does not match the actual scope:
Fix: Use "Developer: Inspect Editor Tokens and Scopes" to find the exact scope string for the token you want to style.
Theme looks different across languages
Different language grammars use different scopes for similar constructs:
Fix: Target broader scopes (e.g., keyword instead of keyword.control.js) for consistency, or add language-specific rules for each language you support.
Semantic highlighting overrides your token colors
When semantic highlighting is enabled, it takes precedence over TextMate rules:
Fix: Define semanticTokenColors in your theme to control semantic highlighting colors explicitly. Or set "editor.semanticHighlighting.enabled": false to disable it.
Best Practices
- Start with an existing theme and modify it. Do not design from a blank canvas. Fork a theme you like and adjust colors incrementally.
- Use a consistent color palette. Pick 8-10 colors and use them consistently. Random colors make code harder to scan, not easier.
- Test with real code in multiple languages. A theme that looks great with JavaScript may look terrible with Python or HTML. Test broadly.
- Ensure sufficient contrast. WCAG AA requires 4.5:1 contrast ratio for text. Use a contrast checker tool to verify readability.
- Use semantic highlighting. It provides richer information than TextMate scopes. Set
"semanticHighlighting": truein your theme. - Provide both a standard and soft variant. Some developers prefer higher contrast, others prefer muted colors. Two variants cover both.
- Include a screenshot in your README. Users choose themes visually. Show code samples in JavaScript, Python, HTML, and Markdown.