Skip to content

Theme switcher: noir / claude-dark / claude-light across kitty, niri, herdr, nvim, fish, GTK; Imagine wallpaper accents - #417

Open
mecattaf wants to merge 4 commits into
mainfrom
theme-switcher
Open

mecattaf wants to merge 4 commits into
mainfrom
theme-switcher

Conversation

@mecattaf

@mecattaf mecattaf commented Sep 17, 2026

Copy link
Copy Markdown
Owner

What this is

Four commits, one story: the claude.ai/imagine wallpaper harvested in ~/colors, then a dotfiles-wide, nix-native switcher between three hand-curated themes — noir (the Claude Dark palette on OLED black, as today), claude-dark and claude-light (Claude's own light and dark modes, recovered from the claude.ai bundle) — with GTK following through recoloured MacTahoe builds, and a second, independent knob for the colour accent: one of the seven Imagine grounds as wallpaper and matching folder colour.

Design and mechanism: docs/theme-switcher-2026-09-17.md. Research reports (not in the repo): ~/colors/theme-switcher-design.md, ~/colors/quickshell-live-theming.md.

Commits

  1. coordinator: the claude.ai/imagine olive wallpaper, frosted kitty at 0.96 — the exact recovered SVG rasterised under Anthropic's own CSS recipe at 5120x2880; swaybg back at startup; the kitty blur rule back (now matching every kitty-derived app-id), background_opacity 0.96.

  2. theme switcher — palettes as Nix attrsets in home/themes/*.nix, keyed by role; home/theme.nix renders every consumer's colour fragment for every theme under ~/.config/themes/<name>/; every RAW file keeps its body raw and includes ~/.config/theme/<fragment>; ~/.local/bin/theme retargets that one symlink and fires the reloads (kitty load-config per socket, niri msg action load-config-file, nvim --remote-expr). herdr is set to its terminal theme and follows kitty via DEC 2031 with no config write-back. fish re-sources at the next prompt. starship's one hex becomes red. Two stale tracked files deleted.

  3. GTK, accents, folder colours, F2pkgs/mactahoe-gtk-theme.nix gains a claude variant (both SCSS branches recoloured to Claude's tokens, clay accent); GTK_THEME removed and gsettings-desktop-schemas + glib added system-wide so GTK3 follows gsettings live (see below); ~/.config/gtk-4.0 points through the theme pointer; seven wallpaper accents rendered; pkgs/mactahoe-icon-theme.nix prebuilds folder-colour variants for all seven; wallpaper <accent> remembers the accent and re-picks the icons; F2 = theme picker, Shift+F2 = accent picker.

  4. remove cliamp and zathura (unused) — packages, configs, the cliamp fish wrapper and its aliases, its activation script and gitignore block, and bin/media's cliamp preference. Navidrome, navidrome-scan and the navidrome-credentials secret stay (no rekey); only comments that named cliamp as the secret's reader change.

Why GTK works now and did not before

GTK_THEME in modules/common.nix was the only thing theming GTK apps — not because niri lacks a settings daemon, but because gsettings-desktop-schemas was never on XDG_DATA_DIRS, so GTK3's Wayland backend (which reads org.gnome.desktop.interface straight from dconf and follows changes live) never found the schema and fell back to settings.ini. Verified with gtk-query-settings: no schema → settings.ini value; schema present → the dconf value; change it → it follows. gsettings itself was on no session PATH, so startup.kdl's four gsettings set lines had been failing silently for months. An env var cannot change under a running session, so GTK_THEME had to go for live switching.

What is live, what is not

live on theme <name> next start
kitty (all instances), niri, herdr (chrome + every pane), fish, GTK3 apps, folder icons, Chrome light/dark, nvim (running instances) GTK4/libadwaita CSS (their light/dark flips live), Claude Code / Codex TUIs

Verified

  • nix build of the coordinator Home Manager generation; coordinator and client system toplevels evaluate.
  • Both MacTahoe GTK packages build; the claude variant's gtk-3.0/gtk-4.0 CSS carries #F9F9F7/#151515/#20201F/#D97757 as designed. The icon theme builds 24 dirs (163 MB after jdupes); MacTahoe-olive folders carry #697751.
  • niri validate on the full config.
  • After the removal commit: no cliamp or zathura in the generation's home-files or home-path; coordinator, client and nas toplevels evaluate. The noir kitty fragment reproduces the previous kitty.conf colours key for key.
  • Live on the coordinator through a preview render: noir → claude-dark → noir flipped 5 kitty instances, niri and the herdr projector; wallpaper olive, theme apply, both picker lists.
  • Not testable before the switch: the GTK path end to end (needs the schemas on the system path) and the F2 windows themselves.

After merging, on the coordinator

sudo nixos-rebuild switch --flake .#coordinator   # renders ~/.config/themes, schemas, packages
theme noir      # or just log in again: the activation heals the pointer, startup runs `theme apply`

The Zenbook gets everything on its next pull + switch; a pull ahead of the switch degrades gracefully (niri's include is optional, nvim falls back to noir, kitty/fish to defaults).

Deliberately not in this PR

  • Fonts (Anthropic Sans/Serif/Mono from ~/colors): another session owns that spec; they are proprietary brand faces and must follow the NAS requireFile pattern of pkgs/sf-pro.nix, never land in git.
  • Claude Code's custom:<name> theme file (mechanism confirmed in the 2.1.266 binary, file format unverified) — would remove the ~/.claude.json write and hot-reload running agents. Follow-up.
  • qt6ct theming.
  • The claude-light ANSI-16 mapping is a design proposal (every slot WCAG AA on white) flagged for colorlab tuning; the light terminal sits on bg-100 #F9F9F7, one step warmer than the strict "terminal = code block" rule used for dark.

🤖 Generated with Claude Code

mecattaf and others added 4 commits September 17, 2026 06:23
…0.96

Tom 2026-09-16: "let's make it the green pistachio recreated 5k monitor my
wallpaper", then "make the blur at 0.96 from the niri side for kitty
terminal, to appreciate the new colors. this is not transparency".

- dot_local/share/wallpapers/imagine-olive-5120x2880.png: the claude.ai/imagine
  "olive" theme (ground #7c8b62) — the exact recovered source SVG
  (~/colors/waves/capture/imagine-background.svg) rasterised by Chrome under
  Anthropic's own CSS recipe (cover/center) at the PA27JCV's native
  5120x2880. Not July's design-pass export, whose reconstruction has the
  crossing-ribbons bug (~/colors/waves/STATE.md).
- bin/wallpaper defaults to it; startup.kdl spawns `wallpaper` again (the
  swaybg spawn was deleted 2026-09-11 for the Zenbook's OLED black).
- window-rules.kdl: the kitty blur rule is back, now matching every
  kitty-derived app-id — kitty, herdr-projector, and the fzf *-prompt popups —
  so no kitty is see-through without the frost. kitty.conf: background_opacity
  0.96, dynamic_background_opacity yes. Same mechanism PR #381 removed: kitty
  makes only its background translucent, niri blurs the wallpaper behind it,
  text stays opaque.

Shared RAW files: the client laptop gets the wallpaper and the frost too after
its next pull.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…erdr, nvim, fish

Tom 2026-09-16: "resurface the light mode and the dark mode, original from
claude. currently i have 'noir' mode. i wonder what it would take to make a
dotfiles-wide switcher … i'm talking kitty, herdr(?), nvim. gtk-noir is fine
to keep it that way for now" — three hand-curated themes, switched in a
nix-native way. Design and option analysis: docs/theme-switcher-2026-09-17.md
(full research report in ~/colors/theme-switcher-design.md).

The themes. noir is the Claude Dark 12-role syntax palette on OLED black;
claude-dark is the same palette on claude.ai's own dark grounds (#1A1A1A
code-block, #20201F page) — home/themes/claude-dark.nix is `noir // {
ground; … }`; claude-light is the Claude Light palette on --bg-100 #F9F9F7,
with a designed 16-slot ANSI mapping (Claude ships none), every slot WCAG AA
on white, flagged for colorlab tuning. Sources: the palettes recovered from
the claude.ai bundle in ~/colors/waves/capture/claude-code-theme/.

The mechanism. home/themes/*.nix are palettes keyed by ROLE; default.nix
renders each app's colour FRAGMENT; home/theme.nix writes every fragment for
every theme under ~/.config/themes/<name>/ and bootstraps/heals the pointer
~/.config/theme, a plain symlink into themes/ that ~/.local/bin/theme
retargets at runtime (HM owns the plural, the switcher the singular). Every
RAW file keeps its body raw and joins its fragment through the app's own
include — the kitty-scrollback-nix.conf / niri-local.kdl move, one level up.
Switching needs no rebuild; only adding a theme or changing a palette does.

- kitty.conf: the colour block becomes `include ${HOME}/.config/theme/kitty.conf`;
  opacity stays raw (dynamic_ cannot change on reload). `theme` runs
  `kitten @ load-config` per instance socket — a symlink retarget fires no
  watcher event, so the explicit reloads are load-bearing.
- niri: config.kdl includes ~/.config/theme/niri.kdl LAST (sections merge,
  later wins per property; `optional=true` so a checkout pulled ahead of its
  switch cannot take niri down). layout.kdl / misc.kdl lose their colours.
  Two stale comments corrected: 26.04 knows both `optional=true` and `~`.
  Mod+Shift+T cycles.
- herdr: `[theme] name = "terminal"` — every token an ANSI slot, so kitty
  paints herdr, and re-paints it live: herdr enables DEC 2031, kitty reports
  the background change as CSI ?997;n on reload, herdr re-queries OSC 10/11/4
  (source-verified in the pinned 0.9.0, then seen live: the projector followed
  noir -> claude-dark -> noir with no herdr write-back). There was no [theme]
  section before, i.e. herdr was on default catppuccin, not noir.
- nvim: RAW lua/theme.lua dofile()s the fragment (noir fallback inline) and
  owns the catppuccin setup, bufferline's ground highlights and lualine's two
  colours; reload() purges catppuccin, re-setups, :CatppuccinCompile, and is
  called over --remote-expr on every running instance. plugins.lua.in's
  catppuccin block moves there. Deleted: lua/plugins.lua (a stale tracked
  duplicate of the rendered file) and lua/plugins/bufferline.lua (linked,
  never required — plugins.lua.in set bufferline up inline).
- fish: conf.d/colors.fish sources the fragment and re-sources it at the next
  prompt when the pointer moved. No universal variables — fish_variables is
  tracked here and `set -U` would dirty the checkout on every switch.
- starship: the one `#F47B85` becomes `red`; everything else was ANSI-named
  and `#DA7756` (accent-brand) is identical in Claude's light and dark tokens.
- Claude Code: `theme` writes the light/dark key into ~/.claude.json
  (tmp+rename; next start).
- Not in this PR, by Tom's ruling: GTK/MacTahoe stays Dark (Phase 4 in the
  doc; the package already builds the Light variants). cliamp 1.63.2 lists only
  built-in themes, so its catppuccin-noir.toml was inert already; zathura and
  qt6ct untouched.

Verified: `nix build` of the coordinator Home Manager generation and eval of the
client's; `niri validate`; the noir fragment reproduces the previous kitty.conf
colours key for key (only background_opacity is outside it); live on the
coordinator through a preview render (`THEMES_DIR=~/.cache/theme-preview theme
claude-dark`): 5 kitty instances reloaded, niri reloaded, herdr repainted,
back to noir. After the switch the activation entry re-targets that preview
pointer to ~/.config/themes/noir.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rs, F2 pickers

Tom 2026-09-17: "the hardest thing to make sure that you got right is actually
the gtk theme … for my 'noir' theme i am doing nix replacements on an existing
package. the same can be done for the other gtk themes to follow the
'classic'/vanilla claude light and claude dark themes. i want those as well";
"should be able to switch the 'accent' as well … matching folder colors …
matching exactly the wallpaper color selection"; "F2 should be ONLY for the
dark/light/noir variants … Shift + F2 the switch for the color accent
(wallpaper + icons)".

GTK. pkgs/mactahoe-gtk-theme.nix takes a `variant`: `oled` is the existing
noir build, unchanged output; `claude` recolours BOTH branches of
src/sass/_colors.scss to claude.ai's tokens (bg-000/100/200, text-000/200/400,
cds-text-secondary, links = accent-100) with the `orange` accent slot set to
Anthropic clay #D97757 — MacTahoe-Claude-{Dark,Light}[-solid]-orange[-(x)hdpi].
Each palette carries its GTK theme; home/theme.nix puts every theme's package on
the profile, ships each theme's gtk-4.0/ under ~/.config/themes/<name>/, and
points ~/.config/gtk-4.0/{gtk.css,gtk-dark.css,assets} THROUGH ~/.config/theme
at it. `theme apply` sets gtk-theme, wm theme, icon-theme and color-scheme
with gsettings.

Why that works now and did not before. GTK_THEME (modules/common.nix) is
deleted: an env var cannot change under a running session. It had been the
only thing theming GTK because gsettings-desktop-schemas was never on
XDG_DATA_DIRS — no gschemas.compiled with org.gnome.desktop.interface anywhere
in the session — so GTK3's Wayland backend fell back to settings.ini. With the
schema re-homed to share/glib-2.0/schemas (systemPackages) GTK3 follows the
gsettings value LIVE; verified with gtk-query-settings: unset → Dark (settings.ini),
schema present → the dconf value, changed → follows. `gsettings` itself was on
no session PATH, so startup.kdl's four gsettings lines had been failing
silently; glib is in systemPackages and the four lines become
`spawn-at-startup "theme" "apply"`. dconf.settings no longer pins gtk-theme /
color-scheme (a pin would snap a light session back to Dark on every switch).

Accents. The seven claude.ai/imagine grounds (oat olive cactus sky fig heather
coral), rendered at 5120x2880 from the recovered SVG like the olive one.
bin/wallpaper takes an accent name, remembers it in ~/.local/state/wallpaper/
accent (restored at login), and re-picks the folder colour.
pkgs/mactahoe-icon-theme.nix prebuilds MacTahoe-<accent>{,-light,-dark} for all
seven — the folder SVGs of the grey set with #686868 swapped for the accent's
darker ground (--bg-primary-dark) — so every combination is in the store
before it is picked; 163 MB for 24 dirs after jdupes (was 112 MB for 3).
`theme icons` = MacTahoe[-<accent>]-{dark,light}: accent from the wallpaper,
polarity from the theme, stock blue folders when the variant is missing.

Pickers. F2 → bin/theme-prompt theme (noir / claude-dark / claude-light),
Shift+F2 → theme-prompt accent, both the F1/F9/F10 floating-fzf shape with a
theme-prompt window rule. `theme` also gained a flock and a SIGUSR1 fallback
for kitties without our listen_on (from the Omarchy / DankMaterialShell
research, ~/colors/quickshell-live-theming.md).

Live: kitty, niri, herdr, fish, GTK3, folder icons, Chrome's colour scheme.
Next start: GTK4/libadwaita CSS (colour scheme flips live), agent TUIs.
Not here: fonts (another session owns that spec; proprietary, NAS requireFile
pattern only), cliamp/zathura/qt6ct, Claude Code's `custom:` theme file (the
mechanism is in the 2.1.266 binary, the file format is unverified).

Verified: coordinator home-manager-generation builds; coordinator and client
system toplevels evaluate; both MacTahoe packages build with the expected
hexes in gtk-3.0/gtk-4.0 CSS and the olive hex in MacTahoe-olive folders;
niri validate; `wallpaper olive` / `theme apply` / both picker lists live.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Tom 2026-09-17: "i'm not using either of those TUI projects at all. so let's
delete them."

zathura: the config dir (home/dot_config/zathura), its configDirs entry and
the package. Nothing else referenced it; PDFs have no mimeApps binding to it.

cliamp: the package (pkgs/cliamp.nix + overlay entry), home/dot_config/cliamp,
its two xdg.configFile links, the one-time cliampRealDir activation (safe to
drop: a generation with no .config/cliamp path at all lets Home Manager's
cleanup remove an old whole-dir link on its own), the cliamp fish wrapper
function and the m/music/mshuffle/mp/mn/mb/mnow aliases, the .gitignore block
for its runtime files, and bin/media's cliamp preference (it now drives
playerctl's default player).

Kept: Navidrome itself, `mscan`/navidrome-scan (its Subsonic client id is now
`navidrome-scan`), and the navidrome-credentials secret, which navidrome-scan
still reads — only the comments in secrets.nix, modules/secrets.nix and
hosts/coordinator/services.nix that named cliamp as its consumer change. No
rekey. The piri music scratchpad stays; its "later a cliamp-in-kitty pane"
notes go. Historical records (DECISIONS.md, the zenbook return doc, a test
comment) are left as written.

Not touched: ~/.config/cliamp on the coordinator is a real directory holding
cliamp's history.toml and resume.json (which carries a Navidrome Subsonic
token, DECISIONS.md 2026-09-13); the switch removes only the two links.

Verified: coordinator home-manager-generation builds with no cliamp or zathura
in home-files or home-path; coordinator, client and nas toplevels evaluate;
niri validate; fish -n on the edited fish files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

behaviour-changing triage bucket 2026-09-20

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant