No-Hassle Visual Studio Code Theming: Building an Extension
css-tricks.com
Monday, February 2, 2026
Years ago, when I read Sarah Drasner’s article on creating a VS Code theme, I silently thought to myself, That’s a lot of work… I’m never going to make a theme… But lo and behold, I went ahead and made one — and it took less than six hours to get most of the theme...
Years ago, when I read Sarah Drasner’s article on creating a VS Code theme, I silently thought to myself, That’s a lot of work… I’m never going to make a theme…
But lo and behold, I went ahead and made one — and it took less than six hours to get most of the theme working, then a day or two to polish up my final tweaks.

In this article, I want to you walk you through my process of creating this theme — along with the actual steps I took to create it.
I think talking about the process is powerful because I went from Nah, too much work
to Oh, I can do it to It’s done..?
all within a matter of hours. (The rest is simply time spent polishing).
I never wanted to make a VS Code theme…
I was in the middle of redesigning my website. I’ve been rocking a super duper old design that I’ve wanted to change for years — and I finally started moving.

I used Dracula Theme for code snippets in my old design and it worked since Dracula was the only thing that provided a splash of color in my otherwise stark design.
But it didn’t work well with my new site design.

All I wanted to do was to improve syntax highlighting for the code blocks so they’re more aligned with the rest of the site.
That was the beginning of everything.
Shiki CSS variable theming made it simple
I use Astro for my website. Shiki is a syntax highlighter that is built into Astro by default.
With some quick research, I realized Shiki allows you to create themes with CSS variables — and there are only a handful of colors we need to choose.

That doesn’t sound too complicated, so I got AI to help flesh out a Shiki theme based on the CSS variables. Here’s the CSS and JavaScript you need if you’re using Astro as well:
:root {
--shiki-foreground: #eeeeee;
--shiki-background: #333333;
--shiki-token-constant: #660000;
--shiki-token-string: #770000;
--shiki-token-comment: #880000;
--shiki-token-keyword: #990000;
--shiki-token-parameter: #aa0000;
--shiki-token-function: #bb0000;
--shiki-token-string-expression: #cc0000;
--shiki-token-punctuation: #dd0000;
--shiki-token-link: #ee0000;
}
pre.shiki,
pre.astro-code {
padding: 1rem;
border-radius: 0.5rem;
color: var(--shiki-foreground);
background-color: var(--shiki-background);
overflow-x: auto;
}
pre.shiki code,
pre.astro-code code {
padding: 0;
font-size: inherit;
line-height: inherit;
color: inherit;
background: none;
}
import { createCssVariablesTheme } from 'shiki/core'
const shikiVariableTheme = createCssVariablesTheme({
name: 'css-variables',
variablePrefix: '--shiki-',
fontStyle: true,
})
export default defineConfig ({
// ...
markdown: {
shikiConfig: {
theme: shikiVariableTheme
}
}
})
I did a quick experiment with the colors I had already used for my website and compared it to various popular themes, like Dracula, Sarah’s Night Owl, and Moonlight 2.
This gave me the confidence to push my own theme a little further — because the syntax highlighting was shaping up in the right direction.
But, to push this further, I had to ditch CSS variable theming and dive into TextMate tokens. It was essential because certain code blocks looked absolutely horrendous and TextMate tokens provide more granular control of how and what gets color.
This is where the “hard” part begins.
Getting AI to help with TextMate scopes
Thankfully, AI is here to help. If AI wasn’t here, I might have just given up at this point.
Here’s what I got my AI to do:
- I said I wanted to make a custom theme.
- I told it to create a scaffold for me.
- I asked it to look for Moonlight 2’s theme files as a reference and create the TextMate scope tokens based on that.
I got it to consolidate the colors used into semantic keywords like foreground, background, keyword — like the Shiki CSS variable theme.
And I asked it to pull all of the colors into a color object so I can have a palette object that includes only the semantic names.
Here’s roughly what it created:
const colors = {
purple: '...',
blue: '...',
// ...
}
const palette = {
foreground: '...',
background: '...',
// ...
}
export default {
colors: {
// Used for theming the text editor
},
displayName: 'Display Name of your Theme',
name: 'your-theme-name',
tokenColors: [
{
name: 'Scope name (optional)',
scope: [/*scopes used*/],
settings: {
foreground: /* change color */,
background: /* background of the text */,
fontStyle: /* normal, bold or italic */,
}
}
]
}
You need to provide JSON for VS Code to configure things, so I also got AI to create a build script that converts the above format into a .json file.
You can find the build script and everything I used in the GitHub Repo.
Debugging locally
It was impossible to debug syntax highlighting on my website because I had to manually restart the server whenever I changed a variable.
So, I asked AI for a suggestion.
It said that I can use VS Code’s Extension Host for local development, then proceeded to created a .vscode/launch.json file with the following contents:
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}
To run this, you can use F5 (Windows) or Fn + F5 (Mac) and a new editor window will pop up — in this new window, you can change the theme to your custom theme.
Spotting a window that uses the extension host is quite simple because:
- If you change your theme, that window will be a different theme compared to your other opened text editors.
- The Extension Host keyword is prominent in the title.

