From 3bfe81ea6ec7779558c08e08755893ca700a83e5 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 11 Sep 2026 09:03:06 +0200 Subject: docs: add unified desktop theme design spec Catppuccin Macchiato base with a pywal-driven accent snapped to the nearest named Macchiato accent. Phase 1 scope is rofi: collapse five theme systems into three shared layouts over one palette, covering eleven call sites. Records the four brainstorming decisions and the alternatives rejected, so later phases (waybar, dunst, kitty, conky, hyprland, quickshell) reuse the mechanism rather than relitigate it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01G6aeE37K4GHBsaM51yLTTq --- .../2026-09-11-unified-desktop-theme-design.md | 200 +++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md diff --git a/docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md b/docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md new file mode 100644 index 0000000..8db59e5 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md @@ -0,0 +1,200 @@ +# Unified Desktop Theme, Design + +Date: 2026-09-11 +Status: approved, phase 1 not yet implemented + +## Goal + +One consistent visual identity across the desktop: Catppuccin Macchiato as the +fixed base, Noto Sans as the UI font, Inconsolata Nerd Font Mono as the +monospace font. The accent color follows the wallpaper through pywal, but is +always snapped to a real Macchiato accent so the result is never unreadable. + +Phase 1 covers rofi only. Later phases extend the same mechanism to waybar, +dunst, kitty, conky, hyprland and quickshell. + +## Problem + +The rofi setup has grown five independent theme systems with three different +color sources and three different fonts: + +| Theme system | Color source | Used by | +| --- | --- | --- | +| `darknix/` | pywal (`~/.cache/wal/darknix-colors.rasi`) | appsmenu, emoji, notes, qemu, scrotmenu | +| `launchers/type-2/style-1` (adi1090x) | `colors/catppuccin.rasi` | sshmenu, github-repos, hypr-windows, cliphist | +| `launchers/type-1` (adi1090x) | `colors/catppuccin.rasi` | ALT+F2 launcher | +| `powermenu/type-4` (adi1090x) | `colors/catppuccin.rasi` | Mod+x powermenu | +| `elegantVagrant/` | self-contained | rofipass | +| (none) | rofi defaults | ddgr_search.py | + +Fonts in play: `JetBrains Mono Nerd Font 10` (adi1090x shared/fonts.rasi), +`Mono 12` (rofi config.rasi), plus whatever each standalone theme sets. Neither +matches the Qt/GTK setting, which is already Noto Sans + Inconsolata Nerd Font +Mono. + +The `colors/catppuccin.rasi` shipped by adi1090x is not Macchiato. Its +background is `#1E1D2F` and its `selected` is `#7AA2F7`, a Tokyo Night blue. + +## Decisions + +Four decisions were made during brainstorming, each with alternatives +considered: + +1. **Color model: Macchiato base with a pywal-driven accent.** Structural + colors (backgrounds, text) are fixed Macchiato. Exactly one color, the + accent, follows the wallpaper. Rejected: fully fixed palette (loses the + wallpaper tie-in, and the pywal pipeline already exists); fully + pywal-driven (readability not guaranteed). + +2. **Accent selection: snap to the nearest Macchiato accent.** pywal's dominant + color is matched by hue against the 14 named Macchiato accents and the + closest one wins. Rejected: raw pywal color (can be muddy or dark against + the base); raw color with a contrast floor (keeps more wallpaper fidelity + but can emit colors outside the palette). + +3. **Scope: rofi only in phase 1.** Rofi holds the actual inconsistency and + exercises every part of the pipeline. Once proven, each further app is a + single template file. + +4. **Layouts: three shapes, one palette.** The scripts genuinely have three + different jobs and forcing one shape on all of them would push the variation + back into scattered `-theme-str` strings. Revisit if three proves wrong. + +## Architecture + +``` +~/.config/wal/templates/udt-accent.rasi # pywal template, emits raw dominant color +~/.config/rofi/udt/ + palette.rasi # Macchiato, fixed, hand-written + accent.rasi # symlink -> ~/.cache/wal/udt-accent.rasi (generated) + common.rasi # fonts, radii, spacing, element states; imports palette + accent + launcher.rasi # grid + search + menu.rasi # small, fixed options + list.rasi # tall searchable list +~/bin/udt-accent # snaps pywal dominant color to nearest Macchiato accent +``` + +Data flow on wallpaper change: + +1. pywal renders `udt-accent.rasi` into `~/.cache/wal/` with the raw dominant + color. +2. `udt-accent` reads it, snaps to the nearest Macchiato accent, rewrites the + file in place. +3. Next rofi invocation picks it up. Rofi reads its theme per launch, so no + daemon and no reload are needed. + +Step 3 is why this needs no running process. `udt-accent` is called from +`wal.sh`, which already performs the symlink-and-restart sequence for dunst and +kitty. + +## Color contract + +`palette.rasi` defines the full Macchiato palette by semantic name. Values are +from the official `catppuccin/palette` repository: + +``` +base #24273a mantle #1e2030 crust #181926 +text #cad3f5 subtext1 #b8c0e0 subtext0 #a5adcb +overlay2 #939ab7 overlay1 #8087a2 overlay0 #6e738d +surface2 #5b6078 surface1 #494d64 surface0 #363a4f + +rosewater #f4dbd6 flamingo #f0c6c6 pink #f5bde6 mauve #c6a0f6 +red #ed8796 maroon #ee99a0 peach #f5a97f yellow #eed49f +green #a6da95 teal #8bd5ca sky #91d7e3 sapphire #7dc4e4 +blue #8aadf4 lavender #b7bdf8 +``` + +`accent.rasi` defines exactly one variable, `@accent`, always equal to one of +the 14 accent colors above. + +Themes reference semantic names only, never literal hex. Everything structural +is fixed; only `@accent` moves. + +## The accent snapper + +`~/bin/udt-accent`, Python 3, standard library only (`colorsys`, `re`). + +- Reads the raw color from `~/.cache/wal/udt-accent.rasi`. +- Converts it and the 14 candidate accents to HLS. +- Picks the candidate with the smallest circular hue distance. +- Writes `* { accent: #rrggbb; }` back to the same path. +- Falls back to `mauve` when the cache file is missing or unparseable, so a + fresh machine or a failed pywal run still yields a working theme. + +Hue alone is the comparison metric: the Macchiato accents are already +normalized for lightness and saturation against the base, so matching hue is +what selects the perceptually right one. A near-grey wallpaper color has an +unstable hue, so colors below a small saturation threshold fall back to `mauve` +rather than snapping arbitrarily. + +Leaves one runnable self-check (`udt-accent --selftest`) asserting that a known +orange input snaps to `peach`, a known green to `green`, and a grey to the +`mauve` fallback. + +## Layouts + +All three import `common.rasi`, which holds the shared identity: Noto Sans for +UI text, Inconsolata Nerd Font Mono for monospace fields, one border radius, +one border width, and one `element.selected` treatment (accent background, +`@base` text). + +- **`launcher.rasi`**: centered, search bar, icon grid. App launching. +- **`menu.rasi`**: small centered box, no search, few fixed options. +- **`list.rasi`**: tall, search bar, single wide column. Long searchable lists. + +## Migration + +Eleven targets. Each gets its `-theme` argument pointed at one of the three new +files; `ddgr_search.py` has no `-theme` today and gains one. + +| Layout | Targets | +| --- | --- | +| `launcher.rasi` | `blackpearl-appsmenu.sh`, `launchers/type-1/launcher.sh` (ALT+F2) | +| `menu.rasi` | `qar-scrotmenu.sh`, `blackpearl-notes.sh`, `powermenu/type-4/powermenu.sh` (Mod+x) | +| `list.rasi` | `blackpearl-sshmenu.sh`, `blackpearl-emoji.sh`, `rofi-qemu.sh`, `github-repos.sh`, `hypr-windows.sh`, `rofipass`, `ddgr_search.py`, cliphist bind | + +The cliphist binding carries its `-theme` inline in +`~/.config/hypr/sections/keybindings.lua` rather than in a script, so that line +is edited directly. + +`launchers/type-1` and `powermenu/type-4` are third-party adi1090x scripts. +They are treated like any other target, a `-theme` path change only, with no +attempt to restructure them. + +The old theme directories (`darknix/`, `elegantVagrant/`, `launchers/`, +`applets/`, `powermenu/`) stay on disk untouched. Deleting them is a separate +decision to be made after the new themes are confirmed good. + +Out of scope: `ronema` (rofi NetworkManager applet, no longer used, pending +archival by the user). + +## Verification + +Visual, performed by the user. Each of the eleven targets is launched and +eyeballed against three checks: colors are Macchiato, fonts are Noto Sans and +Inconsolata, and the shape suits the job. + +The accent pipeline is verified separately by changing the wallpaper to images +with clearly different dominant hues and confirming the accent tracks and stays +readable. + +`udt-accent --selftest` covers the snapping logic non-visually. + +## Later phases + +Each subsequent app reuses `palette.rasi` and `accent.rasi` through a +format-appropriate template, and needs no new mechanism: + +- **waybar**: CSS, consumes a generated `colors.css`. +- **dunst**: already pywal-templated, template gets repointed. +- **kitty**: already pywal-templated, same. +- **conky**: Lua config, reads generated values. +- **hyprland**: window borders use the accent. +- **quickshell**: new work, to be designed when the user starts on it. + +## License + +GPLv2 (to be confirmed). Needs `LICENSE`, per-file header notices with +`Copyright (C) 2026 Danilo M. `, and a License section in the +README, added early rather than retrofitted. The README also needs the standard +Development Approach section disclosing AI-assisted development. -- cgit v1.2.3