Open-source project
catppuccin/vscode avatar
catppuccin/vscode

catppuccin/vscode: a colour theme that writes its own files, and the packaging problem that creates

🦌 Soothing pastel theme for VSCode

2,267 stars87 forksTypeScriptMIT

At a glance

What is it?
Catppuccin for VS Code ships four flavours across three separate artefacts, a theme extension, an icon pack and an npm package of theme JSON, and it configures the editor chrome by how many shades of the palette ramp it is allowed to use. The interesting engineering is not the palette, it is the fact that the extension writes generated files at runtime, which is why declarative Nix users are pushed to a separate module.
Who is it for?
Catppuccin for VS Code is a good choice if you want a coherent palette across the editor chrome, the terminal and your other tools, and you are willing to turn on semantic highlighting and hand the terminal palette over entirely. It is a poor fit for a declarative Nix setup taken straight from the package set, because the extension expects a writable directory and the documentation is explicit that the usual escape hatch does not work for it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Four flavours shipped as three separate artefacts

The palette comes in four named variants, and they are not variations on a single scheme. Latte is the light one, and the other three, Frappé, Macchiato and Mocha, are dark, each with its own screenshots in the preview section of the README. Mocha is the one the documentation uses for every example, which is a reasonable signal about which variant most users end up on.

The distribution story is less obvious, because the same colours reach you three ways and the repository reflects that with three release streams. There is a theme extension, an icon pack published separately as its own extension, and a plain npm package. The release history shows the theme extension and the npm package versioned together and tagged within a second of each other, while the patch release before them is dated in the previous autumn, so the two move in lockstep on a slow cadence rather than independently.

The npm package exists for a specific audience and it is not end users. It is described as the place to get the theme JSON files if you need them for a library such as a syntax highlighter for the web, where a VS Code extension is useless but the colour definitions are exactly what you want. A separate icon pack is also mentioned in a note near the customization section, so a full Catppuccin setup in the editor is two extensions rather than one.

Installation has three documented paths. Two are the obvious ones, a listing on the Visual Studio Marketplace and a listing on the alternative extension registry. The third is manual: download the packaged extension file from the latest GitHub release and install it from the command palette. That third path exists mainly for the Nix situation described below, and it is the reason the repository publishes packaged artefacts to releases at all rather than treating the marketplace as the only distribution channel.

Why the theme writes files, and why Nix users cannot use the package set

Read the Nix section carefully, because it explains a design decision that is otherwise invisible.

The extension expects to have a mutable directory it can write its JSON files into. That is not incidental. The theme exposes settings that generate colour values at runtime, including per-flavour overrides and a free choice of accent colour, and generating a colour means producing a JSON file with the resolved values. So the extension is a program that writes configuration as a side effect of running, and every packaging system that mounts a read-only, hash-addressed extension directory is going to have an opinion about that.

The documentation does not soften the consequence. The usual escape hatch, marking the extension directory as mutable, is named and rejected outright: the project states that using that option will not work. Instead it offers exactly two paths, and they are opposites.

The first is to abandon declarative installation for this one extension. You keep everything else under your configuration, and you fully exclude this particular extension from it so that it installs itself and has permission to write. The documentation is specific that a partial exclusion is not enough.

The second is to keep the installation declarative and move the generation to build time, which is what the project's own Nix module is for. You point your inputs at the module, enable it, and pass the options you want, and the theme is compiled with those settings baked in rather than writing anything later. The example configuration shows the shape:

nix
{
  # in your inputs:
  inputs.catppuccin.url = "github:catppuccin/nix";

  # in your home-manager options:
  catppuccin = {
    enable = true;
    # optionally configure the extension settings, defaults are shown below:
    vscode = {
      accent = "mauve";
      settings = {
        boldKeywords = true;
        italicComments = true;
        italicKeywords = true;
        colorOverrides = {};
        customUIColors = {};
        workbenchMode = "default";
        bracketMode = "rainbow";
      };
    };
  };
  programs.vscode = {
    enable = true;
  };
}

