Scripting a theme change in GNOME systems
Intro
There are several variation of the same thing here now, but I'm adding my approach, because I think it's a bit more complete, and is the only one that is working for me on a fresh (or it was yesterday) installation of Ubuntu.
My setup
System
lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.1 LTS
Release: 24.04
Codename: noble
Script
To each their own, so the following is definitely not the only way to do this, but my approach is as follows:
- (Create if necessary and) edit the
~/.aliases
by adding the line alias theme='~/scripts/change-theme.sh'
touch ~/scripts/change-theme.sh
and code
/nano
/vim
into it to edit.
- Copy-paste the script from bellow.
Usage
theme dark
and theme light
commands will set the theme as expected
theme whatever
will gracefully fallback to theme
theme
will read the current theme, and changed to its opposite
The script changes gtk-theme
, icon-theme
, cursor-theme
and color-scheme
.
change-theme.sh
The normal way
Usually your gsettings
(from GLib
) would work as expected, and the following script will work. This is the preferred way, because gsettings
is schema-aware, and you cannot break anything with its commands. gsettings
also take care of the correct settings propagation, but I am unsure, if these theme settings need to be propagated anywhere else, but where they're already being set.
#!/bin/bash
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
if [ -n "$XDG_RUNTIME_DIR" ]; then
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
else
export DBUS_SESSION_BUS_ADDRESS=$(dbus-launch | grep DBUS_SESSION_BUS_ADDRESS | cut -d= -f2-)
fi
fi
current_theme=$(gsettings get org.gnome.desktop.interface gtk-theme)
echo "[.] Currently using ${current_theme}"
if [[ "$1" == "dark" ]]; then
new_theme="Yaru-dark"
elif [[ "$1" == "light" ]]; then
new_theme="Yaru-light"
elif [[ "${current_theme}" == "'Yaru-light'" ]]; then
new_theme="Yaru-dark"
else
new_theme="Yaru-light"
fi
if [[ "${new_theme}" == "Yaru-light" ]]; then
new_color_scheme="prefer-light"
else
new_color_scheme="prefer-dark"
fi
gsettings set org.gnome.desktop.interface gtk-theme "'${new_theme}'"
gsettings set org.gnome.desktop.interface icon-theme "'${new_theme}'"
gsettings set org.gnome.desktop.interface cursor-theme "'${new_theme}'"
gsettings set org.gnome.desktop.interface color-scheme "'${new_color_scheme}'"
echo "[✓] Theme changed to '${new_theme}'"
The "Nix way"
In my case I have gsettings
installed via Nix package manager. I love it, but every now and then, I find a package I think it'd be best to install via apt install
. I think gsettings
is one of them. That said, here's how to avoid using gsettings
for theme changing alltogether. dconf
is not schema aware, so putting a wrong value anywhere can break your GNOME. There is also no propagation, but as said, I don't know if there needs to be any for changing a theme.
#!/bin/bash
current_theme=$(dconf read /org/gnome/desktop/interface/gtk-theme)
echo "[.] Currently using ${current_theme}"
if [[ "$1" == "dark" ]]; then
new_theme="Yaru-dark"
elif [[ "$1" == "light" ]]; then
new_theme="Yaru-light"
elif [[ "${current_theme}" == "'Yaru-light'" ]]; then
new_theme="Yaru-dark"
else
new_theme="Yaru-light"
fi
if [[ "${new_theme}" == "Yaru-light" ]]; then
new_color_scheme="prefer-light"
else
new_color_scheme="prefer-dark"
fi
dconf write /org/gnome/desktop/interface/gtk-theme "'${new_theme}'"
dconf write /org/gnome/desktop/interface/icon-theme "'${new_theme}'"
dconf write /org/gnome/desktop/interface/cursor-theme "'${new_theme}'"
dconf write /org/gnome/desktop/interface/color-scheme "'${new_color_scheme}'"
echo "[✓] Theme changed to '${new_theme}'"
Scheduled theme change
Adding to CRON jobs remains the same as in other answers, so edit the crontab with crontab -e
using nano (make changes, Ctrl+S
, Ctrl+X
) or vim (i
, make changes, Esc
, type :wq
, Enter
), with the changes being:
0 8 * * * ~/scripts/change-theme.sh light
0 20 * * * ~/scripts/change-theme.sh dark
or whatever your preferred timing is. Confirm changes are saved by listing crontabs with crontab -l
.
Troubleshooting
Your CRON job might not work in case some usual environment variables are missing. If necessary find out your $DISPLAY
(usually :0
or :1
), and add it and $DBUS_SESSION_BUS_ADDRESS
before the script:
0 8 * * * DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus ~/scripts/change-theme.sh light
0 20 * * * DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus ~/scripts/change-theme.sh dark
EDIT
After I migrated to Nix the first solution stopped working for me, so I added a new one.