Selection
Docs
Images
theme-switcher.sh
.config/hypr/scripts/theme-switcher.sh
#!/usr/bin/env bash
set -euo pipefail
state_dir="${THEME_STATE_DIR:-$HOME/.local/state/dotfiles-theme}"
script_dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
palette_dir="$(readlink -f "$script_dir/../../themes/palettes")"
announce=true
pick_theme() {
local selected
selected="$({
for file in "$palette_dir"/*.json; do
printf '%s\t%s\n' "$(jq -r .name "$file")" "$(basename "$file" .json)"
done
} | sort | rofi -dmenu -i -p Theme -display-columns 1)"
[[ -n "$selected" ]] || exit 0
printf '%s\n' "$selected" | cut -f2
}
requested="${1:-}"
if [[ "$requested" == "--apply" ]]; then
announce=false
requested="$(cat "$state_dir/current" 2>/dev/null || printf black)"
elif [[ -z "$requested" ]]; then
requested="$(pick_theme)"
fi
theme="$(printf '%s' "$requested" | tr '[:upper:]' '[:lower:]')"
palette="$palette_dir/$theme.json"
[[ -f "$palette" ]] || { printf 'Unknown theme: %s\n' "$requested" >&2; exit 1; }
mkdir -p "$state_dir"
tmp_dir="$(mktemp -d "$state_dir/.generate.XXXXXX")"
trap 'rm -rf "$tmp_dir"' EXIT
value() { jq -er ".$1" "$palette"; }
value_or() { jq -er ".$1 // .$2" "$palette"; }
hex() { value "$1" | tr -d '#'; }
# Quickshell watches this file, while other apps import their generated format.
cp "$palette" "$tmp_dir/colors.json"
cat > "$tmp_dir/rofi.rasi" <<EOF
* {
bg: $(value background)e8;
bg-alt: $(value surface)f2;
bg-hover: $(value hover)f2;
fg: $(value foreground)ff;
fg-soft: $(value foregroundSoft)ff;
muted: $(value muted)ff;
edge: $(value border)ff;
edge-muted: $(value borderMuted)ff;
accent: $(value accent)ff;
danger: $(value danger)ff;
}
EOF
cat > "$tmp_dir/alacritty.toml" <<EOF
[colors.primary]
background = "$(value_or terminalBackground background)"
foreground = "$(value_or terminalForeground foregroundSoft)"
[colors.cursor]
cursor = "$(value accent)"
text = "$(value_or terminalBackground background)"
[colors.selection]
background = "$(value accent)"
text = "$(value_or terminalForeground foregroundSoft)"
EOF
cat > "$tmp_dir/dunstrc" <<EOF
[global]
monitor = 0
origin = top-right
offset = 10x10
width = 300
height = 100
gap_size = 6
padding = 10
horizontal_padding = 12
frame_width = 1
frame_color = "$(value borderMuted)"
font = MesloLGS Nerd Font 11
line_height = 2
corner_radius = 6
timeout = 5
[urgency_low]
background = "$(value surface)"
foreground = "$(value foregroundSoft)"
frame_color = "$(value borderMuted)"
[urgency_normal]
background = "$(value surface)"
foreground = "$(value foregroundSoft)"
frame_color = "$(value accent)"
[urgency_critical]
background = "$(value surface)"
foreground = "$(value danger)"
frame_color = "$(value danger)"
timeout = 0
EOF
cat > "$tmp_dir/hyprlock.conf" <<EOF
\$theme_background = rgb($(hex background))
\$theme_surface = rgb($(hex surface))
\$theme_foreground = rgb($(hex foreground))
\$theme_muted = rgb($(hex muted))
\$theme_border = rgb($(hex border))
\$theme_accent = rgb($(hex accent))
\$theme_danger = rgb($(hex danger))
\$theme_warning = rgb($(hex warning))
EOF
cat > "$tmp_dir/gtk.css" <<EOF
@define-color theme_bg_color $(value background);
@define-color theme_fg_color $(value foreground);
@define-color theme_base_color $(value surface);
@define-color theme_text_color $(value foreground);
@define-color theme_selected_bg_color $(value accent);
@define-color theme_selected_fg_color $(value background);
@define-color accent_bg_color $(value accent);
@define-color accent_color $(value accent);
@define-color accent_fg_color $(value background);
@define-color window_bg_color $(value background);
@define-color window_fg_color $(value foreground);
EOF
# Atomic replacement prevents file watchers from reading half-written configs.
for file in colors.json rofi.rasi alacritty.toml dunstrc hyprlock.conf gtk.css; do
mv "$tmp_dir/$file" "$state_dir/$file"
done
printf '%s\n' "$theme" > "$state_dir/current"
if [[ "$(value mode)" == light ]]; then
gtk_theme=Adwaita
color_scheme=prefer-light
else
gtk_theme=Adwaita-dark
color_scheme=prefer-dark
fi
if command -v gsettings >/dev/null; then
gsettings set org.gnome.desktop.interface gtk-theme "$gtk_theme" 2>/dev/null || true
gsettings set org.gnome.desktop.interface color-scheme "$color_scheme" 2>/dev/null || true
fi
if pgrep -x dunst >/dev/null && command -v dunstctl >/dev/null; then
dunstctl reload "$state_dir/dunstrc" >/dev/null 2>&1 || true
fi
if $announce; then
notify-send -t 1200 Theme "$(value name)" 2>/dev/null || true
fi