It is a bit fiddly indeed. The problem is that home-manager specialisations don’t account for such use case. They expect you to “rebuild and switch” into your specialisations, but don’t provide activation links to them on every home-manager rebuild, so that you could instantly toggle another specialisation that would include all your recent changes built when in different specialisations as well. That means, that it requires to intervene in between home-manager building and activation and set up activation links there. Neither home-manager or nh support this natively yet.
I have home-manager in standalone installation mode though. For home-manager as nixos module, I didn’t test this since I was using only nixos specialisations when running “home-manager as module” setup before. There, home-manager configuration can be included in nixos modules and there is therefore not much need to use home-manager specialisations at all.
My base config includes all theming for the dark mode. Then I created a dummy specialisation for it. I am using nh and as instructed on nh git page, the marker file here in ~/.local/share/home-manager/specialisation is for nh to handle specialisations better, which is also leveraged in the rebuild script below:
dark
specialisation.dark.configuration = {
xdg.dataFile."home-manager/specialisation".text = "dark";
};
Then I created the light mode specialisation, which overrides settings related to theming inherited from the base config to light values instead:
light
specialisation.light.configuration = {
xdg.dataFile."home-manager/specialisation".text = "light";
stylix.base16Scheme = lib.mkForce light-pallete;
stylix.polarity = lib.mkForce "light";
gtk = {
gtk3.extraConfig.gtk-application-prefer-dark-theme = lib.mkForce false;
gtk4.extraConfig.gtk-application-prefer-dark-theme = lib.mkForce false;
};
dconf.settings = lib.mkForce { "org/gnome/desktop/interface" = { color-scheme = "prefer-light"; }; };
...
...
...
};
I replaced nh home switch command with the following script. That, is I always use this script to rebuild my home-manager config. It symlinks fresh activation links to dark and light specialisations at $HOME on every rebuild independently from whether I’m building from within the base, light or dark specialisation, so that they are always available to switch into them.
rebuild script
#!/usr/bin/env bash
set -euo pipefail
spec_marker="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager/specialisation"
explicit="${3:-}" # optional: mmm nix hm dark
result="$(mktemp -u /tmp/hm-result-XXXXXX)"
# always builds config.home.activationPackage (the base) — never activates
nh home build --out-link "$result" || { rm -f "$result"; exit 1; }
# Resolve to the real store path so the persistent symlink survives
# deletion of the temp out-link below.
real_result="$(readlink -f "$result")"
if [[ -e "$real_result/specialisation" ]]; then
[[ -L "$HOME/.specialisation" ]] && unlink "$HOME/.specialisation"
ln -s "$real_result/specialisation" "$HOME/.specialisation"
fi
# pick target: explicit arg > current marker > base
spec="${explicit:-$(cat "$spec_marker" 2>/dev/null || true)}"
if [[ -n "$spec" ]]; then
if [[ -e "$real_result/specialisation/$spec" ]]; then
"$real_result/specialisation/$spec/activate"
echo "$spec" > "$HOME/.cache/theme-marker" # update marker file for toggle script as well
else
echo "error: '$spec' is not a valid specialisation" >&2
rm -f "$result"
exit 1
fi
else
"$real_result/activate"
fi
rm -f "$result"
notify-send 'Home rebuild finished'
Then I toggle between specialisations like this. Marker file for nh cannot be used here, as it is being written by home-manager itself and only exists within the specialisations, but not inside the base. Therefore, from home-manager independent new marker file needed to be created here for toggling:
toggle script
!/usr/bin/env bash
set -euo pipefail
MARKER="$HOME/.cache/theme-marker"
current=$(cat "$MARKER" 2>/dev/null)
if [ "$current" == "light" ]; then
target="dark"
brightnessctl set 10%
echo "Dark Mode"
notify-send "Dark Mode"
else
target="light"
brightnessctl set 75%
echo "Light Mode"
notify-send "Light Mode"
fi
# activate specialialisation and change marker file
"$HOME/.specialisation/$target/activate" && echo "$target" > "$MARKER"