This setup automatically switches themes for WezTerm, Neovim, Git, k9s, and Fish based on your macOS system appearance (light/dark mode).
- Theme Detection:
scripts/theme-mode.shdetects the current macOS appearance - WezTerm: Reads the system appearance natively and re-applies the scheme from its status-bar callback — no script involved
- Neovim: Switches between
github_dark_dimmedandgithub_lighton focus/startup- Lualine status bar: Automatically updates to match the theme
- Git: Repoints a
config-themesymlink at the dark or light delta config - k9s: Swaps the active skin
- Fish: Colors are universal variables, so already-running shells recolor
.dotfiles/
├── scripts/
│ ├── theme-mode.sh # Detects macOS appearance (dark/light)
│ ├── git-theme.sh # Repoints config/git/config-theme
│ ├── k9s-theme.sh # Swaps the k9s skin
│ ├── fish-theme.sh # Recolors running fish shells
│ ├── update-themes.sh # Master script to update all themes
│ └── theme-watcher.sh # Background process to watch for changes
├── config/
│ ├── git/
│ │ ├── config-dark # Dark delta/diff colors
│ │ └── config-light # Light delta/diff colors
│ ├── k9s/skins/ # k9s skins (the config is k9s's own, untracked)
│ ├── wezterm/theme.lua # Native appearance detection
│ ├── fish/functions/
│ │ └── update-themes.fish # Fish function for manual updates
│ └── launchd/
│ └── com.user.theme-watcher.plist # Launch agent
└── config/nvim/lua/scrogson/plugins/
├── colorscheme.lua # Updated with auto-detection
└── lualine.lua # Updated to sync with colorscheme
~/.dotfiles/scripts/update-themes.shjust theme-watcherThis symlinks the launch agent into ~/Library/LaunchAgents/ and bootstraps it.
The recipe is re-runnable — it boots out any existing copy first. install.sh
runs it for you on a new machine.
Neovim will automatically detect the theme on:
- Startup
- When gaining focus (FocusGained event)
No additional setup needed - it works automatically!
You can manually trigger a theme update anytime:
# Using the Fish function
update-themes
# Or directly
~/.dotfiles/scripts/update-themes.sh- Auto-detection: Native, via
wezterm.gui.get_appearance() - Configuration:
config/wezterm/theme.lua - Update:
config.color_schemeand the tab bar colors are only read when the config loads, and WezTerm does not reliably reload when macOS switches appearance on a schedule — which left the terminal background on the old scheme while the status bar, which redraws every second, tracked the switch. Touching the config file from outside did not trigger a reload either, so theupdate-right-statushandler re-applies both viaset_config_overrides()once the appearance no longer matches. Takes effect within a second.
- Auto-detection: On startup and when window gains focus
- Themes:
github_dark_dimmed(dark) /github_light(light) - Status bar: Lualine automatically updates when colorscheme changes
- Configuration:
config/nvim/lua/scrogson/plugins/colorscheme.luaconfig/nvim/lua/scrogson/plugins/lualine.lua
- Themes:
config/git/config-dark/config/git/config-light - Mechanism:
config/git/config-themeis a symlink repointed at the active one. It is generated and gitignored — machine state, not config — so it no longer shows up dirty after every switch.config/git/configincludes it by path, and git ignores the include until the symlink exists.just linkcreates it on a new machine. - Update: Applies to the next git command
- Themes:
github-dark-dimmed/github-lightskins - Skins:
config/k9s/skins/, symlinked into the k9s config dir - Config location:
$K9S_CONFIG_DIR, else the platform config dir — on macOS~/Library/Application Support/k9s, not~/.config/k9s.k9s-theme.shwrites theskin:key into whichever config k9s actually loads. The config itself is not tracked: k9s rewrites it on exit, and the copy the repo used to carry was symlinked to a path k9s never reads. - Update: Requires restarting k9s to apply
- Themes:
config/fish/github_dark_dimmed.fish/github_light.fish - Mechanism: the palettes set universal variables (
set -U), which fish syncs across every running shell.scripts/fish-theme.shjust sources the right one from a throwaway fish process and the change lands everywhere.config.fishonly picks a palette when$__theme_modeis unset, i.e. on a shell that has never had one applied — otherwise it would fight the watcher. - Update: Running shells recolor without a restart; the current line repaints on the next keystroke or prompt.
~/.dotfiles/scripts/theme-mode.shlaunchctl list | grep theme-watchertail -f /tmp/theme-watcher.log
tail -f /tmp/theme-watcher.errNote that launchd runs the watcher with a bare PATH that omits Homebrew, so a
script reaching for a tool installed there fails with command not found in
theme-watcher.err while working fine by hand. The launch agent sets a PATH
covering /opt/homebrew/bin; anything beyond that needs an absolute path.
just theme-watcherlaunchctl bootout gui/$(id -u)/com.user.theme-watcher
rm ~/Library/LaunchAgents/com.user.theme-watcher.plist- WezTerm: Edit the scheme names in
config/wezterm/theme.lua - Neovim: Modify
config/nvim/lua/scrogson/plugins/colorscheme.lua - Git: Edit
config/git/config-dark/config-light - k9s: Edit skin names in
scripts/k9s-theme.sh
Edit scripts/theme-watcher.sh and change the sleep 5 value (in seconds).
- Change your macOS appearance:
- System Settings > Appearance > Light/Dark/Auto
- WezTerm updates within a second, independently of the watcher
- For Neovim: Switch to an nvim window - it should update immediately
- For Git: Run any
git diff- colors follow the new mode - For k9s: Restart the session
- For Fish: Already-open shells recolor on their own
Enjoy seamless theme switching! 🎨