Themes

Theme System Architecture

A modular, compositor-agnostic theming engine with dual-mode support: DIY (static themes) and DMS (dynamic Material You from wallpaper).

Two-Tiered Architecture

Tier 1: Global Defaults (Catppuccin Mocha)

When modules.theme.active = null, the system uses Catppuccin Mocha as the universal fallback. Every app renders correctly and looks polished out of the box.

  • Palette: Catppuccin Mocha (community standard, wide tooling support)
  • Fonts: JetBrains Mono (terminal) + Inter (UI)
  • Terminals: Alpha defaults to 0.85 on all five configurable terminals

Tier 2: Active Theme (Decoration)

When modules.theme.active is set to a theme name, the theme module populates modules.theme.colors and modules.theme.fonts. Apps react to these via mkIf blocks that override the Catppuccin defaults.


Desktop Modes

The theme system supports two operating modes, controlled by modules.desktop.dms.enable / modules.desktop.diy.enable:

ModeBehaviorTheme Source
DIY (modules.desktop.diy.enable = true)Per-app, user-chosen static themesmodules.theme.apps.<app>.theme
DMS (modules.desktop.dms.enable = true)Dynamic Material You from wallpapermatugen (auto-generated)

DIY Mode

Each app’s theme is individually selectable. Terminals default to "matugen", neovim defaults to "catppuccin", rofi defaults to "autumnal".

modules.theme.apps.foot.theme = "catppuccin";   # force catppuccin
modules.theme.apps.rofi.theme = "gemini";        # pick a rofi theme

DMS Mode (Dynamic Material Shell)

All apps are forced to "matugen" — colors are generated from the current wallpaper by the matugen tool. Templates in config/matugen/templates/ render Material You color palettes into per-app config files:

AppTemplateOutput
footfoot.ini~/.config/foot/dank-colors.ini
kittykitty.conf~/.config/kitty/dank-colors.conf
alacrittyalacritty.toml~/.config/alacritty/dank-colors.toml
ghosttyghostty.conf~/.config/ghostty/dank-colors.conf
weztermwezterm.lua~/.config/wezterm/dank-colors.lua
rofirofi.rasi~/.config/rofi/themes/dank-colors.rasi
niriniri.colors.kdl~/.config/niri/dms/colors.kdl
tmuxtmux.conf~/.config/tmux/dank-colors.conf
firefoxfirefox.cssuserChrome.colors.css

Matugen runs at session start (hey.hooks.startup."15-matugen") and on wallpaper changes via DMS’s dynamic theming.

DMS Theme Modes

DMS has two theme source modes, toggled in DMS Settings (Mod+Comma) or via DankDash (Mod+Y):

ModeSourceBackgroundmatugen
Hex modeManually-picked accent colorSolid/gradient from hexGenerates palette from hex
Image modeWallpaper image fileThe actual image displayedExtracts dominant colors from image

In hex mode, changing “wallpaper” only changes the accent color — the background is always a gradient, never an image. Use image mode (select an image file) for actual wallpaper pictures.

DMS Config Locations

PathPurpose
~/.config/DankMaterialShell/settings.jsonGUI settings (theme, layout, transparency, wallpaper fill mode)
~/.config/DankMaterialShell/plugin_settings.jsonPlugin toggles
~/.cache/DankMaterialShell/dms-colors.jsonGenerated Material You palettes (dark + light variants)
~/.cache/DankMaterialShell/cache.jsonFile browser paths, wallpaperLastPath
~/.config/niri/dms/colors.kdlNiri compositor colors (generated by matugen)
~/.config/niri/dms/layout.kdlNiri layout/window rules (managed by DMS)
~/.config/matugen/config.tomlMatugen template mapping (generated by nix)
~/.local/share/hey/info.jsonHost profile info + default wallpaper path (imported by matugen)

Niri DMS Config & Git Skip-Worktree

Why We Need These Files

The NixOS desktop module for the Niri window manager (niri.nix) links and expects the configuration files in config/niri/dms/ (such as colors.kdl, layout.kdl, outputs.kdl) to exist at build time. Without these baseline files present in the repository, a fresh system bootstrap or compile of the Niri desktop configuration will fail due to missing target links.

Why We Do Not Track Modifications

These configuration files are modified dynamically at runtime by DMS (Dynamic Menu System/Dynamic Monitor Setup) and Matugen (the Material You palette generator) to automatically change color schemes, window rules, and monitor layout options. To prevent these local dynamic updates from constantly showing up as unstaged changes in git status or polluting your commits, we ignore changes to them.

How to Skip Worktree (Ignore Local Modifications)

To stop Git from tracking changes to the committed baseline configuration files under config/niri/dms/, use the Git skip-worktree index flag:

# Ignore local changes to the dms configuration files
git update-index --skip-worktree config/niri/dms/*

This ensures the repository retains a working default configuration, but Git will ignore any dynamic modifications made by DMS/Matugen on your active workspace.

Restoring Tracking & Updating Baselines

If you make intentional changes to the default baseline configurations and want to save them back to the repository:

# 1. Temporarily restore tracking:
git update-index --no-skip-worktree config/niri/dms/*

# 2. Add and commit your changes:
git add config/niri/dms/
git commit -m "chore(niri): update baseline dms configurations"

# 3. Mark them as skipped again:
git update-index --skip-worktree config/niri/dms/*

Listing Marked Files

To see which files are currently marked with the skip-worktree flag:

git ls-files -v | grep ^S

Terminal Transparency

All five configurable terminals expose an alpha option controlling background opacity:

TerminalOptionDefaultApplied via
footmodules.desktop.term.foot.alpha0.85[colors-dark] INI section
kittymodules.desktop.term.kitty.alpha0.85background_opacity
alacrittymodules.desktop.term.alacritty.alpha0.85window.opacity
ghosttymodules.desktop.term.ghostty.alpha0.85background-opacity
weztermmodules.desktop.term.wezterm.alpha0.85window_background_opacity
st(hardcoded)0.93Xresources st.alpha

Foot in DMS/matugen mode: the matugen template includes alpha=0.85 directly. In DIY/non-matugen mode, alpha is set via cfg.alpha in the generated [colors-dark] section.

Override per host:

modules.desktop.term.kitty.alpha = 0.78;

Rofi Theming

Rofi is themed differently in each mode:

DIY Mode

The active theme is selected via modules.theme.apps.rofi.theme. Available .rasi theme files live in modules/themes/config/rofi/:

ThemeKey colorsAlpha
autumnaldark warm leavesbg: 0.85, bg-alt: 0.80
geminicatppuccin-inspiredbg: 0.90, bg-alt: 0.60
cyberpunkneon pink/cyanbg: 0.90, bg-alt: 0.80
matrixgreen-on-blackbg: 0.90, bg-alt: 0.80
config.theme.x11catppuccin-like (X11)bg: 0.85, bg-alt: 0.75

DMS Mode

A config.theme.rasi symlink points to @import "themes/dank-colors.rasi" — the matugen-generated Material You palette. The rofi window uses transparency: "real" + background-color: transparent, and container elements carry opacity: 0.85 for the translucent effect.


Integration Levels

System Level

  • GTK & QT (Kvantum): Centralized in modules/theme/default.nix
  • Compositors (Hyprland, Niri): Border, gap, and shadow colors applied via per-WM theme modules
  • Fonts: Unified modules.theme.fonts submodule — mono, sans, terminal aliases consumed by all apps

App Level (Reactive)

  • Terminals: Default to Catppuccin, override to active theme hex values when a theme is active
  • Shell (Zsh): Prompt selectable via modules.theme.apps.zsh.promptstarship, matrix, p10k
  • Tmux: Theme selectable via modules.theme.apps.tmux.theme; status bar transparency defaults to true
  • Rofi: Centralized theme selection with per-mode dispatch
  • Startpage (packages/startpage): Uses Catppuccin design tokens directly in CSS custom properties; light/dark toggle persisted in localStorage

Available Themes

Theme presets are stored as single modular Nix files under modules/themes/presets/ and are automatically loaded at evaluation time:

ThemeFile PathDescription
Catppuccinmodules/themes/presets/catppuccin.nixCommunity standard (Mocha/Latte)
Alucardmodules/themes/presets/alucard.nixDark fantasy palette
Autumnalmodules/themes/presets/autumnal.nixWarm autumn tones

Each theme defines 16 ANSI colors + semantic types (bg, fg, highlight, cursor, border, panelbg, etc.).


Adding a New Theme

  1. Create modules/themes/presets/<name>.nix
  2. Define mkIf (cfg.active == "<name>")
  3. Populate modules.theme.colors (ANSI + Types spec) and modules.theme.fonts
  4. The preset is automatically picked up and loaded by the modular autoloader.
  5. Optionally add a rofi .rasi file in modules/themes/config/rofi/

Roadmap

  1. Live Reloading: Seamless theme transitions without session restart via hey hook reload

Completed Refactoring Wins

  • Standardized Color Submodule: Strictly typed Nix submodules to catch missing color or font keys at evaluation time.
  • Catppuccin-First Strategy: Unified catppuccin standard baseline styling when no active theme is set.
  • Modularized apps.nix: Split the monolithic apps file into highly focused specialized modules under modules/themes/apps/.