Universal Theme Manager for Linux
Under the hood β flexible patching system and atomic operations.
- π¨ Dynamic Palettes β Color extraction from wallpapers using a native PIL backend or matugen (Material You).
- π¦ Static Themes β Built-in themes included (Catppuccin, Gruvbox, Nord). Custom themes supported.
- π§ Template Engine β Place
.pawlettefiles alongside your configs; pawlette renders them using color tokens and filters. - ποΈ Filter Support β Apply special filters to color variables within templates.
- π Plugin System β Drop executables into
plugins/; they receive the palette via environment variables. - π XDG Compliant β All paths follow the XDG Base Directory specification; fully overridable for testing.
- Python 3.10+
- Pillow library
- matugen (optional, for Material You backend)
yay -S pawlettegit clone https://github.com/meowrch/pawlette
cd pawlette
uv sync
uv run pawlette --help# Apply theme from wallpaper (native backend, dark mode)
pawlette apply image ~/wallpapers/mountain.jpg
# Apply with matugen backend and light mode
pawlette apply image ~/wallpapers/mountain.jpg --backend matugen --mode light
# Apply from hex color
pawlette apply hex "#cba6f7"
# Apply a static theme
pawlette apply theme catppuccin-mocha
# Re-render templates without re-extracting the palette
pawlette renderPawlette follows a three-stage pipeline:
graph TD
A[Source] --> B{Source Type}
B -->|Image/Hex| C[Backend: native or matugen]
B -->|Theme Name| D[Load colors.toml]
C --> E[Palette: 40+ color roles]
D --> E
E --> F[Scan ~/.config/**/*.pawlette]
F --> G[Render templates with color tokens]
G --> H[Write files without .pawlette extension]
H --> I[Run plugins from `~/.local/share/pawlette/plugins/` passing PAWLETTE_* env vars]
I --> J[Apply theme system-wide]
style E stroke:#89b4fa,stroke-width:2px
style J stroke:#89b4fa,stroke-width:2px
Configuration is optional. Create ~/.config/pawlette/pawlette.toml to set default values:
# Default backend (overridden by --backend flag)
backend = "native" # or "matugen"
# Default mode (overridden by --mode flag)
mode = "dark" # or "light"
# Backend settings
[backends.matugen]
# Color preference when multiple dominant colors exist
prefer = "saturation" # darkness, lightness, saturation, less-saturation, value, closest-to-fallback
# Fallback color for "closest-to-fallback"
fallback_color = "#cba6f7"
# Plugin settings
[plugins.telegram]
template_dir = "~/.config/tg-config/"
output = "~/.config/tg-config/pawlette.tdesktop-theme"
background_image = "~/Pictures/wallpaper.jpg"See pawlette.toml.example for a full reference.
Settings are resolved in the following order:
- CLI arguments (highest priority)
- Configuration file (
pawlette.toml) - Hardcoded defaults (lowest priority)
Place .pawlette files near your configs. Pawlette scans ~/.config recursively and renders them:
~/.config/polybar/config.ini β generated (do not edit)
~/.config/polybar/config.ini.pawlette β template (edit this one)
Use {{token}} for color substitution with optional filters:
[colors]
background = {{color_bg}}
foreground = {{color_text}}
primary = {{color_primary}}
alert = {{color_red}}
# With filters
background-alt = {{color_bg | lighten 10}}
border = {{color_primary | alpha 80}}
rgb-color = {{color_primary | rgb}}| Filter | Example | Result |
|---|---|---|
alpha N |
{{color_primary | alpha 80}} |
#cba6f7cc (80% opacity) |
lighten N |
{{color_bg | lighten 20}} |
Lightened by 20% |
darken N |
{{color_text | darken 15}} |
Darkened by 15% |
strip |
{{color_primary | strip}} |
cba6f7 (without #) |
rgb |
{{color_primary | rgb}} |
203,166,247 |
uppercase |
{{color_primary | uppercase}} |
#CBA6F7 |
Combine multiple filters using |:
{{color_primary | darken 15 | strip}} β 9e7ec5
{{color_primary | darken 10 | strip | uppercase}} β A882D4
{{color_bg | alpha 90 | uppercase}} β #1E1E2Ee6
See examples/configs for ready-to-use templates:
alacritty/β Terminal emulatorkitty/β Terminal emulatorhyprland/β Wayland compositorgtk-3.0/,gtk-4.0/β GTK themesfish/,zsh/β Shell themes
Pawlette provides 40+ color roles organized into three categories:
| Variable | Description | Usage |
|---|---|---|
color_bg |
Deepest background | Window background, terminal background |
color_bg_alt |
Panels, sidebars | One step lighter than bg |
color_surface |
Buttons, input fields | Interactive elements |
color_surface_alt |
Hover / raised surface | Hover states, elevated cards |
color_text |
Main readable text | Primary text color |
color_text_muted |
Secondary text | Placeholders, descriptions |
color_text_subtle |
Disabled, captions | Disabled text, subtle hints |
color_primary |
Primary accent | Active tabs, highlights, links |
color_secondary |
Secondary accent | Alternative highlights |
color_border_active |
Active window border | Focused window border |
color_border_inactive |
Inactive window border | Unfocused window border |
color_cursor |
Terminal/Editor cursor | Cursor color |
color_selection_bg |
Text selection background | Highlighted text background |
| Variable | Description |
|---|---|
ansi_color0 |
Black (terminal background) |
ansi_color1 |
Red |
ansi_color2 |
Green |
ansi_color3 |
Yellow |
ansi_color4 |
Blue |
ansi_color5 |
Magenta |
ansi_color6 |
Cyan |
ansi_color7 |
White (terminal text) |
ansi_color8 |
Bright Black (dim background) |
ansi_color9 |
Bright Red |
ansi_color10 |
Bright Green |
ansi_color11 |
Bright Yellow |
ansi_color12 |
Bright Blue |
ansi_color13 |
Bright Magenta |
ansi_color14 |
Bright Cyan |
ansi_color15 |
Bright White (bright text) |
Always true hues β never aliases of ANSI colors. Generated at fixed target hues (0Β°, 120Β°, 225Β°, etc.) using primary saturation.
| Variable | Hue | Usage |
|---|---|---|
color_red |
0Β° | Errors, deletions, alerts |
color_green |
120Β° | Success, additions, confirmations |
color_yellow |
60Β° | Warnings, modifications |
color_blue |
225Β° | Info, links, functions |
color_cyan |
185Β° | Hints, strings |
color_magenta |
300Β° | Special, keywords |
Why semantic colors are generated:
Material You (matugen) shifts ALL colors toward the dominant wallpaper hue. This breaks color associations:
- Red becomes pink on purple wallpapers.
- Green becomes mint on purple wallpapers.
- Git diffs and syntax highlighting become unrecognizable.
Pawlette generates semantic colors at fixed hues but uses saturation/brightness from primary for harmony. Result: colors are recognizable (red = red) but visually consistent.
Plugins are any executable files in ~/.local/share/pawlette/plugins/:
- Shell scripts (
.sh) - Python scripts (
.py) - Compiled binaries
Pawlette runs them sequentially and passes the palette via environment variables.
Input:
- Receives environment variables
PAWLETTE_COLOR_*andPAWLETTE_ANSI_COLOR*(uppercase). - Receives plugin-specific settings as
PAWLETTE_PLUGIN_*variables from[plugins.<name>]inpawlette.toml.
Output:
- Exit code 0 = success.
- Non-zero = error (logged).
- Stdout/stderr are captured and logged.
Timeout: 30 seconds per plugin (configurable).
All palette colors are passed as uppercase environment variables:
# UI Roles
PAWLETTE_COLOR_BG="#1e1e2e"
...
# ANSI 16
PAWLETTE_ANSI_COLOR0="#1e1e2e"
...
# Semantic Colors
PAWLETTE_COLOR_RED="#f05b5b"#!/bin/bash
# ~/.local/share/pawlette/plugins/hyprland.sh
# Set Hyprland border colors
if pgrep -x "Hyprland" > /dev/null; then
if command -v hyprctl &> /dev/null; then
hyprctl keyword general:col.active_border "rgb(${PAWLETTE_COLOR_BORDER_ACTIVE#\#})"
hyprctl keyword general:col.inactive_border "rgb(${PAWLETTE_COLOR_BORDER_INACTIVE#\#})"
fi
fi
# Reload waybar
if pgrep -x "waybar" > /dev/null; then
killall -SIGUSR2 waybar
fiMake it executable:
chmod +x ~/.local/share/pawlette/plugins/hyprland.sh#!/usr/bin/env python3
# ~/.local/share/pawlette/plugins/gtk-theme.py
import os
import subprocess
primary = os.environ["PAWLETTE_COLOR_PRIMARY"]
bg = os.environ["PAWLETTE_COLOR_BG"]
# Apply GTK theme
subprocess.run(["gsettings", "set", "org.gnome.desktop.interface", "gtk-theme", "Adwaita-dark"])
subprocess.run(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "prefer-dark"])
print(f"Applied GTK theme with primary={primary}")Note: Python plugins don't need a shebang β pawlette calls them with the current interpreter automatically (with virtualenv support).
The plugin name is derived from the filename (without extension):
telegram.pyβ config section[plugins.telegram]gtk-reload.shβ config section[plugins.gtk-reload]
pawlette apply theme catppuccin-mocha
pawlette apply theme gruvbox-dark
pawlette apply theme nord- Create a theme directory:
mkdir -p ~/.local/share/pawlette/themes/my-theme- Create
colors.tomlwith all palette fields:
[colors]
color_bg = "#1e1e2e"
...- (Optional) Create
meta.toml:
name = "My Theme"
author = "Your Name"
description = "A beautiful custom theme"- Apply it:
pawlette apply theme my-theme~/.config/pawlette/
βββ pawlette.toml β User configuration (optional)
βββ **/*.pawlette β Template files (scattered across ~/.config)
~/.local/share/pawlette/
βββ themes/ β Static theme definitions
β βββ catppuccin-mocha/
β β βββ colors.toml
β β βββ meta.toml
βββ plugins/ β Executable plugins
βββ hyprland.sh
βββ telegram.py
~/.local/state/pawlette/
βββ active_palette.json β Cached active palette (for `pawlette render`)
~/.cache/pawlette/
βββ matugen_output.json β matugen JSON cache
Preview what will be done without making changes:
pawlette apply image ~/wallpaper.jpg --dry-run --print-paletteOverride XDG paths for testing without affecting user config:
XDG_CONFIG_HOME=/tmp/test/config \
XDG_DATA_HOME=/tmp/test/data \
XDG_STATE_HOME=/tmp/test/state \
XDG_CACHE_HOME=/tmp/test/cache \
pawlette apply theme catppuccin-mochaNative Backend (default):
- Fast, no external dependencies.
- PIL median-cut quantization + k-means clustering.
- Good for most wallpapers.
Matugen Backend:
- Material You algorithm.
- Requires external binary.
- Better for complex images with multiple dominant colors.
git clone https://github.com/meowrch/pawlette
cd pawlette
uv syncpytest
pytest --cov=pawlette --cov-report=term-missingIf Pawlette makes your desktop look better:
| Crypto | Address |
|---|---|
| TON | UQB9qNTcAazAbFoeobeDPMML9MG73DUCAFTpVanQnLk3BHg3 |
| Ethereum | 0x56e8bf8Ec07b6F2d6aEdA7Bd8814DB5A72164b13 |
| Bitcoin | bc1qt5urnw7esunf0v7e9az0jhatxrdd0smem98gdn |
| Tron | TBTZ5RRMfGQQ8Vpf8i5N8DZhNxSum2rzAs |
Your support motivates us to build more cool features! β€οΈ