toggle_theme script

This literate configuration for setting and toggling theme in swaywm is maintained in org mode . org-babel tangles this to ~/.config/sway/scripts/toggle_theme↗ , available as part of dotfiles .

Initialization

Let any reference to an unset variable become a fatal error.

#!/bin/sh
set -u

Variables

Light and dark theme names and config paths for Sway, Foot etc are defined here.

Initially i used Adwaita as light theme, but later moved to Nordic-Polar. I’m using Nordic-Darker and Nordic-Polar for dark and light theme. The Icon theme is Nordzy where dark variant is used for dark mode. Since these themes and icons were earlier shared between Arch and Alpine Linux , i maintained these in a common folder and created soft links from each OS.

DARK_THEME="Nordic-darker"
LIGHT_THEME="Nordic-Polar"

DARK_ICONS="Nordzy-dark"
LIGHT_ICONS="Nordzy"

SWAY_CONFIG_DIR="$HOME/.config/sway"
SWAY_THEMES_DIR="$SWAY_CONFIG_DIR/themes"
SWAY_DARK_CONFIG="$SWAY_THEMES_DIR/nord.conf"
SWAY_LIGHT_CONFIG="$SWAY_THEMES_DIR/nord_light.conf"
GTK3_CONFIG="$HOME/.config/gtk-3.0/settings.ini"
FOOT_THEMES_DIR="$HOME/.config/foot/themes"
FOOT_DARK_CONFIG="$FOOT_THEMES_DIR/dark.ini"
FOOT_LIGHT_CONFIG="$FOOT_THEMES_DIR/light.ini"
FOOT_CURRENT_LINK="$HOME/.config/foot/current_theme.ini"

Helper Functions

Setting theme for different applications or components of a desktop is handled by their respective helper functions. These helper functions are invoked by the theme setting functions.

Papirus icons

I tested Papirus icons with Papirus-Nord colors on 2026-07-04, it required a number of changes in addition to a helper function. Papirus-Dark and Papirus-Light icons were used for dark and light mode respectively, with folder colors set via papirus folders script. The folder color variables needs to be added if Papirus icons are used.

PAPIRUS_DARK_FOLDER_COLOR="frostblue1"
PAPIRUS_LIGHT_FOLDER_COLOR="polarnight1"

The install scripts provided by the Papirus-Nord expects elevated privileges. I used the following script to copy over Papirus-Nord to Papirus, Papirus-Dark and Papirus-Light folders.

#!/bin/sh
PAPIRUS_DIR="/data/docs/Computer/linux/icons/papirus-icon-theme-20250501"
for theme in Papirus Papirus-Dark Papirus-Light; do
    for size in 22x22 24x24 32x32 48x48 64x64; do
        cp -f "Icons/$size/"* "$PAPIRUS_DIR/$theme/$size/places/"
    done
done

The below command will show Papirus-Nord colors, if the above step worked correctly.

papirus-folders -l --theme Papirus-Dark

To use Papirus-Nord colors, the papirus-folders script used by the papirus icon theme can still expect elevated privileges even if the icons are installed in folders with write privileges as it tries to remember the last-used color in a config file at /var folder. Since this tracking is done by this script, we use the papirus-folders script with –once flag to avoid permission errors.

apply_folder_color() {
    local icons=$1
    local color=$2

    if command -v papirus-folders >/dev/null 2>&1; then
        papirus-folders --once -C "$color" --theme "$icons"
    else
        logger "papirus-folders not found; skipping folder recolor"
    fi
}

The above helper function needs to be called in set_dark_theme and set_light_theme functions respectively.

apply_folder_color "$DARK_ICONS" "$PAPIRUS_DARK_FOLDER_COLOR"
apply_folder_color "$LIGHT_ICONS" "$PAPIRUS_LIGHT_FOLDER_COLOR"

Foot terminal

The theme toggle script requires few changes in the foot configuration file . It needs an include section and necessary theme files for the toggle script to work. We reset the symbolic file link for current theme using the force option to overwrites existing link. These settings are effective only for new terminals as the config file is read once at process start.

