Monday, February 2, 2026

No-Trouble Visible Studio Code Theming: Constructing an Extension


Years in the past, after I learn Sarah Drasner’s article on making a VS Code theme, I silently thought to myself, That’s numerous work… I’m by no means going to make a theme…

However lo and behold, I went forward and made one — and it took lower than six hours to get many of the theme working, then a day or two to shine up my last tweaks.

On this article, I wish to you stroll you thru my course of of making this theme — together with the precise steps I took to create it.

I believe speaking in regards to the course of is highly effective as a result of I went from Nah, an excessive amount of work to Oh, I can do it to It’s finished..? all inside a matter of hours. (The remainder is solely time spent sharpening).

I by no means wished to make a VS Code theme…

I used to be in the midst of redesigning my web site. I’ve been rocking a brilliant duper outdated design that I’ve wished to vary for years — and I lastly began shifting.

Two overlapping screenshots of the website. The left one is the old design and the right is the new design.

I used Dracula Theme for code snippets in my outdated design and it labored since Dracula was the one factor that supplied a splash of shade in my in any other case stark design.

However it didn’t work effectively with my new website design.

Two overlapping screenshots of a webpage with syntax highlighted code snippets. The left is the old theme and the right is the new theme, which is more colorful.

All I wished to do was to enhance syntax highlighting for the code blocks in order that they’re extra aligned with the remainder of the location.

That was the start of all the things.

Shiki CSS variable theming made it easy

I take advantage of Astro for my web site. Shiki is a syntax highlighter that’s constructed into Astro by default.

With some fast analysis, I noticed Shiki means that you can create themes with CSS variables — and there are solely a handful of colours we have to select.

Showing the 11 CSS variables defined for the Shiki theme.

That doesn’t sound too difficult, so I bought AI to assist flesh out a Shiki theme based mostly on the CSS variables. Right here’s the CSS and JavaScript you want for those who’re utilizing Astro as effectively:

: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;
  shade: 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;
  shade: inherit;
  background: none;
}
import { createCssVariablesTheme } from 'shiki/core'

const shikiVariableTheme = createCssVariablesTheme({
  identify: 'css-variables',
  variablePrefix: '--shiki-',
  fontStyle: true,
})

export default defineConfig ({
  // ...
  markdown: {
    shikiConfig: {
      theme: shikiVariableTheme
    }
  }
})

I did a fast experiment with the colours I had already used for my web site and in contrast it to numerous well-liked themes, like Dracula, Sarah’s Evening Owl, and Moonlight 2.

This gave me the arrogance to push my very own theme a little bit additional — as a result of the syntax highlighting was shaping up in the fitting course.

However, to push this additional, I needed to ditch CSS variable theming and dive into TextMate tokens. It was important as a result of sure code blocks appeared completely horrendous and TextMate tokens present extra granular management of how and what will get shade.

That is the place the “exhausting” half begins.

Getting AI to assist with TextMate scopes

Fortunately, AI is right here to assist. If AI wasn’t right here, I may need simply given up at this level.

Right here’s what I bought my AI to do:

  1. I stated I wished to make a customized theme.
  2. I advised it to create a scaffold for me.
  3. I requested it to search for Moonlight 2’s theme recordsdata as a reference and create the TextMate scope tokens based mostly on that.

I bought it to consolidate the colours used into semantic key phrases like foreground, background, key phrase — just like the Shiki CSS variable theme.

And I requested it to tug all the colours right into a shade object so I can have a palette object that features solely the semantic names.

Right here’s roughly what it created:

const colours = {
  purple: '...',
  blue: '...',
  // ...
}

const palette = {
  foreground: '...',
  background: '...',
  // ...
}

export default {
  colours: {
    // Used for theming the textual content editor
  },
  displayName: 'Show Identify of your Theme',
  identify: 'your-theme-name',
  tokenColors: [
    {
      name: 'Scope name (optional)',
      scope: [/*scopes used*/],
      settings: {
        foreground: /* change shade */,
        background: /* background of the textual content */,
        fontStyle: /* regular, daring or italic */,
      }
    }
  ]
}

You have to present JSON for VS Code to configure issues, so I additionally bought AI to create a construct script that converts the above format right into a .json file.

You’ll find the construct script and all the things I used within the GitHub Repo.

Debugging regionally

It was inconceivable to debug syntax highlighting on my web site as a result of I needed to manually restart the server every time I modified a variable.

So, I requested AI for a suggestion.

It stated that I can use VS Code’s Extension Host for native growth, then proceeded to created a .vscode/launch.json file with the next contents:

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

To run this, you need to use F5 (Home windows) or Fn + F5 (Mac) and a brand new editor window will pop up — on this new window, you’ll be able to change the theme to your customized theme.

Recognizing a window that makes use of the extension host is sort of easy as a result of:

  • When you change your theme, that window will probably be a distinct theme in comparison with your different opened textual content editors.
  • The Extension Host key phrase is distinguished within the title.
Showing the following text: Extension Development host, index.astro, zellwk.com.

Now, all the things has been a blur at this level, so I can’t keep in mind if you must embrace the next into your bundle.json file for theme switching to work within the extension host. If that’s the case, embrace it:

