# Theme Centralization & Bluetooth Module — Design Date: 2026-06-07 Status: Approved (pending spec review) ## Summary Two independent enhancements to the niri dotfiles: 1. **Theme centralization** — collapse the duplicated color palette into a single source of truth (`theme/colors.json`) and generate all derived configs from it at install time. Bring `hyprlock` under the shared palette. Leave `alacritty` untouched (out of scope). 2. **Bluetooth module** — add a waybar bluetooth indicator using the already-installed `blueman`/`bluez` stack. ## Motivation `CLAUDE.md` mandates a single source of truth and warns against duplicating configs / hardcoding colors in multiple places. Current state violates this: - `theme/colors.css` (`@define-color` palette) and `theme/colors.json` (same palette as JSON) **both** hand-maintain the same 12 colors. Changing a color requires editing both files. - `hyprlock/hyprlock.conf` hardcodes 7 `rgba(...)` colors, every one of which already exists in the palette. Already-correct (not changed by this work): - waybar (`waybar/style.css`) and wofi (`wofi/style.css`) `@import` `colors.css`. - mako config is generated from `colors.json` by `install.sh`. ## Scope **In scope:** unify the two palette files into one source; generate `colors.css`, the mako config, and a hyprlock color file from it; convert `hyprlock.conf` to reference the shared palette; add a waybar bluetooth module. **Out of scope:** alacritty terminal colors (needs a full 16-color normal+bright set the palette does not currently model — deferred); any change to the Tomorrow Night palette values themselves; GTK theming (handled by Materia). ## Approach Chosen approach **A**: `theme/colors.json` is the single source of truth; everything else is generated at install time. This extends the existing Python generation block already in `install.sh` (which generates the mako config). Rejected: parsing `colors.css` as the source (fragile regex), and introducing a new neutral source format (YAGNI for this scope, discards working JSON+Python). ## Detailed Design ### 1. Single source of truth `theme/colors.json` remains the only hand-edited palette file. It holds the 12 base colors (already present). The hand-maintained `theme/colors.css` is **deleted from the repo**; it becomes a generated artifact. ### 2. Generator: `theme/generate-theme.py` A new standalone script, invoked by `install.sh`, replacing the current inline Python mako snippet. Reads `theme/colors.json` and writes three artifacts directly into `~/.config` (written, not symlinked — matching how mako is handled today): - **`~/.config/theme/colors.css`** — `@define-color` block for all 12 base colors, plus the three alpha variants (`tn-bg-a90`, `tn-bg-a95`, `tn-bg-a96`) computed from the `background` color at opacities 0.90 / 0.95 / 0.96. This is what waybar and wofi already `@import`; their files do **not** change. - **`~/.config/mako/config`** — same output as the current inline snippet. - **`~/.config/theme/hyprlock-colors.conf`** — hyprlang variable declarations, one per needed color, e.g. `$tn_fg = rgba(c5c8c6ff)`. Hex from `colors.json` is converted to hyprlock's `rgba(RRGGBBAA)` form with `AA = ff`. The generator must be idempotent and safe to re-run (overwrites its outputs). ### 3. hyprlock.conf - Add `source = ~/.config/theme/hyprlock-colors.conf` near the top. - Replace the 7 hardcoded `rgba(...)` values with the matching `$tn_*` variables. Color → variable mapping (from current values): - `rgba(c5c8c6ff)` → `$tn_fg` (foreground) - `rgba(969896ff)` → `$tn_fg_dim` (foreground_dim) - `rgba(282a2eff)` → `$tn_bg_alt` (background_alt) - `rgba(81a2beff)` → `$tn_blue` (blue) - `rgba(cc6666ff)` → `$tn_red` (red) - `rgba(b5bd68ff)` → `$tn_green` (green) - `hyprlock.conf` stays a hand-edited, symlinked file; only its colors come from the generated source file. **Risk + fallback:** hyprlock v0.9.5 uses the hyprlang parser, which documents support for `$variables` and `source=`. This is verified by documentation, not empirically (no dry-run flag; launching hyprlock grabs the screen). The implementation plan MUST include an explicit parse test (lock once and confirm colors render, or launch with `--grace 0` and watch `--verbose` for parse errors). **Fallback if `source=`/variables misbehave:** generate the entire `hyprlock.conf` from a `hyprlock/hyprlock.conf.tmpl` template with color placeholders, instead of using `source=`. Same single-source outcome, more generation. ### 4. install.sh changes - Remove the inline `python3 - <<'PYEOF' ... PYEOF` mako block. - Add a call to `python3 theme/generate-theme.py` after configs are linked. - Remove the `ln -sf .../theme/colors.css ~/.config/theme/colors.css` line (colors.css is now generated into that path, not symlinked). - Ensure `~/.config/theme` exists before generation (already created). ### 5. Bluetooth waybar module - `waybar/config.jsonc`: add `"bluetooth"` to `modules-right` (placed next to `network`), with a `bluetooth` module block: - `format`, `format-connected`, `format-disabled` using Nerd Font glyphs. - `on-click`: `blueman-manager`. - `waybar/style.css`: add a `#bluetooth` padding rule (consistent with the other right-side modules) plus a connected-state accent color using a palette variable (e.g. `@tn-blue`). - Dependencies: `blueman` + `bluez`/`bluez-utils` (blueman pulls bluez). Confirm `packages.txt` lists what's needed; the waybar bluetooth module requires the `bluez` daemon running. ## Testing / Verification - **Generator:** run `python3 theme/generate-theme.py`; diff generated `~/.config/theme/colors.css` against the previous hand-written file to confirm identical palette + alpha variants. Confirm mako config output unchanged. - **waybar/wofi:** restart waybar (`waybar-restart`), confirm no CSS errors and colors render. Open wofi, confirm styling intact. - **hyprlock:** parse test per the fallback note above; confirm lock screen colors match before/after. - **Bluetooth:** confirm module appears, reflects adapter state, and `blueman-manager` opens on click. - **Idempotency:** re-run `install.sh`; confirm no errors and stable output. ## Rollout Notes - Generated files (`~/.config/theme/colors.css`, `~/.config/theme/hyprlock-colors.conf`) are not committed and not symlinked — consistent with the existing mako generation. The committed `theme/colors.css` is removed in this change. - The two features (theme, bluetooth) are independent and can be implemented and verified separately.