Theming - Ziad0dev/dots GitHub Wiki
One palette file per theme, a directory of templates, and themectl to render one into the other. Switching themes writes only to ~/.local/state/dots/ β never into the repo, so it never shows up in git status.
themectl list # 46 themes
themectl set kanagawa
themectl set auto # palette from the current wallpaper (matugen)
themectl set auto ~/x.jpg # β¦or from a given image
# while auto is current, themectl bg β¦ re-derives it
themectl next # / prev β alphabetical, wraps
themectl current
themectl reload # re-render the current theme (after editing a template)
themectl render # same files, but no app reloads and no wallpaper change
themectl bg # print current wallpaper
themectl bg next # / prev β cycles ~/Pictures/wallpapers
themectl bg set ~/Pictures/wallpapers/x.jpgOr: SUPER + CTRL + SHIFT + Space (theme picker), SUPER + SHIFT + T (next theme), SUPER + E (wallpaper picker), SUPER + CTRL + E (next wallpaper).
themectl is in every profile (home/themectl.nix), so the minimal one's prompt, fzf, tmux, Neovim and btop are themed too.
A fresh home gets dots.theme (oxocarbon) on its first activation, so nothing starts unstyled. With dots.liveConfig = false the rendered files and links point into the generation's store path, so every switch also runs themectl render.
- Sources
config/themes/<name>/colors.sh(ortheme.sh), computes five derived colours, and renders everyconfig/themes/_templates/*.ininto~/.local/state/dots/theme/. - Records the name in
~/.local/state/dots/theme/current. - Points
~/.local/state/dots/shell/current/at the palette, wallpaper directory, preview and background β what Quickshell and the lock screen read. - Writes
/var/lib/dots-theme/sddm.jsonfor the login greeter (if the directory is writable). - Links or copies the rendered Kvantum, GTK and Vencord files into place, and concatenates
config/zen/sites/*.cssintozen-sites.css. - Reloads: Ghostty (
SIGUSR2), Quickshell (theme reloadover IPC), GTK (theme name bounce), btop (SIGUSR2), tmux, dunst (sway only), Hyprland, swayosd. Zen reads its CSS only at startup. - If the theme ships a
wallpaper.*, sets it with a randomawwwtransition.
config/themes/<name>/colors.sh β sourced by bash, so plain key="#rrggbb" lines. All 47 keys are expected:
| Group | Keys |
|---|---|
| Terminal |
background foreground cursor accent selection_foreground selection_background
|
| ANSI |
color0 β¦ color15
|
| Short aliases |
bg fg
|
| base16 |
base00 β¦ base0F
|
| Named |
red green yellow blue magenta cyan pink
|
Optional: border β the Hyprland active border becomes color0 β border β color0 (fading out at both ends) instead of the default color0 β color1, and the Quickshell frame rim and lock-screen band use it blended 55 % toward background instead of color1.
The Quickshell bar and lock screen take their muted text from color8 but lift it toward foreground until it reaches a 4.5:1 contrast against background (Palette.js) β terminal "bright black" is often far darker (oled: 1.85:1).
themectl adds five derived colours β dim, muted (foreground blended 35 % / 60 % toward background), surface (background 8 % toward foreground), accent_dim and accent_container (accent blended 25 % / 72 % toward background). Several palettes have base03 == base04 == base05; templates that need a readable grey ramp should use these instead.
Other files in a theme directory:
| File | Used by |
|---|---|
wallpaper.{jpg,jpeg,png,webp} |
set on themectl set; baked (blurred) into SDDM when this theme is dots.sddm.theme
|
preview.png / preview.jpg
|
theme picker, fastfetch's random art |
Templates use ${key} for #rrggbb, ${key_hex} for the bare hex (Hyprland's rgb() wants that) and ${key_rgb} for r g b decimals. Substitution is envsubst with an explicit variable list, so anything that isn't a palette key β $TIME, $HOME, shell variables in the target format β passes through untouched.
| Template | Rendered to | Consumed by |
|---|---|---|
hyprland.lua.in |
hyprland.lua |
pcall(dofile, β¦) at the end of config/hypr/hyprland.lua β window borders |
ghostty.in |
ghostty |
config-file = ?β¦ in config/ghostty/config (the ? makes it optional) |
nvim.lua.in |
nvim.lua |
watched by Neovim's config/nvim/lua/config/theme.lua, re-applied live in every instance |
yazi.toml.in |
yazi.toml |
linked as ~/.config/yazi/theme.toml (home/yazi.nix) |
btop.theme.in |
btop.theme |
linked as ~/.config/btop/themes/dots.theme
|
fzf.fish.in |
fzf.fish |
sourced by fish at interactive start |
swayosd.css.in |
swayosd.css |
linked as ~/.config/swayosd/style.css
|
gtk.css.in |
gtk.css |
copied to ~/.config/gtk-3.0/ and gtk-4.0/
|
kvantum.kvconfig.in |
kvantum.kvconfig |
linked into ~/.config/Kvantum/KvGlass#/
|
vencord-quickcss.css.in |
vencord-quickcss.css |
copied to Vencord's quickCss.css if Vencord's settings dir exists; also hides the Nitro, Shop and Quests tabs and the gift button |
zathura.in |
zathura |
included at the end of the zathurarc from home/documents.nix
|
tmux.conf.in |
tmux.conf |
source-file -q at the end of config/tmux/tmux.conf; re-sourced on switch |
cava.in |
cava |
linked as ~/.config/cava/config (home/theming.nix) |
zen-palette.css.in |
zen-palette.css |
--dots-* custom properties, @imported by config/zen/userContent.css for the per-site styles in config/zen/sites/
|
share-picker.css.in |
share-picker.css |
stylesheets: in config/hyprland-preview-share-picker/config.yaml
|
Not template-driven but still themed:
| Thing | How |
|---|---|
| Quickshell Rise, lock screen | read colors.sh directly via ~/.local/state/dots/shell/current/theme/colors.sh
|
| SDDM | baked palette + live sddm.json β see Desktop β login
|
| Spotify (Spicetify) | palette of dots.theme (home-manager option, default oxocarbon) parsed from colors.sh at build time
|
| Obsidian | hardcoded oxocarbon |
| Websites in Zen |
config/zen/sites/<domain>.css (from dusky), each wrapped in @-moz-document domain(β¦) and coloured from --dots-*; delete a file to stop theming that site |
cp -r ~/dots/config/themes/kanagawa ~/dots/config/themes/mytheme
$EDITOR ~/dots/config/themes/mytheme/colors.sh
# drop in wallpaper.jpg / preview.png if you have them
themectl set mythemeNo rebuild β themectl reads the working tree directly. git add it when you're happy.
Every key must be present. themectl set checks before rendering and refuses with theme '<name>' is missing palette keys: β¦, leaving the previous theme in place β copying an existing palette and editing values is the safe way to start.
A rebuild is needed if you make the new theme dots.sddm.theme or dots.theme, since those are baked.
- Write
config/themes/_templates/<name>.inusing${key}/${key_hex}. -
themectl reloadβ every*.inis rendered automatically, no registration. - Point the app at
~/.local/state/dots/theme/<name>: amkOutOfStoreSymlinkin a home module, an include line in the app's own config underconfig/, or a*_compatstep inscripts/themectl.shif it needs copying (that last one needs a rebuild β themectl is a packaged script). - If the app needs a nudge to pick up changes, add it to
reload_appsinscripts/themectl.sh.
If a template references a name that isn't a palette key, it passes through unsubstituted and themectl warns unsubstituted tokens remain in β¦ with the first few offenders. The check matches any ${name} left in the output, so a target format that legitimately uses ${β¦} will warn too.
- Pool:
~/Pictures/wallpapers(top level only; jpg/jpeg/png/webp). -
dots-set-wallpaper <file>runsawww imgwith a random transition and records the file at~/.local/state/dots/theme/wallpaper. -
dots-current-wallpaperreads that link (falling back toawww query). - Setting a theme with its own
wallpaper.*overrides the current wallpaper; setting one without leaves it alone.
Source: docs/theming.md