{
  "contributes": {
    "themes": [
      {
        "label": "Your Theme Name",
        "uiTheme": "vs-dark",
        "path": ".json"
      }
    ]
  }
}

Understanding TextMate scopes

At first, I copy-pasted photos and tried to get AI to regulate varied tokens to the colours I selected. However it bought irritating fairly rapidly.

Both:

  • the AI bought the textmate scope flawed, or
  • it was overwritten by one thing else.

I couldn’t inform. However fortunately you’ll be able to debug the TextMate scopes simply with a “Developer: Inspector Editor Tokens and Scopes” command.

VS Code control panel open and highlighting a command called Developer: Inspect Editor Tokens and Scopes.

Whenever you’re on this mode, you’ll be able to click on on any textual content and a window will pop up. This comprises all the knowledge you must modify TextMate scopes.

An inspector popover in VS Code showing information for the color-purple-100 variable.

Right here’s easy methods to learn what’s happening:

  • Foreground: Tells you the present energetic scope. On this case, the energetic scope is variable.
  • TextMate scopes: Tells you what are the accessible TextMate scopes you need to use for this particular token.

TextMate scopes work in an fascinating manner. I found out the next by experimenting, so it won’t be 100% correct:

  1. You should use any a part of the accessible scopes. variable, variable.prop, and variable.prop.css all work.
  2. You may enhance specificity by stating extra properties. variable.prop.css > variable.prop > variable by way of specificity.
  3. The upper scope is extra particular than the decrease one. variable > meta.operate.misc.css.
  4. You may different scopes with them like CSS selectors if you must overwrite a better scope. meta.operate variable > variable

How I selected colours for the theme

That is an important subject when making a theme. There’s no level having the theme if syntax highlighting doesn’t help the developer in studying code.

Two articles come into my thoughts right here:

Basically, the rules that I took away from each articles are:

  • We would like highlights to face out.
  • Colours will look similar to one another for those who make use the identical lightness and chroma, and it’ll be exhausting to inform them aside.
  • If all the things is highlighted, nothing is highlighted.
  • If all the things is essential, nothing is.

Principally, we’re speaking about the precept of distinction when designing. Since I’m already designing for somebody to learn, the very subsequent ideas that got here have been:

  1. How do I information my eyes?
  2. What are essential components that I’ve to see/know?
  3. What components are much less essential?

With that, I started working:

  • Features and strategies have been essential in order that they needed to be sturdy, so I used cyan which is the strongest shade in my palette.
  • The export key phrase can be essential because it signifies an export!
  • Key phrases like import and operate might be relatively muted, so purple it’s.
  • Strings might be inexperienced — cos they appear relatively pleasing in an inventory of textual content inside a JSON file.
Showing JSON configuration for a dependencies object with a list of packages used in the project to illustrate the use of syntax highlighting colors.
If textual content wasn’t inexperienced…this may be exhausting to have a look at.

I performed round with the remainder of the colours a little bit, however I ultimately settled with the next:

  • Constants are orange as a result of it’s kinda straightforward to identify them
  • Variables are white-ish as a result of that’s the majority of the textual content — including colours to them creates the “Christmas Lights Diarrhea” impact Tonsky talked about.
  • Properties are blue as a result of they’re like workhorses that wants shade differentiation, however not sufficient to attract an excessive amount of consideration.
Showing syntax highlighting for JavaScript code.

Then I moved onto HTML/Astro/Svelte:

  • Tags are pink as a result of they’re kinda essential — and pink is simpler to learn that cyan.
  • Attributes are purple for a similar purpose as key phrases.
  • Parts are orange as a result of they have to be completely different from Tags.
  • Bonus factors: Tags and Parts are associated — so pink and orange feels good right here.
Showing syntax highlighting for Svelte code.

And, lastly, CSS syntax highlighting. Virtually all the things appeared proper at this level, besides that:

  • CSS Features must be cyan like that in JS.
  • Punctuation must be muted so we will simply differentiate the -- from the remainder of the textual content.
  • Property might be inexperienced as a result of blue is just too uninteresting on this context — and inexperienced is good on the eyes when contrasted with different highly effective colours.
Showing syntax highlighting for CSS code.

It’s a pity that syntax highlighting for nested lessons goes a little bit bit haywire (they’re inexperienced, however they need to be orange), however there’s nothing a lot I can do about it.

Showing syntax highlighting for CSS code.

Debugging colours

VS Code is constructed on Electron, so it’s straightforward to debug and take a look at colours. What I needed to do was hearth up devtools, examine the colour I wished to vary, and alter them on to get a stay replace!

Wrapping up

A very powerful I factor I discovered throughout this course of is to flow. One opening can result in one other, then one other, and one thing what appears “inconceivable” can turn into “Oh, it’s finished?” in a matter of hours.

I name my theme Twilight Cosmos (AI helped with the naming). You’ll find it on:

How did I publish my extension? That’s the topic of a short follow-up article that I’m engaged on.

Within the meantime, right here’s the GitHub repo if you wish to construct upon no matter I’ve finished. Be happy to counsel edits to enhance this theme too!

Lastly, join my electronic mail e-newsletter for those who’re fascinated by listening to my creation adventures. 🙂

That’s it. Thanks for studying and I hope you had a blast!

Related Articles

Latest Articles