Building a custom VS Code theme with Shiki and TextMate scopes

I thought I had seen every way a development environment could break, and then I tried to build my own custom VS Code theme. I avoided it for years. I read the articles, saw the size of the JSON files, and decided that was a nightmare I could live without. Then I was refactoring my personal site and the stock Dracula theme clashed with the new brand. I wanted something exact.

Most devs assume a theme is five colors and a lunch break. The work is in the scopes and the specificity, and in steering clear of what I call “Christmas Lights Diarrhea”: every token a different neon shade until your eyes give out ten minutes into debugging a race condition in a node script.

The simple start: Shiki and CSS variables

Anyone on Astro or a similar static generator is probably already using Shiki for syntax highlighting. The naive route is CSS variables. Quick, functional, about as involved as using a filter in WordPress to change a button color.

:root {
  --shiki-foreground: #eeeeee;
  --shiki-background: #333333;
  --shiki-token-keyword: #990000;
  --shiki-token-function: #bb0000;
}

Then the gotcha shows up. CSS variables cannot target specific properties or nested classes with any real granularity. Once your code blocks start reading like unformatted text, variables have run out of road and you need the engine underneath.

TextMate scopes for your custom VS Code theme

TextMate tokens are where a theme stops looking amateur. Think of them as the hooks and filters of the VS Code world. You are telling the editor: when you see a variable that is a property of a CSS class, color it blue, and when it is a constant, color it orange.

I used AI for the first pass. I pointed it at Moonlight 2’s theme files as a reference and had it generate the initial TextMate scope tokens, which saved me hours of typing JSON by hand. It was not right out of the box. I ended up refactoring the build script so a clean JavaScript object compiles into the .json file VS Code actually wants.

My guide on the Pro Block Theme Development Workflow covers the same kind of build automation, if that part interests you.

Debugging locally with the Extension Host

Guessing does not get you a custom VS Code theme. You want a live feedback loop, so I set up a launch.json that runs VS Code’s Extension Host. It opens a second window with your theme-in-progress already active.

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Extension",
      "type": "extensionHost",
      "request": "launch",
      "args": [
        "--extensionDevelopmentPath=${workspaceFolder}"
      ]
    }
  ]
}

Hit F5 and you are in. To find out which scope a piece of text belongs to, run the “Developer: Inspect Editor Tokens and Scopes” command. It is DevTools for your editor: foreground color, active scope, specificity, all there. That command is how you work out why your variable.other.property.js came out the wrong color.

The principle of contrast

Resist the urge to highlight everything, because a theme where every token shouts tells you nothing. A few rules I stick to when building a custom VS Code theme:

  • The strongest color, cyan in my case, goes to functions and methods. They do the work, so they get the attention.
  • A muted purple covers keywords like import and export. Visible without shouting.
  • Neutral white or grey handles standard variables, which is what keeps the Christmas lights away.

The VS Code Syntax Highlight Guide is the official reference on tokenization and worth a read.

If this custom VS Code theme work is eating your dev hours, I can take it off your plate. I have been wrestling with WordPress since the 4.x days and I know how to get a workflow fast and legible.

Ship it

Mine ended up as Twilight Cosmos. About six hours got me 80 percent of the way, then another full day went on the CSS and HTML specific scopes. That is the part worth knowing: the messy corners of a dev environment are usually where the result you want is hiding, and sometimes that means editing JSON by hand. The Twilight Cosmos repo is up if you want a starting point for your own.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.