31

Has anyone found a way to automatically switch between "light" and "dark" modes yet, with regards to the "Window Theme" in Ubuntu's settings (Settings → AppearanceWindow ThemeLight / Standard / Dark)?

Digging into the settings every morning or evening is a real pain. It'd be great to be able to automatically switch to "dark" mode at sunset, and automatically switch to "light" mode at sunrise.

0

6 Answers 6

34

For GNOME, this shell extension exists: Night Theme Switcher

It has quite a lot of options and already works out of the box, without having to configure anything, but the configuration is straight forward as well!

Screenshot of the Schedule tab

Screenshot of the GTK theme tab

5
  • This is a great extension, thanks for it. Commented Mar 26, 2021 at 8:28
  • I installed it but don't understand how to open it.
    – khatchad
    Commented Apr 6, 2021 at 19:59
  • 1
    @RaffiKhatchadourian that's a thing with gnome extensions - it's really not clear how to open the settings if you don't know. there's a few ways. if you installed the extension using the gnome extensions browser add-on, you can open the preferences from there. otherwise, you might want to use gnome-tweaks or using the command line: gnome-extensions prefs [email protected]
    – sk22
    Commented Apr 13, 2021 at 13:15
  • does it work with 22.04? when I click install on the linked page, it opens a dialog that tells me no apps available
    – Line
    Commented Aug 10, 2022 at 18:38
  • 4
    Also the extension page says Ubuntu is not supported due to their heavily patched GNOME components that conflict with the extension Commented Dec 23, 2022 at 5:06
12

The terminal command for changing theme is:

gsettings set org.gnome.desktop.interface gtk-theme Yaru-dark

for the Yaru-dark theme, and

gsettings set org.gnome.desktop.interface gtk-theme Yaru-light

for the Yaru-light theme.

Now, there's something called cron-job for scheduling jobs (basically executing something, repeatedly at specific time). So, you can write a cron-job to execute these commands at specified times (something like change to dark theme at 9 PM and light theme at 6 AM).

Add the following to a file named script.sh:

#!/bin/bash
echo export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS > lightscript.sh
echo export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS > darkscript.sh
echo "gsettings set org.gnome.desktop.interface gtk-theme Yaru-light" >> lightscript.sh
echo "gsettings set org.gnome.desktop.interface gtk-theme Yaru-dark" >> darkscript.sh
chmod 755 lightscript.sh
chmod 755 darkscript.sh

currenttime=$(date +%H:%M)
if [[ "$currenttime" > "21:00" ]] || [[ "$currenttime" < "06:00" ]]; then
  ./darkscript.sh
else
  ./lightscript.sh
fi

Make the file executable running:

chmod 755 /path/to/script.sh

or:

chmod +x /path/to/script.sh

Run gnome-session-properties in terminal. Add a new start up program by clicking add on right side and selecting the script.sh file by browsing and save it with some name and comment. This will tell GNOME to create lightscript.sh and darkscript.sh whenever you login through GUI.

Add your job (change theme) to crontab by using the command:

crontab -e

and choosing a suitable editor or you can go to /var/spool/cron/crontabs and edit the file with your username. Accessing the file this way requires sudo privileges. Add the following two lines (with /path/to/ replaced by actual path):

0 6 * * * /path/to/lightscript.sh
0 21 * * * /path/to/darkscript.sh

It will say:

crontab: installing new crontab

after exiting the command. You can also check with:

crontab -l

This should do it. The above two lines tell cron to execute lightscript.sh at 6:00 AM and darkscript.sh at 9:00 PM everyday.

We are taking this detour instead of just adding:

0 6 * * * gsettings set org.gnome.desktop.interface gtk-theme Yaru-light

to crontab because this requires the DBUS_SESSION_BUS_ADDRESS variable to be set correctly.

14
  • nice answer....
    – PRATAP
    Commented May 3, 2020 at 17:36
  • I followed the instructions... We'll see in about half an hour whether I have done it right and / or how well it works. But why do your instructions refer to two different scripts at the end (lightscript.sh and darkscript.sh), when you are indicate that I should create a single script at the start (called "script.sh")? For the last section (with the reference to the two scripts), I changed the name to "script.sh" for both of them, because I only created a single script at the start... I also change the times (from "21" to "18", for 6PM). Commented May 4, 2020 at 7:29
  • It didn't work... :-( Commented May 4, 2020 at 8:13
  • So, the file "script.sh" is executed when you login (provided you added it in gnome-session-properties). Its work is to create lightscript.sh and darkscript.sh with proper value for DBUS_SESSION_BUS_ADDRESS. Then the crontab executes these lightscript.sh and darkscript.sh. Just restart and login and see if lightscript.sh and darkscript.sh is created where script.sh is present. Also try service cron status to see if cron is running
    – Shubhzgang
    Commented May 4, 2020 at 8:25
  • This wouldn't work with changes you made to the script names @GregoryOpera
    – Shubhzgang
    Commented May 4, 2020 at 8:57
2

