| .. | ||
| templates | ||
| apply_themes.py | ||
| color_mapper.py | ||
| default.nix | ||
| EXAMPLE_CONFIG.nix | ||
| helix_theme_tester.py | ||
| OUTLINE.md | ||
| README.md | ||
| TESTING_GUIDE.md | ||
| theme_switcher.py | ||
| wallpaper_rotation.py | ||
Dynamic Theme Switcher Module
A NixOS module that automatically generates and applies color themes from wallpapers using pywal16, with integrated wpaperd wallpaper management and support for GTK, Qt/Kvantum, Ghostty, and other applications.
Features
- Integrated Wallpaper Management: Built-in wpaperd integration with configurable rotation modes
- Automatic Color Extraction: Uses pywal16 with configurable backends (haishoku, colorthief, etc.) to extract color palettes from wallpapers
- Multi-Application Support: Generates themes for GTK 3.0, GTK 4.0, Qt/Kvantum, Ghostty terminal, and more
- Time-Based Switching: Automatically switches between light and dark themes based on configured times
- Flexible Rotation Modes: Choose between continuous rotation, switch-only, or hybrid modes
- Manual Control: Provides scripts for manual theme switching
- Synchronized Wallpaper & Theme: Wallpaper and color theme always match
Directory Structure
theme_switcher/
├── default.nix # Main module configuration
├── templates/
│ ├── gtk-3.0.css # GTK 3.0 theme template
│ ├── gtk-4.0.css # GTK 4.0 theme template
│ └── PywalTheme.kvconfig # Kvantum Qt theme template
├── OUTLINE.md # Implementation specification
└── README.md # This file
Installation
1. Import the Module
Add the module to your host configuration:
# In your host's configuration.nix or desktop-configuration.nix
imports = [
../../shared/modules/services/theme_switcher
];
2. Enable and Configure
services.themeSwitcher = {
enable = true;
user = "yourUsername";
# Optional: Customize settings
backend = "haishoku"; # Color extraction backend
lightTime = "06:00:00"; # Switch to light theme at 6 AM
darkTime = "18:00:00"; # Switch to dark theme at 6 PM
enableAutoSwitch = true; # Enable automatic time-based switching
# Wallpaper rotation settings
rotation = {
mode = "continuous"; # Options: continuous, switch-only, hybrid
interval = "5m"; # Rotation interval (for continuous mode)
};
# Wpaperd configuration
wpaperd = {
enable = true;
mode = "center"; # Options: center, fit, stretch, tile
transition.effect = "fade";
transition.duration = 300; # milliseconds
};
};
Note: If you were previously using the separate wallpaper-rotator module, you should disable it to avoid conflicts.
3. Wallpaper Directory Structure
Ensure your wallpaper directory has Light and Dark subdirectories:
wallpapers/
├── Light/
│ ├── wallpaper1.jpg
│ ├── wallpaper2.jpg
│ └── ...
└── Dark/
├── wallpaper1.jpg
├── wallpaper2.jpg
└── ...
4. Rebuild Your System
sudo nixos-rebuild switch --flake .#hostname
Configuration Options
Core Options
services.themeSwitcher.enable
- Type: boolean
- Default: false
- Description: Enable the dynamic theme switcher with integrated wallpaper management
services.themeSwitcher.user
- Type: string
- Required: yes
- Description: Username for which to apply theme switching
services.themeSwitcher.wallpaperPath
- Type: string
- Default:
${homeDirectory}/nixos/shared/modules/services/wallpapers - Description: Path to wallpaper directories (must contain Light/ and Dark/ subdirectories)
services.themeSwitcher.backend
- Type: enum
- Default:
"haishoku" - Options:
"haishoku","modern_colorthief","fast_colorthief","colorthief","colorz","wal" - Description: Pywal color extraction backend algorithm
services.themeSwitcher.lightTime
- Type: string
- Default:
"06:00:00" - Description: Time to switch to light theme (HH:MM:SS format)
services.themeSwitcher.darkTime
- Type: string
- Default:
"18:00:00" - Description: Time to switch to dark theme (HH:MM:SS format)
services.themeSwitcher.enableAutoSwitch
- Type: boolean
- Default: true
- Description: Enable automatic time-based theme switching via systemd timer
Rotation Options
services.themeSwitcher.rotation.mode
- Type: enum
- Default:
"continuous" - Options:
"continuous": Rotate wallpapers every interval AND regenerate theme each time"switch-only": Only change wallpaper at light/dark switch times (6 AM/6 PM)"hybrid": Rotate wallpapers but only regenerate theme at switch times (not yet implemented)
- Description: Wallpaper rotation and theme generation behavior
services.themeSwitcher.rotation.interval
- Type: string
- Default:
"5m" - Example:
"30s","10m","1h" - Description: How often to rotate wallpapers in continuous mode
services.themeSwitcher.rotation.sorting
- Type: enum
- Default:
"random" - Options:
"random" - Description: Wallpaper selection method (currently only random is supported)
Wpaperd Integration Options
services.themeSwitcher.wpaperd.enable
- Type: boolean
- Default: true
- Description: Enable wpaperd wallpaper daemon integration
services.themeSwitcher.wpaperd.mode
- Type: enum
- Default:
"center" - Options:
"center","fit","fit-border-color","stretch","tile" - Description: How to display wallpapers when size differs from display resolution
services.themeSwitcher.wpaperd.transition.effect
- Type: string
- Default:
"fade" - Example:
"simple","fade" - Description: Wallpaper transition effect name
services.themeSwitcher.wpaperd.transition.duration
- Type: integer
- Default: 300
- Description: Transition duration in milliseconds
Manual Usage
Apply Theme Manually
The module installs apply-theme.sh to ~/.local/bin/:
# Apply light theme with random wallpaper from Light/ directory
~/.local/bin/apply-theme.sh --light
# Apply dark theme with random wallpaper from Dark/ directory
~/.local/bin/apply-theme.sh --dark
# Apply theme with specific wallpaper
~/.local/bin/apply-theme.sh --light --wallpaper-path /path/to/wallpaper.jpg
Check Systemd Timer Status
# View timer status
systemctl --user status theme-switcher.timer
# List upcoming timer events
systemctl --user list-timers | grep theme
# Manually trigger theme switch
systemctl --user start theme-switcher.service
# View service logs
journalctl --user -u theme-switcher.service
Test Pywal Color Extraction
# Test light theme generation
wal -i ~/nixos/shared/modules/services/wallpapers/Light/IU-Light.jpg --backend haishoku -l -n
# Test dark theme generation
wal -i ~/nixos/shared/modules/services/wallpapers/Dark/IU-Dark.jpg --backend haishoku -n
# View generated colors
cat ~/.cache/wal/colors.json
# View generated templates
ls -la ~/.cache/wal/
Generated Files
After running the theme switcher, pywal generates the following files:
Pywal Cache (~/.cache/wal/)
colors.json- Extracted color palette in JSON formatcolors.sh- Shell script with color variablesghostty.conf- Ghostty terminal theme (auto-reloads)gtk-3.0.css- GTK 3.0 theme (from custom template)gtk-4.0.css- GTK 4.0 theme (from custom template)PywalTheme.kvconfig- Kvantum Qt theme (from custom template)
User Configuration
~/.config/gtk-3.0/gtk.css- Symlink to pywal-generated GTK3 theme~/.config/gtk-4.0/gtk.css- Symlink to pywal-generated GTK4 theme~/.config/Kvantum/PywalTheme/PywalTheme.kvconfig- Kvantum theme copy
Integration with Existing Themes
When enabling this module, you should adjust your existing theme configuration:
Before (Static Catppuccin Theme)
gtk = {
enable = true;
theme = {
name = "catppuccin-macchiato-lavender-compact+rimless";
package = pkgs.catppuccin-gtk.override { ... };
};
};
qt = {
enable = true;
platformTheme.name = "kvantum";
style.name = "kvantum";
};
xdg.configFile."Kvantum/kvantum.kvconfig".source = ...;
After (Dynamic Pywal Theme)
# The theme switcher module automatically sets:
# - gtk.enable = true
# - qt.enable = true
# - qt.platformTheme.name = "kvantum"
# - qt.style.name = "kvantum"
# You can still keep static icon and cursor themes:
gtk.iconTheme = {
name = "Papirus-Dark";
package = pkgs.catppuccin-papirus-folders;
};
gtk.cursorTheme = {
name = "Bibata-Modern-Classic";
package = pkgs.bibata-cursors;
};
Troubleshooting
Theme Not Applying
# Check if pywal generated files
ls -la ~/.cache/wal/
# Check systemd service status
systemctl --user status theme-switcher.service
# View detailed logs
journalctl --user -u theme-switcher.service -n 50
# Manually run apply-theme.sh with verbose output
~/.local/bin/apply-theme.sh --light
Kvantum Theme Not Working
# Check if Kvantum theme was created
ls -la ~/.config/Kvantum/PywalTheme/
# Manually set Kvantum theme
kvantummanager --set PywalTheme
# Check if Qt applications are using Kvantum
echo $QT_STYLE_OVERRIDE # Should be "kvantum"
Colors Don't Match Wallpaper
Try a different pywal backend:
services.themeSwitcher.backend = "modern_colorthief"; # or "fast_colorthief", "colorthief", etc.
GTK Applications Not Updating
# Check if GTK CSS files exist
ls -la ~/.config/gtk-3.0/gtk.css
ls -la ~/.config/gtk-4.0/gtk.css
# Restart GTK applications for changes to take effect
Supported Applications
Automatically Themed
- GTK 3 Applications: Nautilus, GNOME Calculator, etc.
- GTK 4 Applications: GNOME Text Editor, newer GNOME apps
- Qt Applications: KDE apps when using Kvantum
- Ghostty Terminal: Auto-reloads on config change
- Waybar: If using pywal template (built-in)
- Rofi: If using pywal template (built-in)
- Hyprland/Sway: If using pywal template (built-in)
Manual Integration Required
- Firefox: Use pywal-firefox extension
- VSCode: Use pywal theme extension
- Other Applications: Check if pywal templates exist in
~/.cache/wal/
Advanced Usage
Custom Pywal Templates
To add your own templates, create files in ~/.config/wal/templates/:
# Template file: ~/.config/wal/templates/myapp.conf
# Use pywal template variables
background = {background}
foreground = {foreground}
color0 = {color0}
...
color15 = {color15}
After running apply-theme.sh, find your generated file in ~/.cache/wal/myapp.conf.
Disable Automatic Switching
To use manual control only:
services.themeSwitcher = {
enable = true;
user = "username";
enableAutoSwitch = false; # Disable timer
};
Then use ~/.local/bin/apply-theme.sh manually as needed.
Rotation Modes Explained
Continuous Mode (Default)
- Wallpaper rotates every interval (e.g., 5 minutes)
- Theme is regenerated from each new wallpaper
- Wallpaper and theme always match
- Resource usage: Moderate (pywal runs every interval)
- Best for: Users who want frequently changing, perfectly matched themes
Switch-Only Mode
- Wallpaper changes only at light/dark switch times (6 AM / 6 PM)
- Theme is generated once per switch
- Wallpaper and theme always match
- Resource usage: Minimal (pywal runs twice per day)
- Best for: Users who prefer consistent themes throughout the day
Hybrid Mode (Future)
- Wallpaper rotates every interval
- Theme is generated only at switch times
- Wallpaper and theme may not match mid-day
- Not yet implemented
Migration from wallpaper-rotator Module
If you were previously using the separate wallpaper-rotator module:
-
Disable the old module in your configuration:
# Comment out or remove: # services.wallpaperRotator.enable = true; -
Enable themeSwitcher with equivalent settings:
services.themeSwitcher = { enable = true; user = "yourUsername"; rotation.interval = "5m"; # Same as old duration setting wpaperd.mode = "center"; # Same as old mode setting }; -
Rebuild your system - wpaperd will now be managed by themeSwitcher
Technical Details
- Pywal Template Syntax:
{background},{foreground},{color0}-{color15},{cursor},{wallpaper} - Template Location:
~/.config/wal/templates/filename.ext - Output Location:
~/.cache/wal/filename.ext - Color Extraction: 16-color palette using pywal16
- GTK Reload: Automatic via gsettings changes
- Kvantum Reload: May require app restart
- Ghostty Reload: Automatic on config file change
Future Enhancements
- Direct wpaperd integration (trigger theme change on wallpaper rotation)
- Per-monitor theme configuration
- Additional application templates (Firefox, VSCode, etc.)
- Theme preview before applying
- Fallback color schemes for monochrome wallpapers