Skip to content

Theming & accents (nebelung)

The whole system shares one palette: nebelung, a silver-mist Catppuccin variant. It’s Catppuccin with the blue pulled out of the greys and the accents calmed down — a fog-grey scheme for people who find most dark themes too loud. Grey is the point.

nebelung renders that one palette onto 50+ tools — Ghostty, zellij, starship, bat, delta, lazygit, fzf, yazi, helix, btop, Slack, the Zen browser, and more — so the terminal, editor, git, and browser all speak the same colours. The rice wires up the ones it installs for you; the rest are one roster entry away (see Apps you added yourself).

Three knobs, in the order most people reach for them:

OptionWhat it changes
theme.accentthe one hue that tints highlights
theme.contrasthow far text separates from its background
theme.flavordark or light

They compose, and all three take effect on the next haus rebuild.

The neutral greys stay fixed; only the accent hue changes. Set it once in your host file:

nebelhaus.theme.accent = "sapphire";

Then haus rebuild. The accent re-tints the tools that render per-accent variants — lazygit borders, fzf highlights, yazi, and the Zen browser — while the single-file dotfiles (Ghostty, starship, tmux, bat, zellij) keep their built-in theme.

The default is mauve. Any of the 14 Catppuccin accent names works:

rosewater · flamingo · pink · mauve · red · maroon · peach · yellow · green · teal · sky · sapphire · blue · lavender

See their exact hex values on the palette reference.

The default greys are deliberately soft. If text doesn’t separate enough from its background — a bright room, a glossy screen, or just eyes that prefer more edge:

nebelhaus.theme.contrast = "high";

Same hues, same accents; the neutral ramp is pulled apart in OKLCH so every step gains separation. Body text goes from 11.3:1 to 19.9:1 against the background, which is measured in nebelung’s CI rather than eyeballed — and having now been looked at on a real screen, it reads crisp rather than harsh.

nebelhaus.theme.flavor = "latte";

Light mode is not the dark palette inverted. nebelung is “Catppuccin with the blue stripped out”, and that recipe says nothing about dark — so it’s applied to Catppuccin Latte instead of Mocha to get the same theme in the other polarity: the same warm-grey neutral ramp, the same calmed accents, read the other way up.

Because it’s a real re-render rather than a recolour, tools take their light-mode branches properly — terminal ANSI black/white swap around, the Zen browser switches its prefers-color-scheme block, delta marks itself light = true.

Light mode lands at 7.0:1 for body text on its own, so it’s legible before you reach for contrast. The two axes compose:

contrast = "normal"contrast = "high"
flavor = "mocha"11.3:119.9:1
flavor = "latte"7.0:19.9:1

Obsidian keeps themes and appearance settings inside each vault, so the rice doesn’t guess which folders are yours. List the vaults you want Nebelung to manage as home-relative paths:

nebelhaus.hearth.obsidianVaults = [
"Library/Mobile Documents/iCloud~md~obsidian/Documents/notes"
];

Then haus rebuild. For every listed vault, Hearth:

  • copies the generated full Nebelung theme into .obsidian/themes/Nebelung/;
  • selects Nebelung’s appearance (dark today, regardless of theme.flavor — Obsidian is one of the places light mode does not yet reach); and
  • disables the obsolete nebelung CSS snippet if it was enabled.

The rest of appearance.json is merged and preserved. A path is skipped with a warning unless it already contains .obsidian, so a typo won’t create an empty lookalike vault. List multiple paths to keep several vaults in sync; the default [] touches none.

Everything above covers the tools the rice installs — it owns their configs and themes each one properly. Apps you put in your roster get the same treatment automatically, as long as the name you keyed the entry under matches a nebelung port:

nebelhaus.apps = {
zed = { key = "e"; name = "Zed"; appId = "dev.zed.Zed"; cask = "zed"; };
warp = { key = "w"; name = "Warp"; appId = "dev.warp.Warp-Stable"; cask = "warp"; };
};

On the next haus rebuild, each one’s Nebelung theme is written where that app looks for themes — ~/.config/zed/themes/, ~/.warp/themes/ — in your current flavor, contrast and accent, and re-written whenever you change them. Nothing else to wire.

The key is the roster id, not the display name: zed, not zed-editor. Set nebelhaus.theme.ports.enable = false to turn the whole pass off.

Dropping a theme file is enough for apps that read a fixed path. Plenty of others — Xcode, Warp, OBS, JetBrains, Tabby — have no file interface for choosing a theme; the file is put where they look, and the one-time pick stays yours. A handful more (Slack, Raycast, qBittorrent) can’t be installed by file at all.

Rather than leave you to discover that months later wondering why an app still looks stock, haus doctor tells you exactly where each one stands:

🌫 Nebelung theme
✓ gitui — themed (~/.config/gitui/theme.ron)
ⓘ Xcode — placed at ~/Library/…/Catppuccin Mocha.xccolortheme — pick it in the app's settings
ⓘ Slack — not installed — copy the comma-separated hex string → Slack ▸ Preferences ▸ Themes ▸ paste

A ✓ is live now. A ⓘ tells you the one move left. nebelung publishes which is which per tool — the full table is in its ports documentation.

Colours are generated at build time from a single source, not loaded from loose theme files. That means the palette can never drift out of sync: change nebelung, nix flake update, rebuild, and every tool gets the exact same new values at once. No “I updated the terminal but forgot lazygit” surprises.

nebelung is a standalone colour system — you can theme any tool without the rest of nebelhaus. Two ways:

  • Copy a rendered theme. Each tool’s theme is generated into the flake’s output tree; grab the file for your tool and point its config at it. See the palette reference for the per-tool paths.

  • Consume the flake. Add the input and reference the built output:

    inputs.nebelung.url = "github:nebelhaus/nebelung";
    # then: ${nebelung.packages.${system}.default}/<tool>/...

    The flake also exposes a raw palette attrset (name → "#hex") for configs you generate yourself.

Want a different fog? The palette is computed in OKLCH space from a handful of knobs in nebelung’s scripts/generate-palette.mjs — the grey’s hue and chroma, how much the accents are calmed, which Catppuccin flavor to strip, and how far to push the contrast — then rebuilt with ./build.sh. That’s a nebelung-repo change rather than a host-file one; the palette reference covers the details.