# Stylix — themes the long tail of applications (GTK, Qt, cursors, # fonts) from the same theme-state.json that drives the live engine. # # Division of labour: the hot-reload trio (Hyprland, Waybar, Ghostty) # is owned by the Nomarchy engine and updates instantly; everything # Stylix touches updates on the next home-manager switch. That is why # autoEnable is off and the trio's Stylix targets stay disabled. # # Note: the stylix home module itself is imported by homeModules.nomarchy # in flake.nix (it needs the stylix flake input). { config, lib, pkgs, ... }: let cfg = config.nomarchy; t = cfg.theme; c = t.colors; hex = lib.removePrefix "#"; # Map the Nomarchy palette onto base16 roles. base16 = { base00 = hex c.base; # default background base01 = hex c.mantle; # darker background (status bars) base02 = hex c.surface; # selection background base03 = hex c.muted; # comments base04 = hex c.subtext; # dark foreground base05 = hex c.text; # default foreground base06 = hex c.text; # light foreground base07 = hex c.text; # lightest foreground base08 = hex c.bad; # red base09 = hex c.warn; # orange base0A = hex c.warn; # yellow base0B = hex c.good; # green base0C = hex (builtins.elemAt t.ansi 6); # cyan base0D = hex c.accent; # blue base0E = hex c.accentAlt;# magenta base0F = hex c.bad; # brown/deprecated }; # Submenu arrow, bundled: adw-gtk3 (and our earlier fix) look up # pan-end-symbolic in the ICON theme, but Papirus doesn't ship it and # its inheritance chain (breeze-dark → hicolor) is broken here — so # menu arrows rendered as nothing (nm-applet: no hint that "VPN # Connections" opens a submenu). Same trick adw-gtk3 uses for its # check marks: a bundled asset via -gtk-recolor(url(…)), immune to # icon-pack coverage. Shape traced from Adwaita's pan-end-symbolic. panEndSvg = pkgs.writeText "nomarchy-pan-end.svg" '' ''; panEndHoverSvg = pkgs.writeText "nomarchy-pan-end-hover.svg" '' ''; in { config = lib.mkIf cfg.stylix.enable { stylix = { enable = true; autoEnable = false; # explicit targets only — the engine owns the rest polarity = if t.mode == "light" then "light" else "dark"; base16Scheme = base16; targets = { gtk = { enable = true; # nm-applet / classic GTK menus: Stylix base16 selection can # collapse into surface/muted on dark themes (Boreal: selected # row and submenu arrows were effectively invisible). High- # contrast selected rows + explicit arrow colour. Appended to # Stylix's generated gtk.css (not gtk.gtk3.extraCss — that is a # no-op under Stylix). extraCss = '' menu menuitem, .menu menuitem, .context-menu menuitem { color: #${base16.base05}; } menu menuitem:hover, menu menuitem:selected, .menu menuitem:hover, .menu menuitem:selected, .context-menu menuitem:hover, .context-menu menuitem:selected, menubar > menuitem:hover { background-color: #${base16.base0D}; color: #${base16.base00}; } menu menuitem:disabled, .menu menuitem:disabled { color: #${base16.base03}; } /* Submenu disclosure arrows — bundled assets, NOT an icon-theme lookup: pan-end-symbolic is missing from Papirus and its broken inheritance chain, so -gtk-icontheme() drew nothing. Hard-tinted directly from the palette to bypass GTK's fragile -gtk-recolor behavior. */ menu menuitem arrow, .menu menuitem arrow, menu menuitem:hover arrow, menu menuitem:selected arrow { min-width: 16px; min-height: 16px; margin-left: 8px; color: inherit; } menu menuitem arrow, .menu menuitem arrow { -gtk-icon-source: url("file://${panEndSvg}"); } menu menuitem:hover arrow, menu menuitem:selected arrow { color: #${base16.base00}; -gtk-icon-source: url("file://${panEndHoverSvg}"); } check, radio { min-width: 14px; min-height: 14px; color: inherit; } /* GTK4 / popover menus (some portals). */ popover.menu contents modelbutton { color: #${base16.base05}; } popover.menu contents modelbutton:hover, popover.menu contents modelbutton:selected { background-color: #${base16.base0D}; color: #${base16.base00}; } ''; }; qt.enable = true; # No-op unless programs.zathura is on (viewers.nix enables it). zathura.enable = true; }; cursor = { name = lib.mkDefault "Bibata-Modern-Classic"; package = lib.mkDefault pkgs.bibata-cursors; size = lib.mkDefault 24; }; # GTK/file-manager/rofi icon theme, resolved from the JSON in # theme.nix (per-theme `icons`, else Papirus-Dark/Light by mode). # Stylix sets gtk.iconTheme from this; both dark/light point at the # already-mode-resolved name. The package is papirus by default and # only unions in another pack when a theme's `icons` names one # (theme.nix — opt-in, no default closure bloat). icons = { enable = true; package = lib.mkDefault t.iconThemePackage; dark = t.iconTheme; light = t.iconTheme; }; fonts = { monospace = { name = t.fonts.mono; package = lib.mkDefault pkgs.nerd-fonts.jetbrains-mono; }; sansSerif = { name = t.fonts.ui; package = lib.mkDefault pkgs.inter; }; serif = { name = t.fonts.ui; package = lib.mkDefault pkgs.inter; }; emoji = { name = "Noto Color Emoji"; package = lib.mkDefault pkgs.noto-fonts-color-emoji; }; sizes.terminal = t.fonts.size; }; }; # GTK4/libadwaita and Qt6 apps decide dark vs light from the XDG # portal's color-scheme (org.freedesktop.appearance), which # xdg-desktop-portal-gtk sources from this gsettings key. Stylix sets # the GTK theme and `polarity` but not this, so a light theme still # rendered libadwaita/Qt apps dark (and vice-versa). Drive it from the # palette mode. (programs.dconf.enable is already on system-side.) dconf.settings."org/gnome/desktop/interface".color-scheme = if t.mode == "light" then "prefer-light" else "prefer-dark"; }; }