# NixOS Dynamic Theme Switcher - Implementation Instructions ## Objective Build a time-based dynamic theme switching system on NixOS that: 1. Extracts color palettes from wallpapers using pywal16 with haishoku backend 2. Generates and applies themes for GTK, Qt/Kvantum, Ghostty, and other applications 3. Switches between light/dark themes based on time of day 4. Integrates with existing wpaperd wallpaper rotation service ## Current System State ### Existing Theming Configuration **GTK Configuration:** - Current theme: `catppuccin-macchiato-lavender-compact+rimless` - Settings location: `~/.config/gtk-3.0/settings.ini`, `~/.config/gtk-4.0/settings.ini` - Icon theme: `Papirus-Dark` - Cursor: `Bibata-Modern-Classic` - Files are symlinked from Nix store via home-manager **Qt/Kvantum Configuration:** - Current theme: `catppuccin-macchiato-lavender` - Location: `~/.config/Kvantum/kvantum.kvconfig` - Style: kvantum (both Qt5 and Qt6) - Environment variable: `QT_STYLE_OVERRIDE=kvantum` **Wallpaper Service:** - Using wpaperd with home-manager integration - Wallpapers in: `/home/nate/nixos/shared/modules/services/wallpapers/` - Organized into `Light/` and `Dark/` subdirectories - Service configured via `services.wallpaperRotator` module **Actions Required:** 1. Disable/modify static GTK theme configuration in home.nix 2. Disable/modify static Kvantum theme configuration 3. Integrate with wpaperd to trigger theme changes on wallpaper rotation 4. Remove hardcoded Catppuccin theme packages when pywal themes are working ## Implementation Components ### 3. Install Required Packages Add to module configuration: ```nix home.packages = with pkgs; [ pywal16 # Color scheme generator with 16-color support python3Packages.haishoku # Recommended backend for pywal16 wpaperd # Already installed via wallpaper service ]; ``` **Available pywal16 backends:** modern_colorthief, wal, fast_colorthief, haishoku, colorthief, colorz, okthief, schemer2 ### 4. Color Extraction & Theme Generation #### 4.1 Pywal16 Built-in Templates **Pywal16 already includes templates for:** - Ghostty: `ghostty.conf` template (tested and confirmed) - CSS variables: `colors.css` template - Waybar: `colors-waybar.css` - Hyprland: `colors-hyprland.conf` - Rofi: `colors-rofi-dark.rasi` and `colors-rofi-light.rasi` - Sway: `colors-sway` - Mako: `colors-mako` - Foot, Kitty, Alacritty terminal configs - Many others (47 templates total) **Output location:** `~/.cache/wal/` after running `wal -i /path/to/wallpaper` **Usage:** ```bash wal -i /path/to/wallpaper --backend haishoku -n # -n flag skips setting wallpaper (wpaperd handles that) # --backend haishoku uses the haishoku color extraction # -l flag for light themes ``` #### 4.2 GTK Theme Generation **Goal:** Create custom GTK 3.0 and GTK 4.0 CSS themes from pywal colors **Note:** Pywal16 includes `colors.css` template with CSS variables, but NOT full GTK themes. **Required files:** - `~/.config/gtk-3.0/gtk.css` - Custom CSS overlay - `~/.config/gtk-4.0/gtk.css` - Custom CSS overlay **Approach:** Create pywal template to generate GTK CSS using colors.json output - Map pywal colors to GTK color definitions - Use @define-color for GTK theme variables - Apply to common widgets #### 4.3 Qt/Kvantum Theme Generation **Goal:** Generate Kvantum theme from pywal colors **Kvantum structure (.kvconfig file):** - `[General]` section with metadata - `[GeneralColors]` section with color definitions (window.color, base.color, button.color, etc.) - `[Hacks]` section with tweaks - Per-widget sections (PanelButtonCommand, etc.) **Location:** `~/.config/Kvantum/PywalTheme/PywalTheme.kvconfig` **Required:** Create pywal template that maps colors.json to .kvconfig format **Optional:** SVG file can be minimal or omitted for basic themes **Application:** Use `kvantummanager --set PywalTheme` to activate ### 5. Theme Application Script **Script: `apply-theme.sh`** **Parameters:** - `--light` or `--dark` (theme mode) - Optional: `--wallpaper-path` (specific wallpaper, otherwise use wpaperd current) **Script Logic:** 1. **Determine wallpaper source:** - If `--wallpaper-path` provided, use it - Otherwise, let wpaperd select based on mode (Light/ or Dark/ directory) - Use `wpaperctl` to interact with wpaperd if needed 2. **Generate color scheme:** ```bash if [ "$MODE" = "light" ]; then wal -i "$WALLPAPER" --backend haishoku -l -n else wal -i "$WALLPAPER" --backend haishoku -n fi ``` 3. **Apply GTK theme:** ```bash # pywal doesn't generate full GTK themes, use custom CSS ln -sf ~/.cache/wal/gtk-3.0.css ~/.config/gtk-3.0/gtk.css ln -sf ~/.cache/wal/gtk-4.0.css ~/.config/gtk-4.0/gtk.css gsettings set org.gnome.desktop.interface color-scheme "prefer-$MODE" ``` 4. **Apply Qt/Kvantum theme:** ```bash kvantummanager --set PywalTheme ``` 5. **Reload configurations:** - Ghostty: Auto-reloads config on file change (no action needed) - GTK apps: Automatically detect gsettings changes - Qt apps: May need restart for Kvantum changes - Hyprland/Sway: Reload via IPC if using pywal templates 6. **Optional: Update Flatpak:** ```bash flatpak override --user --filesystem=~/.cache/wal:ro ``` ### 6. Wallpaper Integration & Time-Based Switching **Option A: Integrate with wpaperd events (Recommended)** - Modify wallpaper-rotator module to support Light/Dark subdirectories - Change wallpaper directory based on time of day - Trigger theme generation on wallpaper change **Option B: Independent systemd timer** **Timer configuration:** ```nix systemd.user.timers.theme-switcher = { Unit.Description = "Dynamic Theme Switcher Timer"; Timer = { OnCalendar = [ "*-*-* 06:00:00" "*-*-* 18:00:00" ]; # 6am light, 6pm dark Persistent = true; }; Install.WantedBy = [ "timers.target" ]; }; ``` **Service configuration:** ```nix systemd.user.services.theme-switcher = { Unit.Description = "Dynamic Theme Switcher"; Service = { Type = "oneshot"; ExecStart = "${pkgs.bash}/bin/bash ${./scripts/theme-switcher.sh}"; Environment = "PATH=${lib.makeBinPath [ pkgs.pywal16 pkgs.coreutils ]}"; }; }; ``` **Script determines light/dark based on current hour:** ```bash hour=$(date +%H) if [ $hour -ge 6 ] && [ $hour -lt 18 ]; then MODE="light" WALLPAPER_DIR="Light" else MODE="dark" WALLPAPER_DIR="Dark" fi ``` ### 7. NixOS Module Structure **Module: `theme_switcher/default.nix`** ```nix { config, lib, pkgs, ... }: let cfg = config.services.themeSwitcher; in { options.services.themeSwitcher = { enable = lib.mkEnableOption "dynamic theme switching based on time and wallpapers"; user = lib.mkOption { type = lib.types.str; description = "Username for theme switching"; }; wallpaperPath = lib.mkOption { type = lib.types.str; default = "/home/${cfg.user}/nixos/shared/modules/services/wallpapers"; description = "Path to wallpaper directories (Light/ and Dark/)"; }; backend = lib.mkOption { type = lib.types.enum [ "haishoku" "modern_colorthief" "fast_colorthief" ]; default = "haishoku"; description = "Pywal color extraction backend"; }; lightTime = lib.mkOption { type = lib.types.str; default = "06:00:00"; description = "Time to switch to light theme (HH:MM:SS)"; }; darkTime = lib.mkOption { type = lib.types.str; default = "18:00:00"; description = "Time to switch to dark theme (HH:MM:SS)"; }; }; config = lib.mkIf cfg.enable { home-manager.users.${cfg.user} = { home.packages = with pkgs; [ pywal16 python3Packages.haishoku ]; # Install theme application scripts home.file.".local/bin/apply-theme.sh" = { source = ./scripts/apply-theme.sh; executable = true; }; # Pywal custom templates for GTK and Kvantum xdg.configFile."wal/templates/gtk-3.0.css".source = ./templates/gtk-3.0.css; xdg.configFile."wal/templates/gtk-4.0.css".source = ./templates/gtk-4.0.css; xdg.configFile."wal/templates/kvantum.kvconfig".source = ./templates/kvantum.kvconfig; # Systemd timer and service systemd.user.timers.theme-switcher = { /* ... */ }; systemd.user.services.theme-switcher = { /* ... */ }; # Keep Qt/GTK enabled but don't set static themes qt = { enable = true; platformTheme.name = "kvantum"; style.name = "kvantum"; }; gtk.enable = true; }; }; } ``` **Template files needed:** - `templates/gtk-3.0.css` - GTK3 theme using pywal variables - `templates/gtk-4.0.css` - GTK4 theme using pywal variables - `templates/kvantum.kvconfig` - Kvantum config using pywal colors ## Testing & Validation ### 8. Test Plan 1. **Test pywal color extraction:** ```bash # Test with light wallpaper wal -i ~/nixos/shared/modules/services/wallpapers/Light/IU-Light.jpg --backend haishoku -l -n cat ~/.cache/wal/colors.json # Verify colors extracted # Test with dark wallpaper wal -i ~/nixos/shared/modules/services/wallpapers/Dark/IU-Dark.jpg --backend haishoku -n cat ~/.cache/wal/colors.json # Verify colors extracted ``` 2. **Verify pywal template output:** ```bash ls -la ~/.cache/wal/ # Should see: colors.json, ghostty.conf, colors.css, and custom templates cat ~/.cache/wal/ghostty.conf # Check ghostty colors ``` 3. **Manual theme application:** ```bash ~/.local/bin/apply-theme.sh --light # Check: GTK apps, Qt apps, Ghostty colors update ~/.local/bin/apply-theme.sh --dark # Check: Theme switches to dark ``` 4. **Verify theme files:** - `~/.cache/wal/colors.json` - Generated by pywal - `~/.cache/wal/gtk-3.0.css` - From custom template - `~/.cache/wal/gtk-4.0.css` - From custom template - `~/.cache/wal/kvantum.kvconfig` - From custom template - `~/.cache/wal/ghostty.conf` - From built-in template 5. **Test Kvantum theme:** ```bash kvantummanager --set PywalTheme # Or check: cat ~/.config/Kvantum/kvantum.kvconfig ``` 6. **Test systemd automation:** ```bash systemctl --user start theme-switcher.service systemctl --user status theme-switcher.timer systemctl --user list-timers | grep theme ``` 7. **Test with live applications:** - Open GTK app (nautilus, gnome-calculator) - verify colors - Open Qt app (if available) - verify Kvantum theme - Open Ghostty terminal - verify color palette matches wallpaper - Check waybar/rofi if using pywal templates for those ## Implementation Steps Summary ### Phase 1: Setup pywal with existing wallpapers 1. Create theme_switcher module with pywal16 package 2. Test color extraction on Light/ and Dark/ wallpapers 3. Verify built-in templates (ghostty.conf, colors.css) generate correctly ### Phase 2: Create custom templates 1. Create `templates/gtk-3.0.css` using pywal variables 2. Create `templates/gtk-4.0.css` using pywal variables 3. Create `templates/kvantum.kvconfig` mapping colors.json to Kvantum format 4. Install templates to `~/.config/wal/templates/` ### Phase 3: Theme application script 1. Write `apply-theme.sh` script that: - Runs pywal with appropriate flags (-l for light) - Symlinks generated CSS to GTK config directories - Copies Kvantum config to `~/.config/Kvantum/PywalTheme/` - Runs `kvantummanager --set PywalTheme` - Updates gsettings for color-scheme preference ### Phase 4: Time-based automation 1. Create systemd timer for light/dark switching times 2. Create systemd service that calls apply-theme.sh 3. Script determines mode based on current time 4. Selects random wallpaper from Light/ or Dark/ directory ### Phase 5: Integration and cleanup 1. Modify existing home.nix files to disable static themes when module enabled 2. Keep icon themes, cursor themes (not wallpaper-dependent) 3. Test on one host before deploying to all 4. Document wpaperd integration approach for future enhancement ## Key Technical Details **Pywal template syntax:** ``` {background} -> #081212 {foreground} -> #c1c3c3 {color0} -> #081212 ... {color15} -> #c1c3c3 {cursor} -> #c1c3c3 {wallpaper} -> /path/to/wallpaper.jpg ``` **Template location:** `~/.config/wal/templates/filename.ext` **Output location:** `~/.cache/wal/filename.ext` **Ghostty:** Automatically reloads config when files change - no manual reload needed **GTK:** Changes propagate via gsettings/dconf - running apps update automatically **Kvantum:** May require app restart to see changes (Qt limitation) **Wallpaper directories:** - Light: `/home/nate/nixos/shared/modules/services/wallpapers/Light/` - Dark: `/home/nate/nixos/shared/modules/services/wallpapers/Dark/`