Years ago, when I read Sarah Drasner’s article on creating a VS Code theme, I quietly 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, and then a day or two to polish up my final tweaks.
In this article, I want to 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
Unpleasant Oh, I can do it. It’s done..?
all within a few hours. (The rest is just time spent polishing).
I never wanted to make a VS Code theme…
I was in the middle of redesigning my website. I designed 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 because Dracula was the only thing that brought a pop of color to my otherwise stark design.
But it didn’t work well with my new site design.

All I wanted to do was improve the syntax highlighting of the code blocks so that they fit in better with the rest of the site.
That was the beginning of it all.
Shiki CSS variable themes made it easy
I use Astro for my website. Shiki is a syntax highlighter built into Astro by default.
After some quick research, I realized that Shiki allows you to create themes with CSS variables – and we only have to choose a handful of colors.

That doesn’t sound too complicated, so I enabled AI to build out a Shiki theme based on the CSS variables. Here are the CSS and JavaScript you’ll need if you’re also using Astro:
: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 them to several popular themes, such as Dracula, Sarah’s Night owlAnd Moonlight 2.
This gave me the confidence to take my own theme one step further, as the syntax highlighting was developing in the right direction.
But to take this further, I had to leave the CSS variable themes behind and dive in TextMate tokens. It was essential because certain code blocks looked absolutely horrible and TextMate tokens provide more granular control over how and what gets color.
This is where the “hard” part begins.
Let AI help with TextMate scopes
Luckily, AI is here to help. If it weren’t for AI, I might have given up at this point.
This is what I need my AI to do:
- I said I wanted to create a custom theme.
- I told it to create a scaffold for me.
- I asked it to find the Moonlight 2 theme files as a reference and create the TextMate scope tokens based on that.
I got it to consolidate the colors used in semantic keywords like foreground, background, keyword — like Shiki’s variable CSS theme.
And I asked him for all the colors in one color object, so I can create a palette object containing only the semantic names.
This is 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 have 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 repository.
Debug locally
It was impossible to debug syntax highlighting on my website because I had to manually restart the server every time I changed a variable.
So I asked AI for a suggestion.
It said I can use it VS Code’s extension host for local development, and then went on to create a .vscode/launch.json file with the following contents:
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}To perform this, you can use F5 (Windows) or Fn + F5 (Mac) and a new editor window will appear. In this new window you can change the theme to your own.
Finding a window that uses the extension host is quite easy because:
- If you change your theme, that window will have a different theme than your other open text editors.
- The keyword Extension Host is prominently featured in the title.

Now everything is a blur right now so I can’t remember if you should take in the next thing package.json theme switching file to work in extension host. If so, please state:
{
"contributes": {
"themes": [
{
"label": "Your Theme Name",
"uiTheme": "vs-dark",
"path": ".json"
}
]
}
} Understanding the TextMate scopes
At first I copied and pasted images and tried to get AI to match different tokens to the colors I chose. But it quickly became frustrating.
Or:
- the AI misunderstood the scope of the textmate, or
- it was overwritten by something else.
I didn’t know. But luckily you can easily debug the TextMate scopes with the “Developer: Inspector Editor Tokens and Scopes” command.

When you are in this mode you can click on any text and a window will pop up. Here you will find all the information you need to adjust the TextMate scopes.

Here’s how you can read what’s going on:
- Foreground: Tells you the current active range. In this case it is the active range
variable. - TextMate ranges: Tells you what are the available TextMate scopes you can use for this specific token.
TextMate scopes work in an interesting way. Through experimentation I discovered the following, so it may not be 100% accurate:
- You can use any part of the available scopes.
variable,variable.propAndvariable.prop.cssall work. - By specifying more properties you can increase specificity.
variable.prop.css>variable.prop>variablein terms of specificity. - The higher range is more specific than the lower.
variable>meta.function.misc.css. - You can use other scopes with it, such as CSS selectors if you need to overwrite a higher range.
meta.function variable>variable
How I chose colors for the theme
This is the most important topic when creating a theme. There is no point in using the theme if syntax highlighting does not support the developer in reading code.
Two articles come to mind here:
Essentially, the principles I took from both articles are:
- We want highlights to stand out.
- Colors will look very similar if you use the same lightness and chroma, and it will be difficult to tell them apart.
- If everything is highlighted, nothing will be highlighted.
- When everything is important, nothing is.
In short, we’re talking about the contrast principle when designing. Since I’m already drafting it for someone to read, the following thoughts came in:
- How do I guide my eyes?
- What are important elements I need to see/know?
- Which elements are less important?
I started working with this:
FunctionsAndmethodswere important, so they had to be strong, so I used themcyanwhich is the strongest color in my palette.- The
exportkeyword is also important because it means an export! KeywordslikeimportAndfunctioncan be quite muted, sopurpleit is.Stringscan begreen– because they look quite nice in a list of text in a JSON file.

I played around with the rest of the colors a bit, but I ended up with the following:
ConstantsAreorangebecause it is quite easy to recognize themVariablesArewhite-ish because that’s most of the text. Adding colors creates the ‘Christmas Lights Diarrhea’ effect that Tonsky mentioned.PropertiesArebluebecause they are like workhorses that need color differentiation, but not enough to attract too much attention.

Then I went to HTML/Astro/Svelte:
Tagsare red because they are quite important – and red is easier to read than cyan.AttributesArepurplefor the same reason askeywords.ComponentsAreorangebecause they have to be differentTags.- Bonus points:
TagsAndComponentsare related – soredAndorangefeels just right here.

And finally CSS syntax highlighting. Almost everything seemed fine at this point, except that:
CSS Functionsshould becyanso in JS.Punctuationneeds to be muted so we can get the--from the rest of the text.Propertycan begreenbecause blue is too boring in this context – andgreenis beautiful for the eyes in contrast with other strong colors.

It’s a shame that the syntax highlighting for nested classes is a bit messed up (they are greenbut they should be orange), but there’s not much I can do about it.

Debugging colors
VS Code is built on it Electronso 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 it directly to get a live update!
Packing
The most important thing I learned during this process is to go with the flow. One opening can lead to another, and then to another, and something that seems “impossible” can turn into “Oh, is it done?” within a few hours.
I name my theme Twilight Cosmos (AI helped with the naming). You can find it at:
How did I publish my extension? That’s the subject of a short follow-up article I’m working on.
In the meantime, here’s the GitHub repository if you want to build on what I’ve done. Feel free to suggest edits to improve this theme too!
Finally, sign up for my email newsletter if you are interested in my creation adventures. 🙂
That’s it. Thanks for reading and I hope you enjoyed it!
#Hasslefree #Visual #Studio #Code #Theme #Building #Extension #CSS #tricks


