Skip to main content

NixOS: Theming with Stylix

NixOS - This article is part of a series.
Part 7: This Article

Adding options for colour schemes, fonts and wallpapers
#

Using Stylix, we can apply base16 colour schemes, wallpapers and fonts. Separate modules should be instantiated for nixos for system theming and home-manager for user theming. The options and contents for each of these modules can be identical:

{
  inputs,
  ...
}:
let
  module =
    {
      config,
      lib,
      pkgs,
      ...
    }:
    {
      options = {
        theme = {
          enable = lib.mkEnableOption "stylix theme";

          polarity = lib.mkOption {
            type = lib.types.enum [
              "light"
              "dark"
            ];
            default = "dark";
            description = "light or dark theme";
          };

          base16Scheme = lib.mkOption {
            type = lib.types.str;
            default = "default-dark";
            description = "tinted theming colour scheme";
            example = "catppuccin-mocha";
          };

          image = lib.mkOption {
            type = lib.types.submodule {
              options = {
                url = lib.mkOption {
                  type = lib.types.str;
                  description = "Download URL";
                };
                hash = lib.mkOption {
                  type = lib.types.str;
                  description = "SHA256 hash in SRI format";
                };
              };
            };
            default = {
              url = "https://raw.githubusercontent.com/NixOS/nixos-artwork/refs/heads/master/wallpapers/nix-wallpaper-simple-dark-gray.png";
              hash = "sha256-JaLHdBxwrphKVherDVe5fgh+3zqUtpcwuNbjwrBlAok=";
            };
            description = "Custom source with URL and hash";
          };

          fonts = lib.mkOption {
            type = lib.types.submodule {
              options = {
                interface = lib.mkOption {
                  type = lib.types.submodule {
                    options = {
                      package = lib.mkOption {
                        type = lib.types.package;
                        description = "package to use for the interface font";
                      };
                      name = lib.mkOption {
                        type = lib.types.str;
                        description = "The name to use for the interface font";
                      };
                    };
                  };
                  default = {
                    package = pkgs.inter;
                    name = "Inter";
                  };
                  defaultText = lib.literalExpression "Inter";
                  description = "The font to use for the interface";
                };
                monospace = lib.mkOption {
                  type = lib.types.submodule {
                    options = {
                      package = lib.mkOption {
                        type = lib.types.package;
                        description = "package to use for the monospace font";
                      };
                      name = lib.mkOption {
                        type = lib.types.str;
                        description = "The name to use for the monospace font";
                      };
                    };
                  };
                  default = {
                    package = pkgs.nerd-fonts.jetbrains-mono;
                    name = "JetBrainsMono Nerd Font Mono";
                  };
                  defaultText = lib.literalExpression "JetBrainsMono Nerd Font Mono";
                  description = "The font to use for the terminal";
                };
                emoji = lib.mkOption {
                  type = lib.types.submodule {
                    options = {
                      package = lib.mkOption {
                        type = lib.types.package;
                        description = "package to use for the emoji font";
                      };
                      name = lib.mkOption {
                        type = lib.types.str;
                        description = "The name to use for the emoji font";
                      };
                    };
                  };
                  default = {
                    package = pkgs.noto-fonts-color-emoji;
                    name = "Noto Color Emoji";
                  };
                  defaultText = lib.literalExpression "Noto Color Emoji";
                  description = "The font to use for emoji";
                };
              };
            };
            default = { };
            description = "Fonts used for interface, terminal and emojis";
          };
        };
      };

      config = lib.mkIf config.theme.enable {
        stylix = {
          enable = true;
          inherit (config.theme.polarity) ;
          base16Scheme = "${pkgs.base16-schemes}/share/themes/${config.theme.base16Scheme}.yaml";
          image = pkgs.fetchurl config.theme.image;
          fonts = {
            serif = config.theme.fonts.interface;
            sansSerif = config.theme.fonts.interface;
            inherit (config.theme.fonts.monospace) ;
            inherit (config.theme.fonts.emoji) ;
          };
        };
      };
    };
in
{
  flake.modules.nixos.theme = module;
  flake.modules.homeManager.theme = module;
}

Setting theme options in our config
#

With the theme modules loaded we can configure themes for each system and user:

        theme = {
          image = {
            url = "https://raw.githubusercontent.com/AngelJumbo/gruvbox-wallpapers/refs/heads/main/wallpapers/photography/forest-2.jpg";
            hash = "sha256-RqzCCnn4b5kU7EYgaPF19Gr9I5cZrkEdsTu+wGaaMFI=";
          };
          base16Scheme = "gruvbox-material-dark-hard";
        };

NixOS - This article is part of a series.
Part 7: This Article