# swaync — notification daemon + control centre, themed from # state.json. Until this shipped, nothing rendered notify-send at # all: the theme-switch progress toasts, the CLI's font warnings and the # live ISO's welcome message were all invisible. { config, lib, ... }: let cfg = config.nomarchy; t = cfg.theme; c = t.colors; r = toString t.ui.rounding; in { config = lib.mkIf cfg.swaync.enable { services.swaync = { enable = true; settings = { positionX = "right"; positionY = "top"; control-center-margin-top = t.ui.gapsOut; control-center-margin-right = t.ui.gapsOut; control-center-width = 420; notification-window-width = 400; notification-icon-size = 48; timeout = 6; timeout-low = 3; timeout-critical = 0; # critical stays until dismissed }; style = '' /* Palette baked from state.json. Only roles guaranteed to contrast @base in EVERY palette are used: subtext/surface mean "on-surface" in some light themes (summer-day: subtext==base, surface==text — body text was invisible on hardware, item 25). Chips/hovers are alpha(@text) tints — contrast by construction. tools/check-theme-contrast.py guards the hex-on-hex pairings. */ @define-color base ${c.base}; @define-color text ${c.text}; @define-color accent ${c.accent}; @define-color bad ${c.bad}; .notification { background: alpha(@base, 0.95); border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4); border-radius: ${r}px; color: @text; font-family: "${t.fonts.ui}", "${t.fonts.mono}"; font-size: ${toString t.fonts.size}pt; } .notification-content .summary { color: @text; font-weight: bold; } .notification-content .body { color: @text; } .notification.critical { border-color: @bad; } .control-center { background: alpha(@base, 0.95); border: ${toString t.ui.borderSize}px solid alpha(@accent, 0.4); border-radius: ${r}px; color: @text; font-family: "${t.fonts.ui}", "${t.fonts.mono}"; font-size: ${toString t.fonts.size}pt; } /* Unscoped on purpose: the floating popup wraps .notification in a .notification-row too, and swaync's DEFAULT stylesheet (still loaded underneath this one) paints that row dark on hover — on light themes the theme's dark @text then sat on the default's dark hover, unreadable. The same alpha(@text) tint as everywhere else overrides it in both surfaces. */ .notification-row:focus, .notification-row:hover { background: alpha(@text, 0.1); border-radius: ${r}px; } /* The REAL popup-hover culprit: the notification body is one big GTK button (.notification-default-action), and the default sheet hovers IT to rgb(56,56,56) — on light themes that put dark @text on a dark chip (unreadable, hardware report 2026-07-18). Re-pin body + action buttons to the palette's tint construction; hover stays a tint of @text, so it works in both modes by construction. */ .notification-default-action, .notification-action { background: transparent; color: @text; } .notification-default-action:hover, .notification-action:hover { background: alpha(@text, 0.08); } /* Same default-stylesheet leak: swaync ships a dark close-button chip; re-pin both ends to the palette's tint construction. */ .close-button { background: alpha(@text, 0.1); color: @text; border-radius: ${r}px; } .close-button:hover { background: alpha(@text, 0.2); color: @text; } .widget-title { color: @text; font-weight: bold; } .widget-title > button { background: alpha(@text, 0.1); color: @text; border: none; border-radius: ${r}px; } .widget-title > button:hover { background: @accent; color: @base; } ''; }; }; }