We can achieve this with the help of crontab and @shubhzgang's answer.

#!/bin/bash

# Script that auto switches Ubuntu themes to Dark or Light, 
# depending on the time of day

# Copy this script file to /usr/local/bin/my-auto-theme
# Add following lines to crontab -e
# 0 9 * * * bash /usr/bin/local/my-auto-theme light
# 0 17 * * * bash /usr/bin/local/my-auto-theme dark
# @reboot bash /usr/bin/local/my-auto-theme

set_theme() {
    if [[ "$1" == "dark" ]]; then
        new_gtk_theme="Yaru-dark"
    elif [[ "$1" == "light" ]]; then
        new_gtk_theme="Yaru-light"
    else
        echo "[!] Unsupported theme: $1"
        return
    fi
    
    # https://askubuntu.com/a/1234819/895417
    export DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS
    current_gtk_theme=$(gsettings get org.gnome.desktop.interface gtk-theme)
    # echo "[.] Currently using ${current_gtk_theme}"
    if [[ "${current_gtk_theme}" == "'${new_gtk_theme}'" ]]; then
        echo "[i] Already using gtk '${new_gtk_theme}' theme"
    else
        echo "[-] Setting gtk theme to ${new_gtk_theme}"
        gsettings set org.gnome.desktop.interface gtk-theme ${new_gtk_theme}
        echo "[✓] gtk theme changed to ${new_gtk_theme}"
    fi
}

# If script run without argument
if [[ -z "$1" ]]; then
    currenttime=$(date +%H:%M)
    if [[ "$currenttime" > "17:00" || "$currenttime" < "09:00" ]]; then
        set_theme dark
    else
        set_theme light
    fi
else
    set_theme $1
fi

We added @reboot so that script runs even if PC was shut down at cron trigger time.

0

I achieved this using Shubhzgang's answer and a couple of more answers from other sources. Then added my own modifications. Here's the script I use:

#!/bin/bash

# Script that auto switches Ubuntu themes to Dark or Light, 
# depending on the time of day

# Copy this script file to /usr/local/bin/switch-theme.sh
# Add following lines to crontab -e
# 0 10 * * * bash /usr/bin/local/switch-theme.sh light
# 0 19 * * * bash /usr/bin/local/switch-theme.sh dark
# @reboot bash /usr/bin/local/switch-theme.sh

set_theme() {
    # https://askubuntu.com/a/743024/1193214
    # PID may return multiple ids here, so I converted to to array and got just the first id.
    # Otherwise, you may try another suggestion in the link https://askubuntu.com/a/1437023/1193214
    PID=($(pgrep gnome-session))
    export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)
    echo `date` Starting script execution - setting theme $1 >> ~/Scripts/switch-theme.log
    if [[ "$1" == "dark" ]]; then
        new_gtk_theme="Yaru-blue-dark"
        # Some apps also need color scheme
        new_color_scheme="prefer-dark"
        new_icon_theme="Yaru-blue-dark"
    elif [[ "$1" == "light" ]]; then
        new_gtk_theme="Yaru-blue"
        new_color_scheme="prefer-light"
        new_icon_theme="Yaru-blue"the
    else
        echo "[!] Unsupported theme: $1"
        return
    fi
           
    current_gtk_theme=$(gsettings get org.gnome.desktop.interface gtk-theme)
    current_color_scheme=$(gsettings get org.gnome.desktop.interface color-scheme)
    if [[ "${current_gtk_theme}" == "'${new_gtk_theme}'" ]]; then
        echo "`date` [i] Already using gtk '${new_gtk_theme}' theme" >> ~/Scripts/switch-theme.log
    else
        echo "`date` [-] Setting gtk theme to ${new_gtk_theme}" >> ~/Scripts/switch-theme.log
        gsettings set org.gnome.desktop.interface gtk-theme "${new_gtk_theme}"
        gsettings set org.gnome.desktop.interface color-scheme "${new_color_scheme}"
        gsettings set org.gnome.desktop.interface icon-theme "${new_icon_theme}"
        echo "`date` [✓] gtk theme changed to ${new_gtk_theme}" >> ~/Scripts/switch-theme.log
    fi
}

# If script run without argument
if [[ -z "$1" ]]; then
    currenttime=$(date +%H:%M)
    if [[ "$currenttime" > "19:00" || "$currenttime" < "10:00" ]]; then
        set_theme dark
    else
        set_theme light
    fi
else
    set_theme $1
fi

Finally, I added this script at startup as well.

0

I work on a script to do that, and I like the result, it works similar to gnome autodarkmode, with a sunshine and sunrise time, take a look at:

https://github.com/jackfido/nightMode

You need to have internet conection to get the sunrise and sunset data, this ocurrs here:

getSunRiseAndSet.sh

#!/bin/bash

