13 KiB
Context
The user's setup is Hyprland on jpporta-nixos with swaync as the notification daemon. The current swaync configuration is split into two parts:
-
modules/home-manager/swaync/default.nix— the only place where swaync is configured. It inlines a ~400-linestyle.cssstring with hardcoded gruvbox colors (#282828,#ebdbb2,#83a598,#fb4934,#504945,#fabd2f) and a flat, square-ish layout. The settings block setspositionX = "right",positionY = "top", and a 360px-wide notification column with no corner radius beyond 14px. -
modules/home-manager/hyprland/default.nix(lines 107–116) — already adds blur layers forswaync-notification-windowandswaync-control-center, so the current design intends to be glassy but the CSS doesn't lean into it (noalpha(), low border-radius, no urgency-based color).
The user wants:
- A new "Groovebox" theme: dark, colorful, glassy.
- iOS-style notification cards: rounded, icon left, title + truncated body right, timestamp in small caps top-right, urgency-coded title color + border accent.
- Notification tray (the persistent control center) in the same dark glassy palette.
- Notifications pop from the bottom-left instead of top-right.
- A small
ponytail:mindset: shipment is one new theme (groovebox) wired through the new layout, not 11 themes. Other themes get ported later.
The archived 2026-07-13-theme-switch change designed the layout (~/.config/colorschemes/<theme>/<app>/ + .active symlink + out-of-store symlinks from the Nix module) but was never implemented for swaync. This change ships the layout for swaync, with Groovebox as the first concrete theme so the user can see the result immediately.
Constraints:
- Nix home-manager on NixOS — the live reload path is
swaync-client --reload-css, not a rebuild. The module must not bake colors. - The active theme directory is expected to be reachable via
~/.config/colorschemes/.active(a symlink set by the futuretheme-switchscript). Since that script doesn't exist yet, this change placesgrooveboxat~/.config/colorschemes/groovebox/and the Nix module points at.active— first-time setup also requiresln -sfn ~/.config/colorschemes/groovebox ~/.config/colorschemes/.active(documented in the change notes). - The
~/.config/colorschemes/<theme>/directory is currently empty. There is notheme-switchscript yet. This change ships the dotfiles for one theme and expects the user (or a follow-up change) to wire.active.
Goals / Non-Goals
Goals:
- iOS-style notification cards: large border-radius (16–20px), transparent dark background with alpha + blur, horizontal layout (icon left, summary + body right), small-caps timestamp in the top-right corner.
- Urgency-colored title and border:
low= cyan,normal= white,critical= red. Applies in both the floating notification and the control center. - Control center (notification tray) in the same glassy dark palette, larger border-radius (24px), transparent background, bottom-left position.
- Notifications pop from the bottom-left (
positionX = "left",positionY = "bottom"). - Swaync config + stylesheet live in
~/.config/colorschemes/groovebox/swaync/as dotfiles, not in the Nix store. The Nix module is a thin wrapper thatxdg.configFiles out-of-store symlinks. swaync-client --reload-csscontinues to be the live-reload path; no rebuild required for tweaks.
Non-Goals:
- The
theme-switchscript itself, thetheme-picker, the rofi integration, the boot-timetheme-apply.service, the wallpaper hook. Those are the next change. - Porting the existing 14 themes (
gruvbox-dark,catppuccin-mocha-dark, etc.) into the new layout. Justgrooveboxships here. - gsettings / darkman integration changes.
- Hyprland blur rules: the existing
swaync-notification-window+swaync-control-centerblur rules already target the right namespaces; they continue to work. - Changing the
font-family("BerkeleyMono Nerd Font Propo") or base font size — those stay. - New external dependencies. No new packages.
- The CSS
all: unsetreset on*is kept from the current style (it works fine with swaync's GTK widgets and avoids inherited GTK theme leaks).
Decisions
D1. Two-file split: style.css (layout) + colors.css (palette)
The current Nix inline style.css and the thin colors.css from the theme-switch seeds get separate files. Why: colors are swapped per theme; layout is shared across all future themes. Other future themes can cp groovebox/style.css and just rewrite colors.css. Alternative considered: one file, with the colors inlined as CSS variables — fine, but the seeds archive already used a two-file split, so this stays consistent with the established convention.
D2. Layout values live in style.css, palette lives in colors.css
style.css references palette via CSS custom properties: --accent-critical, --accent-normal, --accent-low, --bg, --bg-alt, --text, --hover, --border. colors.css defines them. Why: keeps the layout file theme-agnostic so other themes can reuse it. Alternative: put color literals in style.css — tied to one theme, defeats the point.
D3. Out-of-store symlink for both files (the archived theme-switch pattern)
xdg.configFile."swaync/style.css".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.config/colorschemes/.active/swaync/style.css"; and the same for colors.css. Why: the user's home dir is the source of truth. The Nix store never has the colors, so live edits + swaync-client --reload-css work without rebuild. Alternative: text = builtins.readFile ... — same effect for static configs, but breaks the moment the user edits the file: home-manager would silently overwrite on the next activation. The symlink is correct.
D4. Active theme pointer is the user's responsibility (for this change)
The Nix module points at ~/.config/colorschemes/.active/swaync/. If .active is missing, swaync ends up with a broken symlink and hyprland's swaync exec (hl.exec_cmd("swaync")) still starts the daemon, but the CSS fails to load. Two options considered:
- Fallback: ship a default symlink in the Nix module (
xdg.configFile."swaync/style.css".source = ... "${current}/style.css"withcurrent = "groovebox"if.activeis missing). Cleaner first-run UX, but adds a Nix string that says "groovebox" — exactly the kind of hardcoding this change is trying to remove. Rejected. - Documented one-liner on first install. First-run setup runs
mkdir -p ~/.config/colorschemes && ln -sfn ~/.config/colorschemes/groovebox ~/.config/colorschemes/.active && swaync-client --reload-css. This is fine because it's a one-time setup, and the same command is whattheme-switchwill eventually own. Chosen.
The change notes in tasks.md will spell out the first-run command.
D5. Urgency → color via class selectors, not via inline style=
Swaync adds CSS classes .low, .normal, .critical on the .notification element. The CSS reads:
.notification.low .summary { color: var(--accent-low); }
.notification.critical .summary { color: var(--accent-critical); }
.notification.critical .notification-background { border-color: var(--accent-critical); }
Why: swaync owns the class assignment; CSS owns the color. Theme swaps via colors.css alone. Alternative considered: inline style (e.g. style="border-color: ...") — not supported by swaync's markup on the notification wrapper. Classes are the right path.
D6. Timestamp displayed via the .time label, top-right
Swaync renders a .time label inside the notification header by default. The CSS positions it in the top-right corner of the notification card, small caps, dimmed (color: alpha(var(--text), 0.55)). Why: matches iOS, and the existing widget-config.label.max-lines = 1 already constrains the body width so it doesn't compete with the timestamp.
D7. Control center border-radius 24px; notification cards 16px
iOS uses a large radius on the full tray and a slightly smaller radius on individual cards. 16px gives a "tile" feel; 24px on the tray gives a "floating surface" feel. Why: matches iOS reference aesthetics. Alternative: uniform 16px — looks flat and grid-like, not iOS.
D8. transition-time bumped to 250ms; transition CSS line kept
Swaync's transition-time setting controls the slide-in animation. iOS-style feels good at 200–300ms. We bump to 250ms. The existing CSS transition: 200ms inside * is a no-op (swaync uses transitions on actual property changes, not on all). We keep it for parity.
D9. Notification close button + timestamp share the top-right corner
The current CSS puts the close button at the top-right. The new layout puts the close button to the right of the timestamp (still top-right, but the timestamp is the inner-most element). iOS shows the app icon on the trailing edge of the header; we use a close × button instead, since swaync doesn't auto-inject a close for floating notifications.
D10. Color palette: invented Groovebox palette
| Token | Hex | Role |
|---|---|---|
--bg |
#14171c |
deepest background |
--bg-alt |
#1a1d24 |
notification card / widget tile |
--bg-glass |
rgba(20, 23, 28, 0.72) |
card body with alpha |
--text |
#e6e6e6 |
main foreground |
--text-dim |
rgba(230, 230, 230, 0.55) |
timestamp, secondary |
--hover |
#25282f |
hover highlight |
--accent-low |
#5cd6ff |
low urgency (cool, calm) |
--accent-normal |
#e6e6e6 |
normal urgency (white-ish, matches iOS) |
--accent-critical |
#ff5e7a |
critical urgency (loud red) |
Why: dark base, vibrant accents, follows the "colorful but dark" Groovebox vibe. The user can edit colors.css later to taste. Alternative: reuse gruvbox-dark's palette — defeats the point of a new theme.
Risks / Trade-offs
-
First-run gotcha:
.activesymlink doesn't exist yet. → Documented intasks.mdand in the change notes. The Nix module fails gracefully onhome-manager switch(the symlink target just doesn't exist yet), so the user runs the one-liner manually. → Mitigation: thetheme-switchchange will own this; for now, oneln -sfnis acceptable. -
CSS
all: unsetreset is aggressive and may break third-party widget styles. Current code already has it; we keep it. Users who want fancier widget styling can override per-widget. → Mitigation: none needed (pre-existing behavior). -
transition-time = 250msmay feel sluggish on slow hardware. → Mitigation: documented as a tunable value incolors.csscomments; the user can dial it back via~/.config/swaync/config.json(rebuilt by home-manager, but the change is small). -
background: alpha(...)for the glass effect depends on the compositor passing blur through. If hyprland's blur rules drift, the glass effect disappears. → Mitigation: the existingswaync-notification-windowandswaync-control-centerblur rules inmodules/home-manager/hyprland/default.nixare untouched and still apply. If the user disables blur in hyprland, the card still has alpha + dark background, so it doesn't become invisible. -
The
style.cssshell assumes class names.low,.normal,.criticalsurvive swaync upgrades. Swaync has used these classes for years; the API is stable. → Mitigation: if a future swaync release renames a class, the symptom is "urgency colors stop working" — visible immediately, fixable in one CSS line. -
Out-of-store symlinks blow away if the user
rm -rf ~/.config/colorschemes/. → Mitigation: the.activesymlink and theme dirs are dotfiles, versioned indotfiles/(already the convention for swaync-alacritty-etc). Agit pullin~/dotfilesrestores them. -
One theme shipped, not 14. → Mitigation: the layout + colors split + symlink infra is now in place; porting the other themes is a follow-up cost (~10 minutes per theme:
cp -r groovebox/seeds/<theme>, editcolors.css). Documented in the change notes as expected follow-up.
Migration Plan
- Apply the change:
home-manager switch --flake .#jpporta-nixos. - First-run setup (one-time, also documented in
tasks.md):mkdir -p ~/.config/colorschemes ln -sfn ~/.config/colorschemes/groovebox ~/.config/colorschemes/.active swaync-client --reload-css - Verify visually: trigger a test notification with
gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.Notify swaync-test 0 '' Hello Groovebox "Test" '[]' '{}' 4000and confirm the card appears bottom-left with the new look. - Rollback: revert to the previous Nix module (the old one had the inline
style.css). The dotfiles in~/.config/colorschemes/groovebox/are tracked separately and don't affect rollback.
Open Questions
- None blocking. The Groovebox palette is invented; the user can tweak
colors.cssafter the first run. - The
theme-switchscript is the natural follow-up change and will own.activesymlink management from there.