danth / stylix

System-wide colorscheming and typography for NixOS
https://stylix.danth.me/
MIT License
1.18k stars 143 forks source link

doc: add Live Theme section #530

Open trueNAHO opened 2 months ago

trueNAHO commented 2 months ago

The question whether the Stylix themes can be changed without rebuilding the Nix configuration or restarting applications arises frequently. 1 3

Consequently, a dedicated section regarding this question should be added to the documentation.

danth commented 2 months ago

In the future this could integrate with #210 to track which applications support live theme reloading.

mighty-spirit commented 2 months ago

Hi, thanks for cool work on stylix. I don't have ssh keys and git set up at the moment, which means, that it would take me some time to be able to submit a proper PR. So I thought myself that I could perhaps also just share it here. My apologies, if that wouldn't be preferred. Anyway, this is what could be more or less put into Tips and Tricks Docs section:

NOTE: I use nh insted of nixos-rebuild, and I am just not 100% sure right now, whether these two don't handle specialisations differently

Dynamic theme switching

For following example, you will need home-manager installed as nixos module, so that you can trigger switch for both system and home-manager parts of your configuration from one place, i.e. system configuration level.

Base configuration

 stylix.base16Scheme = {
      # dark pallette
      base00 = "1b2229";
      base01 = "1c1f24";
      base02 = "202328";
      base03 = "73797e";
      base04 = "5b6268";
      base05 = "bbc2cf";
      base06 = "dfdfdf";
      base07 = "9ca0a4";
      base08 = "a9a1e1";
      base09 = "ecbe7b";
      base0A = "4db5bd";
      base0B = "98be65";
      base0C = "c678dd";
      base0D = "51afef";
      base0E = "da8548";
      base0F = "46d9ff";
      base0G = "5699aF";
  };

  home-manager.users.${user} = {
     gtk.theme.name = "Dracula";
     dconf.settings = { 
          "org/gnome/desktop/interface" = { color-scheme = "prefer-dark"; }; 
     };
  };

Light specialisation

 specialisation.light.configuration = {
   environment.etc."specialisation".text = "light"; # this is for 'nh' to correctly recognise the specialisation
   # you have to force the values to override those declared in base configuration, which this specialisation automatically inherits
   stylix.base16Scheme = lib.mkForce {
      # light pallette
      base00 = "fafafa"; 
      base01 = "e7e7e7"; 
      base02 = "dfdfdf"; 
      base03 = "5b6268"; 
      base04 = "383a42"; 
      base05 = "202328"; 
      base06 = "1c1f24"; 
      base07 = "5b6268"; 
      base08 = "a9a1e1"; 
      base09 = "d19a66"; 
      base0A = "4db5bd"; 
      base0B = "50a14f"; 
      base0C = "a626a4"; 
      base0D = "4078F2"; 
      base0E = "da8548"; 
      base0F = "46d9ff"; 
   };
   # feel free to add here more changes to base configuration from your Nixos options

   home-manager.users.${user} = {
     # change gtk theme
     gtk.theme.name = lib.mkForce "Catppuccin-Latte-Compact-Lavender-light"; 
     # change dark/light preference to trigger the change in darkreader web-browser extension, which has to be set on 'auto' to 'use system color scheme'
     dconf.settings = lib.mkForce { 
            "org/gnome/desktop/interface" = { color-scheme = "prefer-light"; }; 
     }; 
     # feel free to add here more changes to base configuration from your Home-Manager options
   }; 
 }

Doas rules

This is how to set rules exempting user from having to enter the root password for switching back and forth between base-dark and special-light configuration. This example uses doas, but sudo rules are declared in a similar way, check nixos options for details:

security.doas = {
    enable = true;
    extraRules = [
      { users = ["${user}"];
        keepEnv = true;
        persist = true; }
      # Allow your user to run switch-to-specialisation and back without a password
      { groups = [ "wheel" ];
        cmd = "/nix/var/nix/profiles/system/specialisation/light/bin/switch-to-configuration";
        args = ["switch"];
        runAs = "root";
        noPass = true; }
      { groups = [ "wheel" ];
        cmd = "/nix/var/nix/profiles/system/bin/switch-to-configuration";
        args = ["switch"];
        runAs = "root";
        noPass = true; }
   ];
};

Toggle-script

