Why another theme pack
| Most theme extensions |
Modern Themes |
| One theme per extension |
24 themes in one package, 16 dark and 8 light |
| Switch through Settings, then restart your eyes |
Ctrl+K Ctrl+M, arrow keys, every theme applies instantly |
| No idea which theme you are on |
Active theme in the status bar, check mark in the picker |
| Colors picked by hand, contrast checked by hope |
Every color solved against the surface it sits on, verified by 9289 automated checks |
| Comments you cannot read |
Comments hold 4.6:1 in all 24 themes |
| Stuck once you install it |
One click back to the stock VS Code theme |
| Colors only, font is your problem |
A font picker too, showing what is actually installed on your machine |
Install
Grab the .vsix from Releases:
code --install-extension modern-themes-1.0.0.vsix
Or build it yourself:
npm install
npm run build && npm run icon && npm run preview && npm run package
code --install-extension modern-themes-1.0.0.vsix
Usage
Press Ctrl+K Ctrl+M. That is the whole thing.
| Shortcut | What it does |
Ctrl+K Ctrl+M | Open the picker. Arrow keys preview live, Enter keeps, Esc cancels |
Ctrl+K Ctrl+→ | Next theme |
Ctrl+K Ctrl+← | Previous theme |
Everything is also in the Command Palette under Modern Themes:
- Browse Themes (Live Preview) — the picker
- Surprise Me (Random Theme) — for when you cannot decide
- Next Theme / Previous Theme — step through the set
- Pick a Coding Font — the font picker
- Back to the VS Code Default Theme / Back to the Default Font — clears the setting entirely
Knowing where you are
The status bar, bottom right, shows two things: the active theme and the active
font. Click either to open its picker. Inside a picker the current entry carries
a check mark and reads current, so previewing others never loses your place.
Changing your mind
Esc while previewing restores whatever you had before you opened the picker.
To leave the pack entirely, pick Back to the VS Code default at the bottom of
the list. It clears workbench.colorTheme, so VS Code returns to the theme it
shipped with. The themes stay installed, one keystroke away.
Fonts
A theme changes the colors. The font changes the shapes, and the two together are
what the editor actually feels like. Three ways in, whichever you notice first:
- Click the font name in the status bar, bottom right, next to the theme name
- Command Palette (
Ctrl+Shift+P) then Modern Themes: Pick a Coding Font
- The
Aa button in the top right of the theme picker
Same behaviour as the theme picker: arrow keys preview live, Enter keeps,
Esc restores.
It can install the font for you
editor.fontFamily only resolves fonts registered with the operating system.
A font sitting inside an extension is invisible to it, and no extension can
inject @font-face into the workbench. The circulating workaround patches VS
Code's own CSS, which marks the install corrupted and breaks on every update.
So instead of pretending, this puts the file where the OS looks. Four faces ship
inside the extension and install to your user account on click:
| Font |
Ligatures |
|
| JetBrains Mono |
yes |
Tall x-height, built for long reading sessions |
| Victor Mono |
yes |
Cursive italics, so the italic comments in these themes really sing |
| Fira Code |
yes |
The one that made programming ligatures popular |
| Source Code Pro |
no |
Conservative and very legible |
No administrator prompt is needed. Windows has supported per-user font installs
since Windows 10 1803 (%LOCALAPPDATA%\Microsoft\Windows\Fonts plus an HKCU
registry value); macOS reads ~/Library/Fonts, fontconfig reads
~/.local/share/fonts. VS Code enumerates fonts when its renderer starts, so
the picker offers a window reload once the file is in place.
The picker splits everything into three groups:
- Installed — previews live, applies immediately
- Bundled, ready to install — the four above, one click, nothing to download
- Not installed on this machine — inert in the list, and picking one opens a
download page rather than quietly applying a family that falls back
Ligatures are switched on or off to match the face you pick. Set
modernThemes.manageFontLigatures to false to decide that yourself.
Also in the list
Not bundled, but detected if you already have them, with a download link if you
do not: Geist Mono,
Cascadia Code,
Commit Mono,
Iosevka,
IBM Plex Mono,
Hack, plus whichever system faces your
platform has: SF Mono and Menlo on macOS, Consolas on Windows, DejaVu Sans Mono
on Linux.
MonoLisa and
Berkeley Mono are
commercial, so they are listed but never bundled; picking one opens its purchase
page.
Roboto Mono, Arial and Helvetica derivatives are deliberately absent. The design
rules this project follows ban those families as default reaches.
The themes
Dark
Light
Every theme, full size
|
|
|
 Modern Midnight Cool slate-blue |
 Modern Cobalt Deep navy |
 Modern Ink Near-black indigo |
 Modern Nebula Violet-grey |
 Modern Orchid Orchid magenta |
 Modern Rose Plum and deep rose |
 Modern Olive Olive and brick |
 Modern Ember Burnt orange |
 Modern Amber Warm charcoal |
 Modern Citron Cool charcoal, citron |
 Modern Graphite Monochrome, one pop |
 Modern Moss Forest floor |
 Modern Jade Jade green-teal |
 Modern Forest Deep green |
 Modern Abyss Deep teal |
 Modern Aurora Glacier cyan |
 Modern Paper Cool grey and cobalt |
 Modern Linen Slate and terracotta |
 Modern Bone Bone and forest green |
 Modern Mist Cool mist teal |
 Modern Meadow Pale green |
 Modern Coral Warm white and coral |
 Modern Blossom Blossom pink |
 Modern Iris Pale iris magenta |
