aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/specs/2026-09-11-unified-desktop-theme-design.md200
1 files changed, 200 insertions, 0 deletions
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. <danix@danix.xyz>`, 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.