Files
dotfiles/docs/superpowers/specs/2026-06-07-theme-centralization-and-bluetooth-design.md
funman300 1e3adbc842 feat: centralize theme palette + add waybar bluetooth module
Theme centralization:
- theme/colors.json is now the single source of truth for the palette.
- New theme/generate-theme.py renders, at install time, the three derived
  artifacts into ~/.config: colors.css (waybar/wofi @import), mako config,
  and hyprlock-colors.conf (hyprlang $tn_* variables).
- theme/colors.css removed from the repo (now generated); install.sh calls
  the generator instead of the old inline mako snippet.
- hyprlock.conf sources the generated color file and uses $tn_* variables
  instead of hardcoded rgba() values.
- Generator covered by theme/test_generate_theme.py.

Bluetooth:
- Added waybar built-in bluetooth module (config.jsonc + style.css), with
  bluez/bluez-utils in packages.txt.

Includes design spec and implementation plan under docs/superpowers/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:45:01 -07:00

142 lines
6.5 KiB
Markdown

# 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.