Now, everything has been a blur at this point, so I can’t remember if you need to include the following into your package.json file for theme switching to work in the extension host. If so, include it:
{
"contributes": {
"themes": [
{
"label": "Your Theme Name",
"uiTheme": "vs-dark",
"path": "<path-to-your-theme>.json"
}
]
}
}
Understanding TextMate scopes
At first, I copy-pasted images and tried to get AI to adjust various tokens to the colors I chose. But it got frustrating quite quickly.
Either:
- the AI got the textmate scope wrong, or
- it was overwritten by something else.
I couldn’t tell. But thankfully you can debug the TextMate scopes easily with a “Developer: Inspector Editor Tokens and Scopes” command.

When you’re in this mode, you can click on any text and a window will pop up. This contains all the information you need to adjust TextMate scopes.

Here’s how to read what’s going on:
- Foreground: Tells you the current active scope. In this case, the active scope is
variable. - TextMate scopes: Tells you what are the available TextMate scopes you can use for this specific token.
TextMate scopes work in an interesting way. I figured out the following by experimenting, so it might not be 100% accurate:
- You can use any part of the available scopes.
variable,variable.prop, andvariable.prop.cssall work. - You can increase specificity by stating more properties.
variable.prop.css>variable.prop>variablein terms of specificity. - The higher scope is more specific than the lower one.
variable>meta.function.misc.css. - You can other scopes with them like CSS selectors if you need to overwrite a higher scope.
meta.function variable>variable
How I chose colors for the theme
This is the most important topic when creating a theme. There’s no point having the theme if syntax highlighting doesn’t support the developer in reading code.
Two articles come into my mind here:
- Creating a VS Code Theme by Sarah Drasner
- Everyone is getting syntax highlighting wrong by Tonsky
Essentially, the principles that I took away from both articles are:
- We want highlights to stand out.
- Colors will look very similar to each other if you make use the same lightness and chroma, and it’ll be hard to tell them apart.
- If everything is highlighted, nothing is highlighted.
- If everything is important, nothing is.
Basically, we’re talking about the principle of contrast when designing. Since I’m already designing for someone to read, the very next thoughts that came were:
- How do I guide my eyes?
- What are important elements that I have to see/know?
- What elements are less important?
With that, I began working:
Functionsandmethodswere important so they had to be strong, so I usedcyanwhich is the strongest color in my palette.- The
exportkeyword is also important since it signifies an export! Keywordslikeimportandfunctioncan be rather muted, sopurpleit is.Stringscan begreen— cos they seem rather pleasing in a list of text within a JSON file.

I played around with the rest of the colors a little, but I eventually settled with the following:
Constantsareorangebecause it’s kinda easy to spot themVariablesarewhite-ish because that’s the bulk of the text — adding colors to them creates the “Christmas Lights Diarrhea” effect Tonsky mentioned.Propertiesarebluebecause they’re like workhorses that needs color differentiation, but not enough to draw too much attention.

Then I moved onto HTML/Astro/Svelte:
Tagsare red because they’re kinda important — and red is easier to read that cyan.Attributesarepurplefor the same reason askeywords.Componentsareorangebecause they need to be different fromTags.- Bonus points:
TagsandComponentsare related — soredandorangefeels just right here.

And, finally, CSS syntax highlighting. Almost everything seemed right at this point, except that:
CSS Functionsshould becyanlike that in JS.Punctuationshould be muted so we can easily differentiate the--from the rest of the text.Propertycan begreenbecause blue is too dull in this context — andgreenis nice on the eyes when contrasted with other powerful colors.

It’s a pity that syntax highlighting for nested classes goes a little bit haywire (they’re green, but they should be orange), but there’s nothing much I can do about it.

Debugging colors
VS Code is built on Electron, so it’s easy to debug and test colors. What I had to do was fire up devtools, inspect the color I wanted to change, and change them directly to get a live update!
Wrapping up
The most important I thing I learned during this process is to go with the flow. One opening can lead to another, then another, and something what seems “impossible” can become “Oh, it’s done?” in a matter of hours.
I call my theme Twilight Cosmos (AI helped with the naming). You can find it on:
- Visual Studio Marketplace if you use VS Code
- Open VSX if you use Cursor or other editors
- npm if you wanna use the for Shiki
How did I publish my extension? That’s the subject of a brief follow-up article that I’m working on.
In the meantime, here’s the GitHub repo if you want to build upon whatever I have done. Feel free to suggest edits to improve this theme too!
Finally, sign up for my email newsletter if you’re interested in hearing my creation adventures. :)
That’s it. Thanks for reading and I hope you had a blast!
No-Hassle Visual Studio Code Theming: Building an Extension originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Read the full article
Continue reading on css-tricks.com


