NEON
FERATU

One palette file, one build step, every app you actually use.

The count is dead. Editors, terminals, Claude Code and your pager, all painted from the same twenty-odd colors — and a build that refuses to ship them if they drift.

$git clone https://github.com/espresso20/neonferatu && ./install.py
zsh — neonferatu
~/code/neonferatu $ python3 build/build.py
palette check ok — 3 variants, contrast floors, accent
separation, ramp, no raw hex
Neonferatu          v1.0.0 — 8 files → dist/
Neonferatu Amber    v1.0.0 — 8 files → dist/variants/amber/
Neonferatu Phosphor v1.0.0 — 8 files → dist/variants/phosphor/

~/code/neonferatu $ ./install.py
 Monaspace Krypton
 ~/.claude/themes/neonferatu.json
 VS Code extension + settings
 iTerm2 dynamic profile — live, no restart

~/code/neonferatu $ 

The palette

measured, not vibed

Every color is placed in OKLCH and checked at build time. Contrast is against this tube's editor background; the build fails if body text drops under 4.5:1, if the background ramp stops climbing, or if two syntax colors get close enough to be mistaken for each other.

keyrolehexL*chromahuecontrast
neon.fangkeywords, cursor
neon.chromefunctions, links
neon.hextypes, classes
neon.acidstrings
neon.sodiumnumbers, constants
neon.wounderrors, deletions
fg.phosphorbody text
fg.mutedcomments
bg.voideditor background

Five accents, and that's the budget

The hierarchy is short on purpose. If a sixth accent creeps in, we've lost the plot.

build/color.py
# the ceiling on how saturated this hue gets, at this lightness
def max_chroma(L: float, H: float) -> float:
    """Walk chroma down until the color fits in sRGB."""
    lo, hi = 0.0, 0.5
    for _ in range(48):
        mid = (lo + hi) / 2
        if in_gamut(L, mid, H):
            lo = mid
        else:
            hi = mid
    return lo
magentacontrol flow — keywords, the cursor, anything that moves
cyanthings you call — functions, links, properties
violetthings you name — types and classes
greenliteral text — strings
amberliteral numbers — constants, warnings

Targets

one command each

Run ./install.py for all of them, or name the ones you want. Everything it touches is backed up first, and ./uninstall.py puts it back.

font

Monaspace Krypton, weight 500 in editors, bold in terminals

claude

all 72 theme keys, spinner rainbow and diff blocks included

vscode

local extension, 1.55 line height, 3px phase-blinking cursor

iterm

dynamic profile — iTerm2 picks it up live, no restart

terminal

installs the profile and makes it default

zed

~/.config/zed/themes, Cmd+K Cmd+T to pick

sublime

tmTheme, same colors

bat

syntax highlighting in your pager

Install

Python 3, no dependencies. macOS for the terminal profiles; the editor themes are portable.

git clone https://github.com/espresso20/neonferatu

Or download it. Nothing here phones home.

python3 build/build.py

Regenerates every theme file from palette.json, after the palette passes its checks.

./install.py --variant phosphor

Installs a tube and activates it. They live side by side, so run all three and pick from your editor's theme list.

./preview.sh

Prints the palette and a syntax sample straight into your terminal, before you commit to anything.