Notice what the option names mirror. The Nix module takes an accent colour and a settings object whose keys are the same setting names you would write in the editor, including the colour override and custom UI colour maps. That one-to-one mapping is the tell that this module was written to be the declarative twin of the runtime behaviour, not a separate configuration language.

The practical lesson generalises past Nix. If you package editor extensions in any system that expects immutable directories, a theme that generates its own configuration is going to need either an exclusion or a build time step, and knowing which of the two your setup requires is the first thing to check.

workbenchMode turns a colour ramp into a depth setting

The most interesting design idea in the extension is one setting. The Catppuccin palette defines three base shades, and the theme uses all three for the editor chrome by default. In one flavour the values are given explicitly: the editor background is the base colour, the sidebar is the mantle, and the activity bar, status bar and title bar are the crust.

The setting changes how many of those three shades the chrome is allowed to use. The default uses all three. The flat mode uses two, dropping the crust, so the activity bar, status bar and title bar all take the mantle shade. The minimal mode uses one, the base colour, for the entire workbench.

That is a better design than a set of individual colour overrides, and the reason is worth stating. Someone who finds the default workbench too busy is not asking for the activity bar to be a specific colour. They are asking for less contrast between regions. Expressing that as a depth setting means one choice covers every region consistently, and it means a new editor surface added in a future version picks up the right shade without anyone maintaining another override. The per-flavour override mechanism still exists underneath for anyone who genuinely wants a different colour, and the two compose.

The same pattern shows up in the accent. There is a dedicated setting for the accent colour, any colour at all, with a default the documentation names, and the examples use a different one to show the effect. The accent is then referenced by name from elsewhere rather than copied, so a single change to the accent propagates. The example overrides the status bar foreground to refer to the accent by name, which is the mechanism in miniature: the theme exposes a palette of roles and lets other settings point at roles rather than at hex values.

The recommended editor settings change the result more than the theme does

The extension ships a short list of editor settings that it considers necessary, and reading the comments explains why each one exists.

Semantic highlighting is enabled, with the comment that the theme tries to make it look good. That phrasing is a warning as much as a boast. A theme that ships colours for the classic token categories will look wrong against a semantic token set it was not designed for, so this is the setting to turn on before deciding the theme is broken.

The terminal setting is the one with a real consequence. The minimum contrast ratio for the integrated terminal is set to one, with the comment that this prevents the editor from modifying the terminal colours. Left alone, the editor can adjust terminal colours to meet a contrast target, and an editor that has been told what colours your terminal uses will quietly rewrite them. For a theme whose whole proposition is a consistent palette, an editor that re-tints your terminal is not a feature, and this line is what stops it. The cost is that you also lose the contrast adjustment, which is a real accessibility feature on some displays, so it is a setting to change knowingly.

The title bar style is set to custom so the window chrome picks up the workbench colours, which connects directly to the depth setting described earlier. And there is one language specific entry: a flag for the Go language server to emit semantic tokens, marked in the documentation as opt-in and applicable only if you write Go.

The combined picture is a theme that needs cooperation from the editor to look correct, and the documentation is honest that this is a requirement rather than a suggestion. A second block of settings then layers the Catppuccin specific options on top, choosing a flavour by its full theme name, setting the accent, overriding three base shades for one flavour only, and pointing a UI colour at the accent:

jsonc
{
  // use Mocha as the base
  "workbench.colorTheme": "Catppuccin Mocha",
  // pink as the accent color
  "catppuccin.accentColor": "pink",
  // make Mocha specifically very dark
  // (this preserves other flavors!)
  "catppuccin.colorOverrides": {
    "mocha": {
      "base": "#000000",
      "mantle": "#010101",
      "crust": "#020202",
    },
  },
  // use your accent (pink) on the statusBar as well
  "catppuccin.customUIColors": {
    "mocha": {
      "statusBar.foreground": "accent",
    },
  },
}

Two things about that override block matter more than the hex values. The keys are flavour names, so a per-flavour change leaves the other three variants exactly as they were. And the UI colour override refers to the accent by name rather than repeating the hex, which is the indirection that makes a single accent setting useful across the whole interface.