#!/bin/sh
# get current active system configuration
current_system=$(readlink /run/current-system)
# get the system path for the 'light' specialisation
light_specialisation=$(readlink /nix/var/nix/profiles/system/specialisation/light)
# check if the current system configuration matches the 'light' specialisation
if [ "$current_system" == "$light_specialisation" ]; then
   notify-send "Switching to Dark"
   doas /nix/var/nix/profiles/system/bin/switch-to-configuration switch
else
   notify-send "Switching to Light"
   doas /nix/var/nix/profiles/system/specialisation/light/bin/switch-to-configuration switch
fi

Import script as a package

For convinience, you can also import the script as a home package in your home-manager configuration:

home.packages = (with pkgs; [
  (pkgs.writeShellScriptBin "toggle-theme" ''
    #!/bin/sh

    <previous_toggle-theme_script_above>  

  '')
])
trueNAHO commented 2 months ago

I don't have ssh keys and git set up at the moment, which means, that it would take me some time to be able to submit a proper PR. So I thought myself that I could perhaps also just share it here. My apologies, if that wouldn't be preferred.

Thanks for your initial proposal. Feel free to submit a (draft) PR when you are all setup to simplify the review-loop.

NOTE: I use nh insted of nixos-rebuild, and I am just not 100% sure right now, whether these two don't handle specialisations differently

For compatibility and simplicity it might be better to only document the official (unstable) nix commands.

this is what could be more or less put into Tips and Tricks Docs section:

[...]

Dynamic theme switching

For following example, you will need home-manager installed as nixos module, so that you can trigger switch for both system and home-manager parts of your configuration from one place, i.e. system configuration level.

Is it possible to generalize the phrasing and make it not target-specific (NixOS, Home Manager, nix-darwin)?

Base configuration

 stylix.base16Scheme = {
      # dark pallette
      base00 = "1b2229";
      base01 = "1c1f24";
      base02 = "202328";
      base03 = "73797e";
      base04 = "5b6268";
      base05 = "bbc2cf";
      base06 = "dfdfdf";
      base07 = "9ca0a4";
      base08 = "a9a1e1";
      base09 = "ecbe7b";
      base0A = "4db5bd";
      base0B = "98be65";
      base0C = "c678dd";
      base0D = "51afef";
      base0E = "da8548";
      base0F = "46d9ff";
      base0G = "5699aF";
  };

For brevity, just refer to a pkgs.base16-schemes scheme.

  home-manager.users.${user} = {
     gtk.theme.name = "Dracula";
     dconf.settings = {
          "org/gnome/desktop/interface" = { color-scheme = "prefer-dark"; };
     };
  };

For brevity, it might be better to treat this section as reference and not a minimal working example by removing any non-essential information.

Light specialisation

 specialisation.light.configuration = {
   environment.etc."specialisation".text = "light"; # this is for 'nh' to correctly recognise the specialisation
   # you have to force the values to override those declared in base configuration, which this specialisation automatically inherits

For reduced maintenance, it might be better to assume that people using custom wrappers know how to use them and that we can remove this information as it is not relevant to everyone.

   stylix.base16Scheme = lib.mkForce {
      # light pallette
      base00 = "fafafa";
      base01 = "e7e7e7";
      base02 = "dfdfdf";
      base03 = "5b6268";
      base04 = "383a42";
      base05 = "202328";
      base06 = "1c1f24";
      base07 = "5b6268";
      base08 = "a9a1e1";
      base09 = "d19a66";
      base0A = "4db5bd";
      base0B = "50a14f";
      base0C = "a626a4";
      base0D = "4078F2";
      base0E = "da8548";
      base0F = "46d9ff";
   };

For brevity, just refer to a pkgs.base16-schemes scheme.

   # feel free to add here more changes to base configuration from your Nixos options

   home-manager.users.${user} = {
     # change gtk theme
     gtk.theme.name = lib.mkForce "Catppuccin-Latte-Compact-Lavender-light";
     # change dark/light preference to trigger the change in darkreader web-browser extension, which has to be set on 'auto' to 'use system color scheme'
     dconf.settings = lib.mkForce {
            "org/gnome/desktop/interface" = { color-scheme = "prefer-light"; };
     };
     # feel free to add here more changes to base configuration from your Home-Manager options
   };
 }

For brevity, it might be better to treat this section as reference and not a minimal working example by removing any non-essential information.

Doas rules