Every preview above is rendered directly from the theme's own color values, so
what you see is the palette itself, not an artist's impression of it.
The extension icon works the same way. The outer ring is one swatch per theme at
that theme's real accent color, ordered by hue, and the even spacing is the
15-degree minimum separation the audit enforces. The split disc in the middle is
the thesis: a color, and the surface under it.
Every color is solved, not picked
This is the part that makes the pack worth installing.
A theme here is not a list of hex codes. It is a neutral hue, a single
accent, a syntax hue ring, and a depth — and everything else is
derived. For each color the generator walks lightness until it clears a contrast
target against the surface that color actually sits on:
// Walk lightness until the color clears `target` contrast against `bg`.
function solve (h, s, bg, target) {
const dark = luminance(bg) < 0.18
let l = dark ? 40 : 60
for (let i = 0; i < 400; i++) {
const c = hsl(h, s, l)
if (contrast(c, bg) >= target) return c
l = clamp(l + (dark ? 0.5 : -0.5), 0, 100)
}
}
That last part matters more than it sounds. An inactive tab label does not sit
on the editor background, it sits on the tab strip. Solving it against the wrong
surface is how themes end up with tab labels at 2.8:1. Here every ramp is solved
against its own surface.
What that buys you, in all 24 themes:
|
Ratio |
|
| Default text |
~10:1 |
past the AAA target of 7 |
| Syntax tokens |
≥5.5:1 |
comfortably past AA |
| Comments |
≥4.6:1 |
the line most themes fail |
| Button and badge labels |
≥4.6:1 |
accent lightness is fitted to guarantee it |
| Chrome text |
≥4.5:1 |
against its own surface, not the editor's |
The audit
build/audit.js runs 9289 checks and exits non-zero on any violation:
$ npm run audit
ran 9289 checks across 24 themes
PASS: contrast, forbidden colors, hue lock and accent lock all hold
It enforces:
- Contrast for every token, status color, git decoration and chrome pair
- No pure black, no pure white anywhere — pure values kill depth
- One neutral hue per theme, so warm and cool greys never mix in one theme
- One accent per theme, saturation under 80, identical everywhere it appears
- 15 degrees minimum between any two same-type accents, so no two themes
read as the same family
- No banned palettes — the beige/brass/espresso premium-consumer family is
rejected outright
- Syntax role separation, flagging any two roles too close to tell apart
Design rules
The themes were built against a UI/UX rule set (kept outside this repo, under
.agents/skills) treated as hard constraints rather than suggestions. The audit exists because a
rule you only promise to follow is a rule you break.
Some things those rules changed:
- Two planned themes used the banned beige/brass/espresso palette. Cut, and
replaced with approved families (Forest, Cobalt + Cream, Terracotta + Slate).
- A light theme used
#ffffff. Now nothing does; the lightest surface is 99.2%.
- Three themes shipped button labels between 4.08:1 and 4.44:1. Accent fill
lightness is now fitted until a legible label exists.
- Keyword and tag colors were 23 degrees apart and read identically in three
themes. The syntax ring was widened to 45 degrees.
- Shadows are tinted to each theme's neutral hue. None are black.
- Violet is used three times in sixteen dark themes, on purpose, never as a
default reach.
Development
npm run build # palettes -> themes/*.json, and rewrites contributes.themes
npm run audit # 9289 design checks, exit 1 on any violation
npm run preview # re-renders every theme image in media/
npm run icon # re-renders the extension icon from the live accent colors
npm run package # builds the .vsix
To change a theme, edit the palette in build/palettes.js —
never themes/*.json, which is generated output and gets overwritten.
A palette is five numbers and a name:
{
id: 'midnight',
label: 'Modern Midnight',
type: 'dark',
family: 'Cool slate-blue, electric blue accent',
neutral: { h: 220, s: 20 }, // every grey in the theme comes from this hue
accent: { h: 212, s: 72 }, // the one accent, saturation under 80
rot: 0, // rotates the syntax hue ring
ssat: 62, // syntax saturation
depth: 0 // slides every surface lighter or darker
}
Add one to the array, run npm run build && npm run audit, and the theme is
contributed, validated and rendered.
Settings
| Setting |
Default |
Description |
modernThemes.showStatusBarItem |
true |
Show the active theme in the status bar |
modernThemes.manageFontLigatures |
true |
When you pick a font, match editor.fontLigatures to what it supports |
License
The code and the themes are MIT.
The four bundled fonts are not. Each keeps its own SIL OFL 1.1 licence,
included beside it in fonts/ along with the details. The OFL
explicitly permits bundling a font with software when the copyright notice and
licence travel with it and the font is not sold on its own, which is the case
here. The files are unmodified variable builds.