A monorepo, a Storybook and an Open Collective account

The repository is set up like an application, not like a colour file, and the details explain what maintaining a theme in 2026 actually involves.

The root manifest is marked private and named as a monorepo, with a workspace directory listing the packages. Two are named in the type check script: the extension itself and a Storybook package for it. A Storybook for a colour theme means the editor chrome is rendered as components in a browser, so a contributor can see what a colour change does without launching the editor. For a project whose output is visual, that is the difference between reviewing a pull request in ten minutes and reviewing it by rebuilding and reinstalling the extension.

The build tooling is current and specific. The package manager is pinned to a recent major version, the Node engine requirement is 20 or newer, and several dependencies are referenced through a shared catalogue rather than pinned individually, which is how a workspace keeps one version of a shared tool. The extension is packaged by a postinstall step, so a fresh clone produces a loadable extension artefact without a separate manual command.

Quality tooling is wired into the commit path. A hook system runs on install, staged files are linted and formatted automatically, the formatter ignores certain paths, and there is an explicit editor configuration file. The lint step is not just a linter: it runs the type check across both packages as part of the lint command, so a type error fails the same check as a style error. There is also a dependency update configuration, which for a repository with a long dependency list is the difference between staying current and drifting.

Two smaller entries are worth naming. There is a Nix shell file and a flake lock, so developing the project requires Nix rather than a hand-built Node environment. And the funding is set up through an Open Collective account, which is the mechanism the wider Catppuccin project uses rather than a personal donation link.

The one entry that looks like an accident is a dependency metadata override marking one package as not to be built. That is the sort of thing that ends up in a manifest because a native module fails to compile somewhere, and it is a reasonable reminder that even a project with no runtime logic of its own accumulates dependencies it does not directly control.

Editorial conclusion

Catppuccin for VS Code is a good choice if you want a coherent palette across the editor chrome, the terminal and your other tools, and you are willing to turn on semantic highlighting and hand the terminal palette over entirely. It is a poor fit for a declarative Nix setup taken straight from the package set, because the extension expects a writable directory and the documentation is explicit that the usual escape hatch does not work for it. Install it if you read the settings rather than accept the defaults, since the recommended editor settings change how the theme looks and one of them prevents the editor from rewriting your terminal colours. If you are on Home Manager, use the project's own module rather than a mutable extensions directory, and confirm which of the three artefacts you actually need before pulling all of them in.

Frequently asked questions

How do I install the Catppuccin theme for VS Code?

Install the extension from the Visual Studio Marketplace or from the Open VSX registry. You can also download the packaged extension file from the latest GitHub release and install it from the command palette with the install from VSIX command, which is the path the Nix instructions rely on.

Why does the Catppuccin VS Code extension not work with a mutable extensions directory?

Because the extension writes its generated theme JSON into a directory it needs permission to write, and the project states that setting the extensions directory to mutable will not work for it. You must either exclude the extension from your declarative configuration entirely, or use the project's own Nix module to compile the theme with your settings baked in.

What is the difference between the workbench appearance modes?

The default mode uses all three base shades, with the base colour for the editor background, the mantle for the sidebar and the crust for the activity bar, status bar and title bar. Flat uses two shades by dropping the crust, and minimal uses only the base colour for the whole workbench.

Can I use the Catppuccin theme JSON in a web syntax highlighter?

Yes. The theme files are published to npm as a package so tools such as a web highlighter can read them directly, which is separate from the editor extension and from the separate icon pack extension.

Which VS Code settings does the Catppuccin extension recommend?

Enable semantic highlighting, set the integrated terminal minimum contrast ratio to one so the editor stops modifying terminal colours, and set a custom title bar style so the window chrome uses the workbench colours. A Go specific option enables semantic tokens from the Go language server.

Official sources

  1. catppuccin/vscode on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/catppuccin-vscode.svg)](https://hysenlabs.com/projects/catppuccin-vscode)