
VS Code Theme Collections
Twenty-five VS Code palettes across two collections — eleven Zimbabwean cities and seven African countries, dark and light.
Two VS Code theme extensions — 25 palettes anchored to Zimbabwean cities and African countries, dark and light, hand-tuned for readability and dual-published to the Marketplace and Open VSX by GitHub Actions.

Overview
VS Code Theme Collections is a pair of Visual Studio Code colour theme extensions built on one premise: an editor should feel like somewhere rather than nowhere. Instead of abstract palettes, each theme is anchored to a real place and named for it — basalt and spray at the Victoria Falls gorge, jacaranda haze over Harare, warm Bulawayo sandstone, the Namib desert under moonlight, granite drawn from Great Zimbabwe, the Maasai Mara at dusk. Where a country has national colours those are used as seeds rather than as the finished palette: the boldest hue is assigned to keywords, a secondary to strings, and muted slate tones to comments and line numbers, then every colour is pulled toward the landscape the theme is named for so backgrounds carry sand, granite or dusk instead of flat black and white.
The Zimbabwe Theme Collection ships eleven city palettes — six dark and five light — across Harare, Bulawayo, Chitungwiza, Mutare and Victoria Falls. The African Themes Collection ships fourteen, a matched dark and light variant for each of Rwanda, Kenya, Ghana, South Africa, Zimbabwe, Nigeria and Namibia. Twenty-five themes in total, and none of them contain runtime code: every one is a plain JSON document holding a workbench colour map — between twenty-nine and ninety-five keys covering the activity bar, side bar, tabs, status bar, panels, gutter and cursor — alongside TextMate token rules for comments, keywords, strings, functions and types, so chrome and syntax are tuned as one palette rather than drifting apart. Contrast was worked out by hand against real source files rather than generated from seed colours, because generated accents tend to collapse in practice: comments too loud, strings and types too close together, punctuation invisible.
Both extensions share the same distribution pipeline, and it is fully automated. A push to the manifest or the themes directory on the main branch triggers a GitHub Actions workflow that packages the extension into a VSIX on a clean Node 20 runner, reads the version out of the manifest, cuts a GitHub release tagged from that version with generated notes and the package attached, then publishes to the Visual Studio Marketplace through vsce and to Open VSX through ovsx. The second registry matters in practice: Cursor, VSCodium and Gitpod users resolve extensions from Open VSX, and dual publishing means they get the same build on the same day rather than a stale fork. Each publish step is guarded on its token being present, so a fork without credentials still builds. Both collections are MIT licensed.
Problem
Editor themes tend to be placeless — generated from a few seed colours, or built from raw flag palettes that look recognisable in a screenshot but collapse under real code.
Solution
Two collections of hand-tuned JSON themes anchored to real Zimbabwean cities and African countries, seeded from national colours but reassigned by syntax role, with releases automated end-to-end from a version bump.
Outcome
Twenty-five themes across two MIT-licensed extensions, published to both the Visual Studio Marketplace and Open VSX from a single automated release so VS Code, Cursor and VSCodium users install the same build.
Screenshots


Architecture & flows
How it fits together
Press play to walk through a flow — each pseudo-code line lights up the parts of the system it touches.
Place palettes
cities + countries
Theme JSON
25 x colors + tokens
Extension manifests
contributes.themes
GitHub Actions
release workflow
VS Code Marketplace
vsce publish
Open VSX
ovsx publish
GitHub Releases
tagged .vsix
VS Code / Cursor
theme picker
- Service
- Infra
- External
- Client
Palette to two registries
How a colour tweak in one place palette becomes an installable release on both marketplaces without a manual publish step.
A palette is written straight into the theme file as workbench colours and token scopes; seed hues are assigned to syntax roles before any chrome colour is picked.
Decisions
Engineering decisions
One extension per collection, not one per place
- Context
- Every city and country palette could have shipped as its own extension, giving each theme a dedicated marketplace listing, its own README and independent versioning.
- Decision
- Ship one extension per collection, registering all eleven Zimbabwe themes and all fourteen African themes through the manifest theme contribution point.
- Consequence
- Trying a different place is a picker change rather than another install, and each collection reads as one body of work; in exchange, retuning a single palette republishes everything under a new version.
Hand-tuned contrast, with flag colours as seeds rather than the finished palette
- Context
- Deriving a theme algorithmically from a few seed colours is fast, and dropping national flag hex values straight in is the obvious shortcut — but both produce accents that collapse in real code: comments too loud, strings and types too close together, punctuation invisible.
- Decision
- Assign seed hues to syntax roles first, then desaturate and shift them for backgrounds, comments and inactive chrome using tones drawn from the landscape each theme is named for, stress-testing every colour against real source files.
- Consequence
- Themes stay identifiably rooted in a place while surviving a long working session; the cost is that adding a place is slow and manual, reasoned about per palette rather than copied mechanically.
A light variant for every country, not dark only
- Context
- Dark themes are the default expectation and roughly half the work, so many collections ship dark only and add light later, if ever.
- Decision
- Treat dark and light as a pair per country in the African collection and hold it to complete pairs before adding the next country.
- Consequence
- The collection covers bright rooms, projectors and pair-programming as well as night work; each new country costs twice the tuning effort.
Version-driven releases published to both registries from one push
- Context
- Manual packaging and publishing is the step that gets skipped, and doing it twice — once for the Microsoft marketplace and once for Open VSX — is exactly the kind of chore that leaves a listing behind the repository.
- Decision
- Drive the whole release from a path-filtered workflow on main that packages the VSIX, tags a GitHub release from the manifest version, and publishes to the Marketplace and Open VSX in the same run, guarding each publish step on its token so a missing credential skips a registry instead of failing the build.
- Consequence
- Shipping is reduced to bumping a version and pushing, and the two registries never drift apart; the trade-offs are two publisher credentials to maintain and a duplicate-tag failure whenever a version bump is forgotten.
Integrations
Notable integrations
VS Code theming API
Supplies the colour theme contribution point that both extensions are built on.
Each theme JSON file provides a workbench colour map and TextMate token rules; the manifest registers the files with a display label and a light or dark UI base, and the editor loads the selected one at runtime.
vsce (Visual Studio Marketplace)
Packages each collection and publishes it to the official marketplace.
The release workflow runs the packaging command on a Node 20 runner to produce a VSIX, then publishes it with a publisher token held as a repository secret.
ovsx (Open VSX Registry)
Mirrors every release to the open registry that Cursor, VSCodium and Gitpod resolve from.
A second publish step pushes the same packaged version to Open VSX, conditioned on an Open VSX token being present so forks without one still build.
GitHub Actions + Releases
Turns a push into a versioned, downloadable release with no local tooling.
A workflow triggered by changes to the manifest or the themes directory checks out the repository, sets up Node, packages the extension, reads the version from the manifest, and creates a tagged release with generated notes and the VSIX attached before the two publish steps run.
Stack
- VS Code API
- JSON
- TextMate grammars
- GitHub Actions
- vsce
- ovsx
- Node.js