Making a VSCode theme the smart way
Tags: vscode, themes, extensions, tutorial
Published: 2024-01-17 19:27:51

Making a VSCode theme is relatively simple - all you really need is a single `.json` file. 

However, this comes with a whole bunch of problems, the biggest one being the fact that you can't use variables in a `.json` file. What if you want to change a color that was used in multiple places? You'd have to manually replace every single instance of that color.

Now you might think, well, with find & replace, that doesn't sound so bad. But what if you want to make multiple themes? You'd have to copy the entire `.json` file, and then change every hex value manually!

When I made [my own theme](https://marketplace.visualstudio.com/items?itemName=zevross.lightning) for VSCode, I already knew that I wanted to make many variants, so I wanted to find a way to use variables to make themes instead of writing every hex value literally.

So now that I've figured out how to do it, I'd like to share that method so that anyone else who wants to make their own theme knows how.

## Setting up the project

The first thing you'll need to do is to create the project itself. Detailed instructions can be found [on the VSCode documentation](https://code.visualstudio.com/api/extension-guides/color-theme#create-a-new-color-theme), but basically all you have to do is type the following in your terminal and VSCode's yeoman cli will help you generate the rest.
```bash
npm install -g yo generator-code
yo code
```
However, instead of creating a `.json` file, create a new TypeScript (or Javascript) file in your `src/` folder, and name it whatever you want.

## Making the theme

In this file, you can define all your color variables. Then, make a function called `getTheme()` which returns a JSON object. This JSON object should be in the format of a theme JSON, meaning it should specify the name, whether it's a dark, light, or high contrast, and the colors that the theme consists of. Then, export the function you just made, like so:
```js
// src/mytheme.js

const blue = "#2D3079"
const blue2 = "#2C2E58"
const white = "#FFFFFF"
const black = "#000000"
// define as many colors as you want...

function getTheme() {
  return {
    "name": "My theme",
    "type": "dark",
    "colors": {
      "editor.background": blue,
      "editor.foreground": white,
      "editorIndentGuide.background": blue2,
      "editorRuler.foreground": white,
      "activityBarBadge.background": black,
      "sideBarTitle.foreground": white,
      // ... add as many changes as you want
  },       
}

module.exports = getTheme
```

If you want to use translucent colors, you can either hardcode the alpha value at the end of the existing hex values, or you can use a helper function. For mine, I used the `colors-convert` package.
```bash
npm install colors-convert
```
```js
// src/mytheme.js
const { hexToHexWithAlpha } = require("colors-convert");

function hex(color, alpha = 1) {
  return hexToHexWithAlpha(color, alpha);
}

// example usage:
    "list.dropBackground": hex(blue2, 0.3), 
```
Once you've customized all the color values (a detailed description of which can be found [here](https://code.visualstudio.com/api/references/theme-color)), there's still more work to be done. First, we need to go into `index.js` to actually create the `.json` file from the function we just made.
```js
// src/index.js
const getTheme = require("./mytheme")
// ... import any other themes you made
const fs = requre("fs").promises
// generate the json and store it in a const
const myTheme = getTheme()
// ... do the same for any other themes you've made

// write the .json file
fs.mkdir("./themes", { recursive: true })
  .then(() =>
    Promise.all([
      fs.writeFile(
        "./themes/my-theme.json",
        JSON.stringify(myTheme, null)
      ),
      // call fs.writeFile() again on all your themes
    ])
  )
  .catch(() => process.exit(1))
```
Now, if you run the extension in debug mode by pressing F5, you should see a new `themes/` directory with a brand new `.json` file in it!

All you have to do now is register your theme in your `package.json` file:
```json
// package.json
{
  // ...
  "contributes": {
    "themes": [
      {
        "label": "My theme",
        "uiTheme": "vs-dark", // or "vs" if it's a light theme
        "path": "./themes/my-theme.json" 
      } 
      // ...repeat for the rest of your themes
    ]
  }
}
```
And congratulations! You've just created a whole bunch of themes. Just remember that if you make any changes, **you must run F5 again to write the changes to the actual .json files** or none of the changes you made will actually change the theme.

To see a full example, check out my theme, [lightning](https://github.com/Zev18/lightning), on github. Or, you know, just install it and give me a rating if you like how it looks!

Happy coding!
Zev