# 1.- Tihs files pretends to get the sunset and sunrise values from: https://www.timeanddate.com/sun/mexico/
# 2.- To save this values into a temporal file, called as the "yourlocation".out
# 3.- Then compares the dates; from the last modified file date to current date in format YYYYmmdd to determine if we have the today values
# 4.- If the file is for yersterday or older then we have to go for the values for today, else, do nothing 
# 
# Additional:
#       run THIS file in startup applications to run when your session start after a shotdown or reboot
#       run crontab -e to run nightMode.sh file, directly, every minute to set theme as soon as the sunset/sunrise occurss -> * * * * * ~/Public/nightMode/nightMode.sh
#       run THIS task each 3,4, 6 or 8 hours, it depends on how many times you want to check if you download the updated file, it allow us to have the values up-to-date in case of pc enters in hibernatin or freeze state during a day change

# file name and location
location="monterrey"
tmpfile=~/Public/nightMode/tmp/$location.out

# get dates from last file and current
lastUpdate=$(date -r $tmpfile +"%Y%m%d")
currentDate=$(date +%Y%m%d)

# if file doesnt exists
if [ ! -z "$lastUpdate" ]; then
    # compare file date with current date to determine if need download the values
    if [ "$lastUpdate" -lt "$currentDate" ]; then
        wget -q "https://www.timeanddate.com/sun/mexico/$location" -O "$tmpfile"

        echo "Monterrey time getted successfully"
        bash ~/Public/nightMode/nightMode.sh
    fi
else
    wget -q "https://www.timeanddate.com/sun/mexico/$location" -O "$tmpfile"

    echo "Monterrey time getted successfully"
    bash ~/Public/nightMode/nightMode.sh
fi

nightMode.sh

    #!/bin/bash
location="monterrey"
tmpfile=~/Public/nightMode/tmp/$location.out

SUNR=$(grep "Sunrise Today" "$tmpfile" | grep -oE '((1[0-2]|0?[1-9]):([0-5][0-9]) ?([AaPp][Mm]))' | head -1)
SUNS=$(grep "Sunset Today" "$tmpfile" | grep -oE '((1[0-2]|0?[1-9]):([0-5][0-9]) ?([AaPp][Mm]))' | tail -1)

# Use $sunrise and $sunset variables to fit your needs. Example:
sunrise=$(date --date="$SUNR" +%Y%m%d%H%M)
sunset=$(date --date="$SUNS" +%Y%m%d%H%M)

hour=$(date +%Y%m%d%H%M)

if [  $hour -gt $sunrise ] && [ $hour -lt $sunset ];
then
    bash ~/Public/nightMode/day.sh
fi

if [ $hour -lt $sunrise ] || [ $hour -gt $sunset ];
then
    bash ~/Public/nightMode/night.sh
fi

day.sh:

#!/bin/bash

PID=$(pgrep compiz)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)

# theme2Stablish='Materia-light-compact'
theme2Stablish='Yaru'
theme=$(gsettings get org.gnome.desktop.interface gtk-theme)

if [ "$theme" != "$theme2Stablish" ];
then
    gsettings set org.gnome.desktop.interface gtk-theme $theme2Stablish
    gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
    gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Light'
#    notify-send "Day theme stablished"
fi

night.sh

#!/bin/bash

PID=$(pgrep compiz)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ|cut -d= -f2-)

# theme2Stablish='Materia-dark-compact'
theme2Stablish='Yaru-unity-dark'
theme=$(gsettings get org.gnome.desktop.interface gtk-theme)

if [ "$theme" != "$theme2Stablish" ];
then
    gsettings set org.gnome.desktop.interface gtk-theme $theme2Stablish
    gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
    gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Dark'
#    notify-send "Night theme stablished"
fi

then add nightMode to crontab:

# Create with crontab -e command:

* * * * * ~/Public/nightMode/nightMode.sh
0 */6 * * * ~/Public/nightMode/getSunRiseAndSet.sh

I started working based in this page

https://linuxconfig.org/how-to-obtain-sunrise-sunset-time-for-any-location-from-linux-command-line

And then, in your programs configure the automatic darkmode, like in github desktop, Android Studio, firefox, etc, etc

this was my result:

Light Mode:

Light Mode

Dark Mode:

Dark Mode

Regards

1
  • 1
    Hello. What about later when the link does not work? Maybe include the info in the body of the answer.
    – David DE
    Commented Mar 16, 2022 at 14:34
0

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:

  1. (Create if necessary and) edit the ~/.aliases by adding the line alias theme='~/scripts/change-theme.sh'
  2. touch ~/scripts/change-theme.sh and code/nano/vim into it to edit.
  3. Copy-paste the script from bellow.

Usage

  1. theme dark and theme light commands will set the theme as expected
  2. theme whatever will gracefully fallback to theme
  3. 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.

3
  • Ubuntu 24 means Ubuntu Core 24 is that the version you are using?
    – David DE
    Commented Aug 30 at 11:12
  • I edited my answer. First, for @DavidDE , I added lsb_release output. Second, see EDIT.
    – s3c
    Commented Aug 31 at 9:13
  • Edited again. Added a way without gsettings
    – s3c
    Commented Aug 31 at 10:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .