skip to content

Search

How to Automate Theme Switching in Dotfiles

2 min read

Automate day/night theme switching across dotfiles using envsubst, cron, and a shell script. Compatible with stow, chezmoi, and Neovim

Following up on my previous post on automatic theme switching in VS Code, I set out to extend day/night theme switching beyond just VS Code to all my dotfiles, including Alacritty and Neovim.

Existing Tools

There are many existing tools for managing dotfiles dynamically. Here are a couple examples:

Using yadm:

# Split the config into two versions:
cp ~/.config/alacritty/alacritty.yml ~/.config/alacritty/alacritty.yml##theme=day
cp ~/.config/alacritty/alacritty.yml ~/.config/alacritty/alacritty.yml##theme=night
 
# Tell yadm to switch between themes:
yadm alt theme=day yadm alt theme=night

Using chezmoi:

Here’s an example of alacritty.toml.tmpl using chezmoi’s Go template syntax:

{{ if eq .theme "day" }}
import = ["~/.local/share/alacritty-theme/themes/atom_one_light.toml"]
{{ else }}
import = ["~/.local/share/alacritty-theme/themes/onedark.toml"]
{{ end }}
chezmoi set --local theme night
chezmoi apply

My Approach with stow + envsubst

However since my dotfiles repo is heavily based on stow, I wanted a solution that integrates well with it. That’s when I came across envsubst from GNU gettext, which immediately stood out for its portability and availability as a standard GNU utility.

I ended up implementing cowboy-bebug/dotfiles/@dde6579 as my solution. Using envsubst is simple and minimal, as expected from a GNU utility.

Implementation

First, I created a template file at ~/.config/alacritty/alacritty.template.yml that uses a shell variable:

import = ["~/.local/share/alacritty-theme/themes/${ALACRITTY_THEME}.toml"]

Then, I created a script, ~/.local/bin/switch-theme, that dynamically substitutes different values based on the time of day:

#!/usr/bin/env bash
 
HOUR=$(date +%H)
 
if [ "$HOUR" -ge 6 ] && [ "$HOUR" -lt 19 ]; then
  export ALACRITTY_THEME="atom_one_light"
else
  export ALACRITTY_THEME="onedark"
fi
 
envsubst < ~/.config/alacritty/alacritty.template.yml > ~/.config/alacritty/alacritty.yml

Don’t forget to make it executable:

chmod +x ~/.local/bin/switch-theme

This script works whether you’re managing your dotfiles with stow, chezmoi, or manually.

Lastly, schedule running the script with cron (or systemd if you prefer):

crontab -e

And add this line:

# Switch theme at 6:01AM and 7:01PM
1 6,19 * * * /home/yourusername/.local/bin/switch-theme

Gotchas

While implementing this setup, I ran into a couple of caveats worth noting.

1. Exporting Variables

envsubst requires variables to be exported within your script. Without export, envsubst won’t recognise the variable, and the substitution will silently fail.

2. Neovim Cache

Neovim caches Lua modules, including theme settings. When using NvChad , I noticed that updating the theme in ~/.config/nvim/lua/chadrc.lua doesn’t always apply changes immediately. This is because Neovim compiles and stores Lua modules in ~/.cache/nvim/luac, which can cause it to load outdated config unless explicitly reloaded.

To ensure your updated theme is applied on startup, you can force a reload by adding this snippet to chadrc.lua:

vim.api.nvim_create_autocmd("User", {
	pattern = "VeryLazy", -- ensures it runs after NvChad lazy loading
	callback = function()
		require("base46").load_all_highlights()
	end,
})