This is how to set rules exempting user from having to enter the root password for switching back and forth between base-dark and special-light configuration. This example uses doas, but sudo rules are declared in a similar way, check nixos options for details:

security.doas = {
    enable = true;
    extraRules = [
      { users = ["${user}"];
        keepEnv = true;
        persist = true; }
      # Allow your user to run switch-to-specialisation and back without a password
      { groups = [ "wheel" ];
        cmd = "/nix/var/nix/profiles/system/specialisation/light/bin/switch-to-configuration";
        args = ["switch"];
        runAs = "root";
        noPass = true; }
      { groups = [ "wheel" ];
        cmd = "/nix/var/nix/profiles/system/bin/switch-to-configuration";
        args = ["switch"];
        runAs = "root";
        noPass = true; }
   ];
};

For portability and potentially reduced footprint, this should target sudo rather than doas.

Toggle-script

#!/bin/sh
# get current active system configuration
current_system=$(readlink /run/current-system)
# get the system path for the 'light' specialisation
light_specialisation=$(readlink /nix/var/nix/profiles/system/specialisation/light)
# check if the current system configuration matches the 'light' specialisation
if [ "$current_system" == "$light_specialisation" ]; then
   notify-send "Switching to Dark"
   doas /nix/var/nix/profiles/system/bin/switch-to-configuration switch
else
   notify-send "Switching to Light"
   doas /nix/var/nix/profiles/system/specialisation/light/bin/switch-to-configuration switch
fi

Import script as a package

For convinience, you can also import the script as a home package in your home-manager configuration:

home.packages = (with pkgs; [
  (pkgs.writeShellScriptBin "toggle-theme" ''
    #!/bin/sh

    <previous_toggle-theme_script_above>

  '')
])

Assuming that that Nix users are capable of figuring their own use cases out, we should avoid listing trivial and opinionated shell scripts. Never forget that any documented code will inevitably be copy-pasted.

mighty-spirit commented 2 months ago

Thanks for your initial proposal. Feel free to submit a (draft) PR when you are all setup to simplify the review-loop.

Will do that! Thanks for all the feedback : )

Edit: Just so I won't leave anyone waiting for anything. Even though it is a rather small thing, it might take me some time to get to it, as I find myself in the middle of PhD applications deadlines and changing flats rn.

DrVoidest commented 1 month ago

Wouldn't it be better and more universal to do say stylix.dayTheme = "Your day theme" stylix.nightTheme = "Your night theme" in your config. Then from cmd you could do stylix switch day or stylix switch night so people could use what ever triggers they want like cron jobs, scripts etc? I might be completely miss understanding and a live reload is not possible, so sorry for wasting your time if I am.

trueNAHO commented 1 month ago

Wouldn't it be better and more universal to do say stylix.dayTheme = "Your day theme" stylix.nightTheme = "Your night theme" in your config. Then from cmd you could do stylix switch day or stylix switch night so people could use what ever triggers they want like cron jobs, scripts etc?

Providing a predefined set of specialisations does not scale. I would have to think more about whether providing a generic Stylix specialisation abstraction for this specific purpose would really be beneficial.

I might be completely miss understanding and a live reload is not possible, so sorry for wasting your time if I am.

Considering this may be a good feature addition, this is presumably not wasting anyones time.

danth commented 1 month ago

Stylix should try to support changing the theme immediately (without restarting the application) after a rebuild, wherever this is possible.

However, I agree that there is no need to provide options such as stylix.dayTheme, since NixOS specializations already provide a more flexible way to do this.

trueNAHO commented 1 month ago

For reference, https://github.com/danth/stylix/issues/447#issuecomment-2351837256 provides a highly promising PoC.

no-mood commented 3 weeks ago

Just leaving an idea. I recently migrated from Hyprland to Cosmic, and since there's no support for Stylix, the native option for light and dark theme works. Would it be more feasible to implement an option related to DEs that sets the options for both light and dark themes?

trueNAHO commented 2 weeks ago

Just leaving an idea. I recently migrated from Hyprland to Cosmic, and since there's no support for Stylix, the native option for light and dark theme works.

Do you mean that Cosmic has its own light and dark theme implementation? IIRC, most DEs support this.

Would it be more feasible to implement an option related to DEs that sets the options for both light and dark themes?

DEs usually support far less applications (usually only GTK, QT, and the DE itself) than Stylix because we are manually supporting each target. It would be weird for a DE to theme every possible application.