Electric Noctis
One palette,
two editors.
Dark, high-contrast themes for VS Code and Zed.
Built around semantic highlighting.
code --install-extension felixdivall.electric-indigo
- MIT licensed
- Two VS Code themes
- One Zed theme
The look
The same file, three ways
Every sample below is rendered from the theme files themselves, not screenshotted – and rendered twice over, because neither editor stops at the grammar. The second pass is the semantic one, and it is the reason enum members are green here rather than the color of an ordinary variable.
Two of the three are closer than they look. Electric Indigo is not a second palette: it shares every one of the 300 syntax rules with Electric Noctis and differs in the 124 colors around them – the ground, the activity bar, the panels. Same code, lighter room. The Zed build is the one that genuinely diverges, and comments are where you see it.
index.ts
// Barrel file: re-exports, namespace imports, type-only exports.
export * from "./domain/enums";
export * as Types from "./domain/types";
export type { User, UserId, Result } from "./domain/types";
export { UserService, main as runDemo } from "./domain/services";
export { default as ThemeCard, ThemeProvider, useTheme } from "./ui/ThemeCard";
import { App } from "./domain/enums";
if (typeof window !== "undefined") {
window.__THEME__ ??= App.Theme.Mode.Dark;
}
enums.ts
// ─── Numeric enum (auto-incrementing) ─────────────────────────────────────────
export enum Direction {
Up = 1,
Down,
Left,
Right,
}
// ─── String enum ──────────────────────────────────────────────────────────────
export enum LogLevel {
Trace = "TRACE",
Debug = "DEBUG",
Info = "INFO",
Warn = "WARN",
Error = "ERROR",
}
// ─── Const enum (inlined at compile time) ─────────────────────────────────────
export const enum HttpStatus {
Ok = 200,
Created = 201,
NoContent = 204,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
Teapot = 418,
InternalServerError = 500,
}
// ─── Bit flags with computed members ──────────────────────────────────────────
export enum Permission {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
Delete = 1 << 3,
ReadWrite = Read | Write,
All = Read | Write | Execute | Delete,
}
index.ts
// Barrel file: re-exports, namespace imports, type-only exports.
export * from "./domain/enums";
export * as Types from "./domain/types";
export type { User, UserId, Result } from "./domain/types";
export { UserService, main as runDemo } from "./domain/services";
export { default as ThemeCard, ThemeProvider, useTheme } from "./ui/ThemeCard";
import { App } from "./domain/enums";
if (typeof window !== "undefined") {
window.__THEME__ ??= App.Theme.Mode.Dark;
}
enums.ts
// ─── Numeric enum (auto-incrementing) ─────────────────────────────────────────
export enum Direction {
Up = 1,
Down,
Left,
Right,
}
// ─── String enum ──────────────────────────────────────────────────────────────
export enum LogLevel {
Trace = "TRACE",
Debug = "DEBUG",
Info = "INFO",
Warn = "WARN",
Error = "ERROR",
}
// ─── Const enum (inlined at compile time) ─────────────────────────────────────
export const enum HttpStatus {
Ok = 200,
Created = 201,
NoContent = 204,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
Teapot = 418,
InternalServerError = 500,
}
// ─── Bit flags with computed members ──────────────────────────────────────────
export enum Permission {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
Delete = 1 << 3,
ReadWrite = Read | Write,
All = Read | Write | Execute | Delete,
}
index.ts
// Barrel file: re-exports, namespace imports, type-only exports.
export * from "./domain/enums";
export * as Types from "./domain/types";
export type { User, UserId, Result } from "./domain/types";
export { UserService, main as runDemo } from "./domain/services";
export { default as ThemeCard, ThemeProvider, useTheme } from "./ui/ThemeCard";
import { App } from "./domain/enums";
if (typeof window !== "undefined") {
window.__THEME__ ??= App.Theme.Mode.Dark;
}
enums.ts
// ─── Numeric enum (auto-incrementing) ─────────────────────────────────────────
export enum Direction {
Up = 1,
Down,
Left,
Right,
}
// ─── String enum ──────────────────────────────────────────────────────────────
export enum LogLevel {
Trace = "TRACE",
Debug = "DEBUG",
Info = "INFO",
Warn = "WARN",
Error = "ERROR",
}
// ─── Const enum (inlined at compile time) ─────────────────────────────────────
export const enum HttpStatus {
Ok = 200,
Created = 201,
NoContent = 204,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
Teapot = 418,
InternalServerError = 500,
}
// ─── Bit flags with computed members ──────────────────────────────────────────
export enum Permission {
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
Delete = 1 << 3,
ReadWrite = Read | Write,
All = Read | Write | Execute | Delete,
}
Enums, const enums and bit flags – where semantic tokens earn their keep. TypeScript only: it is the one language whose semantic tokens can be reproduced here exactly.
Why
A theme is only half of it
A grammar can tell a string from a number, but it cannot tell an enum member from a constant, or a type from the variable holding one – it has not read your code, only matched it. Semantic tokens come from the language server, which has, and both editors merge them over the top.
Which is why these themes lean on them, and why the Zed one needs a few lines of
settings.json as well as the theme file. Without that block the colors
below are still the theme, but types stop being gold and properties stop being
distinct from everything else.
Zed
The same theme, outside VS Code
Electric Noctis for Zed is not on Zed's extension registry – it is a file. Put it in your themes directory and Zed picks it up, then choose it in the theme selector.
curl -o ~/.config/zed/themes/electric-noctis.json https://felixdivall.com/themes/electric-noctis.json
Then the half that lives in your settings, which is what paints types, properties,
parameters and anything async:
{ "semantic_tokens": "combined", "global_lsp_settings": { "semantic_token_rules": [ { "token_type": "interface", "token_modifiers": ["defaultLibrary"], "style": ["type.builtin", "type.interface"] }, { "token_type": "class", "token_modifiers": ["defaultLibrary"], "style": ["type.builtin", "type"] }, { "token_type": "type", "token_modifiers": ["defaultLibrary"], "style": ["type.builtin", "type"] }, { "token_type": "interface", "style": ["type.interface", "type"], "foreground_color": "#FFD700" }, { "token_type": "class", "style": ["type.class", "type"], "foreground_color": "#FFD700" }, { "token_type": "type", "style": ["type"], "foreground_color": "#FFD700" }, { "token_type": "enumMember", "token_modifiers": ["readonly"], "foreground_color": "#B8FB71" }, { "token_type": "enumMember", "style": ["constant", "property"] }, { "token_type": "property", "foreground_color": "#FFEE80" }, { "token_type": "parameter", "foreground_color": "#AAF5FF" }, { "token_type": "function", "token_modifiers": ["async"], "foreground_color": "#FE9B70" }, { "token_type": "method", "token_modifiers": ["async"], "foreground_color": "#FE9B70" }, { "token_type": "namespace", "foreground_color": "#FFD73D" } ] } }
Get it
code --install-extension felixdivall.electric-indigo