All checks were successful
Check / eval (push) Successful in 3m54s
e9dd3d1 pinned dialog button labels to base00 for dark palettes. That is
correct for suggested-action, whose background adw-gtk3 builds opaquely as
mix(white,@accent_bg_color,0.9) — but destructive-action is the one class
whose background is derived from currentColor:
background-color: mix(@destructive_color, alpha(currentColor, 0.15), 0.9)
so pinning the label dragged the background down with it. On Boreal the
"Delete" button of a GtkMessageDialog rendered #21272F on #252931 —
1.03:1, invisible — and light palettes hit the same 1.03:1. The most
safety-critical button in a confirmation dialog was the one that vanished.
Pin the destructive background to the same opaque pastel ladder
suggested-action uses. Enabled-only (:not(.flat):not(:disabled)): the
sheet's disabled chrome already renders legibly and must keep it.
Boreal 1.03 -> 6.19:1, summer-day 1.03 -> 2.74:1 (= upstream's
white-on-accent convention; the residual light-mode weakness is #130).
V2: GtkMessageDialog rendered under the real HM session and viewed —
boreal before/after, summer-night (whole-swap), summer-day (light).
Scripted checks stayed green throughout and never saw this; only the
render did. Follow-ups #129 (permanent guard) and #130 filed.
236 lines
10 KiB
Nix
236 lines
10 KiB
Nix
# Stylix — themes the long tail of applications (GTK, Qt, cursors,
|
|
# fonts) from the same state.json that drives the live engine.
|
|
#
|
|
# Division of labour: the hot-reload trio (Hyprland, Waybar, Kitty)
|
|
# 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
|
|
};
|
|
|
|
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).
|
|
#
|
|
# #98: dialog button labels. Stylix always installs theme name
|
|
# "adw-gtk3" (light sheet) and only recolors @define-color vars.
|
|
# The light sheet hardcodes dark/black button text in places; on a
|
|
# dark palette that becomes black-on-dark in GTK dialogs. We force
|
|
# adw-gtk3-dark for dark mode (below) and still pin button label
|
|
# colours here so dialogs stay legible if a light sheet slips in.
|
|
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 need no icon remap: pan-end-symbolic
|
|
resolves via Papirus-Dark's breeze-dark inheritance. The
|
|
"invisible arrow" bug was Waybar's process-wide stylesheet:
|
|
its `* { min-height: 0; }` reset also hits the SNI tray menus
|
|
Waybar hosts and collapses arrow/check nodes. NB: this file
|
|
CANNOT fix that — Waybar adds its provider at USER priority
|
|
too and wins the tie in practice (GTK 3.24, observed), so
|
|
every waybar.css carries its own menu counter-rules. The
|
|
rules here cover menus in ordinary GTK apps only. */
|
|
menu menuitem arrow, .menu menuitem arrow, .context-menu menuitem arrow {
|
|
min-width: 16px;
|
|
min-height: 16px;
|
|
}
|
|
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};
|
|
}
|
|
|
|
/* Dialog / message-box action buttons (#98). */
|
|
button, button label,
|
|
.dialog-action-area button,
|
|
.dialog-action-area button label,
|
|
messagedialog button,
|
|
messagedialog button label,
|
|
.message-dialog button,
|
|
.message-dialog button label {
|
|
color: #${base16.base05};
|
|
}
|
|
button:disabled, button:disabled label {
|
|
color: alpha(#${base16.base05}, 0.5);
|
|
}
|
|
/* Suggested/destructive sit on pastel accent/bad — dark label
|
|
is intentional and contrast-checked (7.0:1 suggested, 6.2:1
|
|
destructive on Boreal). */
|
|
button.suggested-action, button.suggested-action label,
|
|
button.destructive-action, button.destructive-action label,
|
|
button.default, button.default label {
|
|
color: #${base16.base00};
|
|
}
|
|
/* adw-gtk3 builds ONLY the destructive background from
|
|
currentColor — mix(@destructive_color, alpha(currentColor,…)) —
|
|
where suggested-action uses an opaque mix(white,@accent_bg…).
|
|
So pinning the label above drags the background down with it
|
|
and the button renders dark-on-dark (1.03:1, invisible). Pin
|
|
the background to the same opaque pastel ladder suggested-action
|
|
uses. Enabled only: :disabled keeps the sheet's dark chrome and
|
|
already renders legibly. */
|
|
button.destructive-action:not(.flat):not(:disabled) {
|
|
background-color: mix(white, @destructive_bg_color, 0.9);
|
|
}
|
|
button.destructive-action:not(.flat):not(:disabled):hover {
|
|
background-color: mix(white, @destructive_bg_color, 0.85);
|
|
}
|
|
button.destructive-action:not(.flat):not(:disabled):active,
|
|
button.destructive-action:not(.flat):not(:disabled):checked {
|
|
background-color: mix(white, @destructive_bg_color, 0.7);
|
|
}
|
|
button.suggested-action.flat, button.suggested-action.flat label {
|
|
color: #${base16.base0D};
|
|
}
|
|
button.destructive-action.flat, button.destructive-action.flat label {
|
|
color: #${base16.base08};
|
|
}
|
|
'';
|
|
};
|
|
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;
|
|
};
|
|
};
|
|
|
|
# GTK3 hides menu-item icons by default (gtk-menu-images was flipped
|
|
# off upstream in 3.10). Classic tray menus — nm-applet's Wi-Fi list,
|
|
# udiskie — are built as image menu items, so without this their icons
|
|
# silently vanish. Stylix drives the HM gtk module, so extraConfig
|
|
# merges into the same settings.ini it already writes.
|
|
gtk.gtk3.extraConfig = {
|
|
"gtk-menu-images" = 1;
|
|
# Pair with adw-gtk3-dark so the theme sheet itself is dark, not just
|
|
# the @define-color recolor (see gtk.theme.name below).
|
|
"gtk-application-prefer-dark-theme" = t.mode != "light";
|
|
};
|
|
gtk.gtk4.extraConfig."gtk-application-prefer-dark-theme" = t.mode != "light";
|
|
|
|
# #98: Stylix hardcodes gtk.theme.name = "adw-gtk3" (light CSS) for every
|
|
# polarity and only injects libadwaita colour tokens. The light adw-gtk3
|
|
# sheet assumes light chrome and hardcodes near-black button labels in
|
|
# places — on Boreal that is black text on dark dialog buttons. Force
|
|
# the matching dark/light sheet; the package still provides both names.
|
|
gtk.theme = {
|
|
package = lib.mkDefault pkgs.adw-gtk3;
|
|
name = lib.mkForce (if t.mode == "light" then "adw-gtk3" else "adw-gtk3-dark");
|
|
};
|
|
|
|
# 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";
|
|
gtk-theme =
|
|
if t.mode == "light" then "adw-gtk3" else "adw-gtk3-dark";
|
|
};
|
|
};
|
|
}
|
|
|