How to Create a Custom VS Code Theme: Developer's Guide
Every popular VS Code theme started as a single JSON file and a developer who thought the existing options could be better. Creating your own theme is surprisingly approachable — you do not need to be a designer, and you do not need deep extension development experience. If you can edit JSON and have opinions about colors, you can build and publish a theme that thousands of developers might use.
This guide takes you from zero to a published theme on the VS Code Marketplace, covering the color token system, TextMate grammar scopes, testing workflow, and the publishing process.
Understanding the VS Code Theming System
VS Code themes control two distinct layers of color:
- Workbench colors: The colors of the editor UI — sidebar, activity bar, tabs, status bar, title bar, panels, borders, buttons, and badges. These are defined as key-value pairs like
"sideBar.background": "#1e1e2e". - Token colors: The colors applied to source code — keywords, strings, variables, functions, comments, operators, and other syntax elements. These use TextMate grammar scopes to target specific token types.
A complete theme defines both layers. You can technically create a theme that only defines one (inheriting defaults for the other), but the best themes carefully design both to create a cohesive visual experience.
Prerequisites
Before you start, install these tools:
- Node.js (v18 or later) — needed to run the extension scaffolding tool and package your theme.
- Yeoman and the VS Code Extension Generator:
npm install -g yo generator-code
That is all you need. No TypeScript, no Webpack, no bundler configuration. Theme extensions are pure JSON.
Step 1: Scaffold the Extension
Run the generator and select "New Color Theme":
yo code
When prompted, choose these options:
- What type of extension? New Color Theme
- Import or fresh? No, start fresh
- Name: Your theme name (e.g., "Midnight Ember")
- Identifier: Auto-generated from name (e.g., "midnight-ember")
- Description: A one-line description
- Theme type: Dark
This creates a project with this structure:
package.json
themes/
Midnight Ember-color-theme.json
README.md
CHANGELOG.md
.vscodeignore
The entire theme lives in that single JSON file inside themes/.
Step 2: Define Your Color Palette
Before editing any JSON, decide on your palette. A well-designed dark theme needs these core colors:
- Background: Your editor background. Stay between #15-#2a lightness for dark themes. Avoid pure black (#000000) — it creates uncomfortable contrast on LCD screens.
- Foreground: Main text color. Aim for a contrast ratio of 8:1 to 11:1 against your background.
- 6-8 accent colors: For syntax tokens. Include at least: a blue/cyan (functions), a green (strings), a yellow/orange (constants/warnings), a red/pink (keywords/errors), a purple (types), and a gray (comments).
- 3-4 UI shades: Variations of your background for sidebars, panels, selections, and hover states.
Here is an example palette for a warm dark theme:
{
"bg": "#1a1410",
"surface": "#221c16",
"border": "#3a3028",
"fg": "#e8dcc8",
"comment": "#7a6e5e",
"red": "#e06c60",
"orange": "#e8965a",
"yellow": "#e8c86a",
"green": "#8cba6a",
"blue": "#6aa8c8",
"purple": "#b88ae8",
"pink": "#e08aaa"
}
Step 3: Configure Workbench Colors
Open your theme JSON file and populate the colors object. Here are the most important workbench color keys:
{
"name": "Midnight Ember",
"type": "dark",
"colors": {
"editor.background": "#1a1410",
"editor.foreground": "#e8dcc8",
"editorCursor.foreground": "#e8965a",
"editor.selectionBackground": "#3a302866",
"editor.lineHighlightBackground": "#221c1680",
"sideBar.background": "#151010",
"sideBar.foreground": "#b0a898",
"sideBarTitle.foreground": "#e8dcc8",
"activityBar.background": "#121010",
"activityBar.foreground": "#e8dcc8",
"statusBar.background": "#121010",
"statusBar.foreground": "#b0a898",
"tab.activeBackground": "#1a1410",
"tab.inactiveBackground": "#151010",
"tab.activeForeground": "#e8dcc8",
"titleBar.activeBackground": "#121010",
"panel.background": "#151010",
"panel.border": "#3a3028",
"terminal.foreground": "#e8dcc8",
"terminal.ansiBlack": "#3a3028",
"terminal.ansiRed": "#e06c60",
"terminal.ansiGreen": "#8cba6a",
"terminal.ansiYellow": "#e8c86a",
"terminal.ansiBlue": "#6aa8c8",
"terminal.ansiMagenta": "#b88ae8",
"terminal.ansiCyan": "#6ac8b8",
"terminal.ansiWhite": "#e8dcc8"
}
}
VS Code supports over 700 workbench color keys. You do not need to define all of them — undefined keys inherit sensible defaults based on your theme type (dark/light). Focus on the 30-40 most visible elements first, then refine edge cases during testing.
Step 4: Define Token Colors with TextMate Scopes
Token colors use TextMate grammar scopes to target syntax elements. Each rule has a scope (what to target) and settings (how to color it). Add a tokenColors array to your theme JSON:
"tokenColors": [
{
"name": "Comments",
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "#7a6e5e",
"fontStyle": "italic"
}
},
{
"name": "Keywords",
"scope": ["keyword", "storage.type", "storage.modifier"],
"settings": {
"foreground": "#e06c60"
}
},
{
"name": "Strings",
"scope": ["string", "string.quoted"],
"settings": {
"foreground": "#8cba6a"
}
},
{
"name": "Functions",
"scope": [
"entity.name.function",
"support.function",
"meta.function-call"
],
"settings": {
"foreground": "#6aa8c8"
}
},
{
"name": "Variables",
"scope": ["variable", "variable.other"],
"settings": {
"foreground": "#e8dcc8"
}
},
{
"name": "Types and Classes",
"scope": [
"entity.name.type",
"support.type",
"support.class"
],
"settings": {
"foreground": "#e8c86a"
}
},
{
"name": "Constants and Numbers",
"scope": [
"constant",
"constant.numeric",
"constant.language"
],
"settings": {
"foreground": "#e8965a"
}
},
{
"name": "Operators",
"scope": ["keyword.operator"],
"settings": {
"foreground": "#b88ae8"
}
},
{
"name": "Tags (HTML/JSX)",
"scope": ["entity.name.tag"],
"settings": {
"foreground": "#e06c60"
}
},
{
"name": "Attributes",
"scope": ["entity.other.attribute-name"],
"settings": {
"foreground": "#e8965a",
"fontStyle": "italic"
}
}
]
How to Find the Right Scope
The fastest way to discover which scope applies to any token in your code is the built-in scope inspector:
- Open any source file in VS Code.
- Press
Cmd+Shift+Pand run "Developer: Inspect Editor Tokens and Scopes". - Click on any token. A panel appears showing the TextMate scope, semantic token type, and current foreground color.
Use this tool constantly while building your theme. It removes all guesswork from scope targeting.
Step 5: Test Your Theme Live
VS Code makes testing effortless with its extension development host:
- Open your theme project folder in VS Code.
- Press
F5to launch a new VS Code window with your theme loaded. - Open various source files (JavaScript, Python, HTML, CSS, Rust, Go) to see how your colors look across languages.
- Edit the theme JSON in your original window — changes apply immediately in the test window after saving.
Step 6: Publish to the VS Code Marketplace
Once your theme looks great across multiple languages, publish it:
Create a publisher account
- Go to Visual Studio Marketplace Management.
- Sign in with a Microsoft account or create one.
- Create a publisher (choose a unique publisher ID).
Create a Personal Access Token
- Go to dev.azure.com and sign in.
- Click your profile icon > Personal Access Tokens > New Token.
- Set the scope to Marketplace > Manage and set a reasonable expiration.
- Copy the generated token — you will need it once.
Package and publish
# Install the publishing tool npm install -g @vscode/vsce # Login with your publisher name and token vsce login your-publisher-name # Package your extension vsce package # Publish to the marketplace vsce publish
Before publishing, update your package.json with these fields:
{
"publisher": "your-publisher-name",
"repository": {
"type": "git",
"url": "https://github.com/you/midnight-ember"
},
"icon": "icon.png",
"galleryBanner": {
"color": "#1a1410",
"theme": "dark"
}
}
Include a 128x128 pixel PNG icon and a detailed README with screenshots. Themes with good screenshots get significantly more installs — developers decide visually, and a single compelling screenshot can be the difference between someone clicking Install or scrolling past.
Tips for a Great Theme
- Test on multiple monitors. Colors look different on IPS, VA, and OLED panels. What looks perfect on your MacBook might wash out on an external monitor.
- Use no more than 8-10 token colors. More than that creates visual noise. The best themes use fewer colors applied consistently.
- Make comments readable. A common mistake is making comments too dim. Developers read comments — do not punish them for it. Aim for at least a 4.5:1 contrast ratio on comment text.
- Test with semantic highlighting enabled and disabled. VS Code's semantic token provider can override your TextMate scopes. Add
"semanticHighlighting": trueto your theme and define semantic token rules if you want full control. - Iterate publicly. Share your work-in-progress on Reddit's r/vscode or the VS Code Slack community. Feedback from other developers catches issues you would never notice on your own.
Going Further
Once your theme is published, consider these enhancements:
- Add a light variant. Many developers switch between dark and light mode. Offering both under one extension doubles your potential audience.
- Port to other editors. Use your color palette to create matching themes for Neovim, JetBrains IDEs, and terminal emulators. The Base16 framework can automate this process.
- Create a companion icon theme. File icon themes that match your color palette create a more immersive experience.
- Set up automated testing. Tools like
vscode-theme-lintcan check your theme for accessibility issues (contrast ratios, colorblind-safe palettes) on every commit.
Building a VS Code theme is one of the most rewarding small projects a developer can take on. You get to use your own creation every day, and if it resonates with others, you might find yourself maintaining a tool used by thousands. Start with a palette that makes you happy, test it thoroughly, and ship it.