For changing the theme of existing foot terminals, signals 1 and 2 are sent as per documentation. For the signals to work, need to include [colors-dark] and [colors-light] sections.

apply_foot_theme() {
    local theme_file="$1"
    if [ -f "$theme_file" ]; then
        ln -sf "$theme_file" "$FOOT_CURRENT_LINK"
    fi
}

Gtk applications

The gsetting based command to update D-Bus works only for modern gtk apps. For legacy apps, we update the physical config file.

apply_gtk_theme() {
    local theme=$1
    local color_scheme=$2
    local icons=$3

    gsettings set org.gnome.desktop.interface gtk-theme "$theme"
    gsettings set org.gnome.desktop.interface color-scheme "$color_scheme"
    gsettings set org.gnome.desktop.interface icon-theme "$icons"

    if [ -f "$GTK3_CONFIG" ]; then
        sed -i "s/^gtk-theme-name=.*/gtk-theme-name=$theme/" "$GTK3_CONFIG"
        sed -i "s/^gtk-icon-theme-name=.*/gtk-icon-theme-name=$icons/" "$GTK3_CONFIG"
    fi
}

Sway

Similar to foot, we reset the symbolic file link for current theme using the force option to overwrite existing link. This is also logged to system log.

apply_sway_theme() {
    local theme_file="$1"
    local target="$SWAY_CONFIG_DIR/current_theme.conf"

    if [ -f "$theme_file" ]; then
        ln -sf "$theme_file" "$target"
        swaymsg reload
        logger "Symlinked $theme_file to $target"
    fi
}

Dark theme

The order of applying theme was choosen based on the response time of each option.


prabu@homepc2 ~> time gsettings set org.gnome.desktop.interface gtk-theme Nordic

________________________________________________________
Executed in   40.58 millis    fish           external
   usr time   17.67 millis    1.53 millis   16.14 millis
   sys time    6.99 millis    0.02 millis    6.97 millis

prabu@homepc2 ~> time gsettings set org.gnome.desktop.interface color-scheme prefer-dark

________________________________________________________
Executed in   37.77 millis    fish           external
   usr time   16.97 millis    0.71 millis   16.25 millis
   sys time    9.69 millis    1.03 millis    8.65 millis

prabu@homepc2 ~> time swaymsg reload

________________________________________________________
Executed in  356.76 millis    fish           external
   usr time    0.69 millis    0.69 millis    0.00 millis
   sys time    2.91 millis    1.03 millis    1.88 millis

Since swaymsg reload takes the longest time, it is applied at the end.

set_dark_theme() {
    echo "Switching to dark theme ($DARK_THEME)..."
    apply_foot_theme "$FOOT_DARK_CONFIG"
    pkill -SIGUSR1 foot
    apply_gtk_theme "$DARK_THEME" "prefer-dark" "$DARK_ICONS"
    apply_sway_theme "$SWAY_DARK_CONFIG"
}

Light theme

set_light_theme() {
    echo "Switching to light theme ($LIGHT_THEME)..."
    apply_foot_theme "$FOOT_LIGHT_CONFIG"
    pkill -SIGUSR2 foot
    apply_gtk_theme "$LIGHT_THEME" "prefer-light" "$LIGHT_ICONS"
    apply_sway_theme "$SWAY_LIGHT_CONFIG"
}

Default theme

Always initialize with dark theme. This option is invoked in sway config file. The option ${1:-} means “use $1 if it’s set, otherwise substitute an empty string”.

if [ "${1:-}" = "init" ]; then
    logger "Sway Init: Forcing dark theme"
    set_dark_theme
    exit 0
fi

Toggle logic

The toggle logic fetches the current theme from gsettings to toggle the theme.

CURRENT_GTK_THEME=$(gsettings get org.gnome.desktop.interface gtk-theme)
CURRENT_GTK_THEME=$(echo "$CURRENT_GTK_THEME" | tr -d "'")

if [ "$CURRENT_GTK_THEME" = "$DARK_THEME" ]; then
    set_light_theme
else
    set_dark_theme
fi

© Prabu Anand K 2020-2026