From 261e69d437a0c0e7885a2bfa86191563c7ef2c26 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Fri, 20 Mar 2026 13:35:06 +0100 Subject: [PATCH 1/5] wip --- flake.nix | 2 +- fonts/default.nix | 37 --- fonts/emoji/default.nix | 3 - fonts/emoji/noto-color.nix | 7 - fonts/module.nix | 97 ------- fonts/monospace/blex-mono.nix | 12 - fonts/monospace/default.nix | 8 - fonts/monospace/fira-code.nix | 9 - fonts/monospace/inconsolata.nix | 7 - fonts/monospace/iosevka-term.nix | 16 -- fonts/monospace/jetbrains-mono.nix | 13 - fonts/monospace/source-code-pro.nix | 12 - fonts/sans-serif/default.nix | 4 - fonts/sans-serif/fira-sans.nix | 7 - fonts/sans-serif/iosevka-aile.nix | 7 - fonts/serif/default.nix | 3 - fonts/serif/source-serif.nix | 7 - modules/flake/machines/default.nix | 5 +- modules/home/fonts/default.nix | 244 ++++++++++++++++++ modules/home/fonts/emoji/noto-color.nix | 9 + modules/home/fonts/monospace/blex-mono.nix | 12 + modules/home/fonts/monospace/fira-code.nix | 11 + modules/home/fonts/monospace/inconsolata.nix | 9 + modules/home/fonts/monospace/iosevka-term.nix | 23 ++ .../home/fonts/monospace/jetbrains-mono.nix | 15 ++ .../home/fonts/monospace/source-code-pro.nix | 12 + modules/home/fonts/sans-serif/fira-sans.nix | 9 + .../home/fonts/sans-serif/iosevka-aile.nix | 9 + modules/home/fonts/serif/source-serif.nix | 9 + modules/home/fonts/stacks/blex.nix | 6 + modules/home/fonts/stacks/docsrs.nix | 6 + modules/home/fonts/stacks/fira.nix | 6 + modules/home/fonts/stacks/iosevka.nix | 6 + modules/home/ghostty/font.nix | 12 +- modules/home/qutebrowser/default.nix | 4 +- modules/home/zathura/default.nix | 4 +- modules/home/zed/settings.nix | 4 +- 37 files changed, 401 insertions(+), 265 deletions(-) delete mode 100644 fonts/default.nix delete mode 100644 fonts/emoji/default.nix delete mode 100644 fonts/emoji/noto-color.nix delete mode 100644 fonts/module.nix delete mode 100644 fonts/monospace/blex-mono.nix delete mode 100644 fonts/monospace/default.nix delete mode 100644 fonts/monospace/fira-code.nix delete mode 100644 fonts/monospace/inconsolata.nix delete mode 100644 fonts/monospace/iosevka-term.nix delete mode 100644 fonts/monospace/jetbrains-mono.nix delete mode 100644 fonts/monospace/source-code-pro.nix delete mode 100644 fonts/sans-serif/default.nix delete mode 100644 fonts/sans-serif/fira-sans.nix delete mode 100644 fonts/sans-serif/iosevka-aile.nix delete mode 100644 fonts/serif/default.nix delete mode 100644 fonts/serif/source-serif.nix create mode 100644 modules/home/fonts/default.nix create mode 100644 modules/home/fonts/emoji/noto-color.nix create mode 100644 modules/home/fonts/monospace/blex-mono.nix create mode 100644 modules/home/fonts/monospace/fira-code.nix create mode 100644 modules/home/fonts/monospace/inconsolata.nix create mode 100644 modules/home/fonts/monospace/iosevka-term.nix create mode 100644 modules/home/fonts/monospace/jetbrains-mono.nix create mode 100644 modules/home/fonts/monospace/source-code-pro.nix create mode 100644 modules/home/fonts/sans-serif/fira-sans.nix create mode 100644 modules/home/fonts/sans-serif/iosevka-aile.nix create mode 100644 modules/home/fonts/serif/source-serif.nix create mode 100644 modules/home/fonts/stacks/blex.nix create mode 100644 modules/home/fonts/stacks/docsrs.nix create mode 100644 modules/home/fonts/stacks/fira.nix create mode 100644 modules/home/fonts/stacks/iosevka.nix diff --git a/flake.nix b/flake.nix index d8db6d3f..c90129d5 100644 --- a/flake.nix +++ b/flake.nix @@ -64,7 +64,7 @@ _module.args = { colorscheme = "gruvbox"; - fonts = (import ./fonts).iosevka; + fontStack = "iosevka"; username = "noib3"; }; diff --git a/fonts/default.nix b/fonts/default.nix deleted file mode 100644 index 54a7477d..00000000 --- a/fonts/default.nix +++ /dev/null @@ -1,37 +0,0 @@ -let - fonts = { - emoji = import ./emoji; - monospace = import ./monospace; - sansSerif = import ./sans-serif; - serif = import ./serif; - }; -in -{ - blex = pkgs: rec { - sansSerif = monospace; - serif = monospace; - monospace = fonts.monospace.blex-mono { inherit pkgs; }; - emoji = fonts.emoji.noto-color { inherit pkgs; }; - }; - - docsrs = pkgs: { - sansSerif = fonts.sansSerif.fira-sans { inherit pkgs; }; - serif = fonts.serif.source-serif { inherit pkgs; }; - monospace = fonts.monospace.source-code-pro { inherit pkgs; }; - emoji = fonts.emoji.noto-color { inherit pkgs; }; - }; - - fira = pkgs: rec { - sansSerif = fonts.sansSerif.fira-sans { inherit pkgs; }; - serif = sansSerif; - monospace = fonts.monospace.fira-code { inherit pkgs; }; - emoji = fonts.emoji.noto-color { inherit pkgs; }; - }; - - iosevka = pkgs: rec { - sansSerif = fonts.sansSerif.iosevka-aile { inherit pkgs; }; - serif = sansSerif; - monospace = fonts.monospace.iosevka-term { inherit pkgs; }; - emoji = fonts.emoji.noto-color { inherit pkgs; }; - }; -} diff --git a/fonts/emoji/default.nix b/fonts/emoji/default.nix deleted file mode 100644 index f10c9fe2..00000000 --- a/fonts/emoji/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - noto-color = import ./noto-color.nix; -} diff --git a/fonts/emoji/noto-color.nix b/fonts/emoji/noto-color.nix deleted file mode 100644 index 99636864..00000000 --- a/fonts/emoji/noto-color.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: - -{ - name = "Noto Color Emoji"; - package = pkgs.noto-fonts-color-emoji; - size = _config: _program: 16.5; -} diff --git a/fonts/module.nix b/fonts/module.nix deleted file mode 100644 index f8eac16d..00000000 --- a/fonts/module.nix +++ /dev/null @@ -1,97 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: - -with lib; -let - cfg = config.fonts; - - mkFontFamily = - { description }: - mkOption { - type = types.submodule { - options = { - name = mkOption { - type = types.str; - example = "Iosevka Nerd Font"; - description = "The name of the font family"; - }; - - package = mkOption { - type = types.package; - example = pkgs.fira-sans; - description = "The package providing the font family"; - }; - - size = mkOption { - type = types.functionTo (types.functionTo types.float); - example = config: program: if program == "qutebrowser" then 18 else 16; - description = '' - The size of the font in a program given the global config and its - name - ''; - }; - - normal = mkOption { - type = types.str; - example = "Regular"; - default = "Regular"; - description = "The normal variant of the font family"; - }; - - bold = mkOption { - type = types.str; - example = "Bold"; - default = "Bold"; - description = "The bold variant of the font family"; - }; - - italic = mkOption { - type = types.str; - example = "Italic"; - default = "Italic"; - description = "The italic variant of the font family"; - }; - - bold_italic = mkOption { - type = types.str; - example = "Bold Italic"; - default = "Bold Italic"; - description = "The bold italic variant of the font family"; - }; - }; - }; - inherit description; - }; -in -{ - options.fonts = { - serif = mkFontFamily { description = "Serif font family"; }; - sansSerif = mkFontFamily { description = "Sans serif font family"; }; - monospace = mkFontFamily { description = "Monospace font family"; }; - emoji = mkFontFamily { description = "Emoji font family"; }; - }; - - config = { - fonts.fontconfig = { - enable = pkgs.stdenv.isLinux; - - defaultFonts = { - serif = [ cfg.serif.name ]; - sansSerif = [ cfg.sansSerif.name ]; - monospace = [ cfg.monospace.name ]; - emoji = [ cfg.emoji.name ]; - }; - }; - - home.packages = [ - cfg.serif.package - cfg.sansSerif.package - cfg.monospace.package - cfg.emoji.package - ]; - }; -} diff --git a/fonts/monospace/blex-mono.nix b/fonts/monospace/blex-mono.nix deleted file mode 100644 index a8364a48..00000000 --- a/fonts/monospace/blex-mono.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ pkgs, ... }: - -{ - name = "BlexMono Nerd Font"; - package = pkgs.nerd-fonts.blex-mono; - size = - config: program: - if program == "ghostty" && config.machines."skunk@linux".isCurrent then - 14.0 - else - 18.5; -} diff --git a/fonts/monospace/default.nix b/fonts/monospace/default.nix deleted file mode 100644 index 90025402..00000000 --- a/fonts/monospace/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - blex-mono = import ./blex-mono.nix; - fira-code = import ./fira-code.nix; - inconsolata = import ./inconsolata.nix; - iosevka-term = import ./iosevka-term.nix; - jetbrains-mono = import ./jetbrains-mono.nix; - source-code-pro = import ./source-code-pro.nix; -} diff --git a/fonts/monospace/fira-code.nix b/fonts/monospace/fira-code.nix deleted file mode 100644 index 56859d78..00000000 --- a/fonts/monospace/fira-code.nix +++ /dev/null @@ -1,9 +0,0 @@ -{ pkgs, ... }: - -{ - name = "FiraCode Nerd Font"; - package = pkgs.nerd-fonts.fira-code; - size = - config: _program: - if config.machines."skunk@darwin".isCurrent then 18.5 else 13.5; -} diff --git a/fonts/monospace/inconsolata.nix b/fonts/monospace/inconsolata.nix deleted file mode 100644 index 34fa0d3a..00000000 --- a/fonts/monospace/inconsolata.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: - -{ - name = "Inconsolata Nerd Font"; - package = pkgs.nerd-fonts.inconsolata; - size = _config: _program: 16.5; -} diff --git a/fonts/monospace/iosevka-term.nix b/fonts/monospace/iosevka-term.nix deleted file mode 100644 index 2d26af2b..00000000 --- a/fonts/monospace/iosevka-term.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, ... }: - -{ - name = "IosevkaTerm Nerd Font"; - bold = "ExtraBold"; - bold_italic = "ExtraBold Italic"; - package = pkgs.nerd-fonts.iosevka-term; - size = - config: _program: - if config.machines."skunk@darwin".isCurrent then - 22.75 - else if config.machines.stolen-bride.isCurrent then - 22.0 - else - 16.5; -} diff --git a/fonts/monospace/jetbrains-mono.nix b/fonts/monospace/jetbrains-mono.nix deleted file mode 100644 index 810e0d24..00000000 --- a/fonts/monospace/jetbrains-mono.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ pkgs, ... }: - -{ - name = "JetBrainsMono Nerd Font"; - package = pkgs.nerd-fonts.jetbrains-mono; - size = _config: _program: 16.5; - bold = { - name = "Extra Bold"; - }; - bold_italic = { - name = "Extra Bold"; - }; -} diff --git a/fonts/monospace/source-code-pro.nix b/fonts/monospace/source-code-pro.nix deleted file mode 100644 index 46a08dbe..00000000 --- a/fonts/monospace/source-code-pro.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ pkgs, ... }: - -{ - name = "SourceCodePro Nerd Font"; - package = pkgs.nerd-fonts.sauce-code-pro; - size = - config: program: - if program == "ghostty" && config.machines."skunk@linux".isCurrent then - 14.0 - else - 16.5; -} diff --git a/fonts/sans-serif/default.nix b/fonts/sans-serif/default.nix deleted file mode 100644 index 97aa6f50..00000000 --- a/fonts/sans-serif/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ - iosevka-aile = import ./iosevka-aile.nix; - fira-sans = import ./fira-sans.nix; -} diff --git a/fonts/sans-serif/fira-sans.nix b/fonts/sans-serif/fira-sans.nix deleted file mode 100644 index 5afe7903..00000000 --- a/fonts/sans-serif/fira-sans.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: - -{ - name = "FiraSans"; - package = pkgs.fira-sans; - size = _config: _program: 14.5; -} diff --git a/fonts/sans-serif/iosevka-aile.nix b/fonts/sans-serif/iosevka-aile.nix deleted file mode 100644 index 06ce1374..00000000 --- a/fonts/sans-serif/iosevka-aile.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: - -{ - name = "Iosevka Aile"; - package = pkgs.iosevka-bin.override { variant = "Aile"; }; - size = _config: _program: 16.5; -} diff --git a/fonts/serif/default.nix b/fonts/serif/default.nix deleted file mode 100644 index 86d0b524..00000000 --- a/fonts/serif/default.nix +++ /dev/null @@ -1,3 +0,0 @@ -{ - source-serif = import ./source-serif.nix; -} diff --git a/fonts/serif/source-serif.nix b/fonts/serif/source-serif.nix deleted file mode 100644 index b6573397..00000000 --- a/fonts/serif/source-serif.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs, ... }: - -{ - name = "Source Serif 4"; - package = pkgs.source-serif; - size = _config: _program: 16.5; -} diff --git a/modules/flake/machines/default.nix b/modules/flake/machines/default.nix index f549de2c..dce6309a 100644 --- a/modules/flake/machines/default.nix +++ b/modules/flake/machines/default.nix @@ -3,7 +3,7 @@ config, lib, colorscheme, - fonts, + fontStack, username, ... }: @@ -100,13 +100,12 @@ in modules = [ homeManagerMachinesModule - ../../../fonts/module.nix ../../home { - fonts = fonts pkgs; home.username = username; machines.current.name = machineName; modules.colorscheme.${colorscheme}.enable = true; + modules.fonts.${fontStack}.enable = true; } ]; diff --git a/modules/home/fonts/default.nix b/modules/home/fonts/default.nix new file mode 100644 index 00000000..dc9dcd42 --- /dev/null +++ b/modules/home/fonts/default.nix @@ -0,0 +1,244 @@ +{ + config, + lib, + pkgs, + ... +}: + +with lib; +let + cfg = config.modules.fonts; + + # The set of font categories, mapping directory names to option keys. + categories = { + monospace = "monospace"; + sans-serif = "sansSerif"; + serif = "serif"; + emoji = "emoji"; + }; + + # A sizes attrset that uses __functor to fall back to `default` for any + # program that doesn't have an explicit override. + mkSizesFunctor = + sizesAttr: + let + overrides = removeAttrs sizesAttr [ "default" ]; + in + { + inherit (sizesAttr) default; + __functor = self: program: overrides.${program} or self.default; + } + // overrides; + + fontSubmodule = types.submodule { + options = { + name = mkOption { + type = types.str; + example = "IosevkaTerm Nerd Font"; + description = "The font family name as known to fontconfig"; + }; + + package = mkOption { + type = types.package; + example = "pkgs.nerd-fonts.iosevka-term"; + description = "The Nix package providing the font"; + }; + + styles = { + normal = mkOption { + type = types.str; + default = "Regular"; + description = "The normal font style"; + }; + + bold = mkOption { + type = types.str; + default = "Bold"; + description = "The bold font style"; + }; + + italic = mkOption { + type = types.str; + default = "Italic"; + description = "The italic font style"; + }; + + boldItalic = mkOption { + type = types.str; + default = "Bold Italic"; + description = "The bold italic font style"; + }; + }; + + sizes = mkOption { + type = types.attrsOf types.float; + example = { + default = 16.5; + ghostty = 14.0; + }; + description = '' + Font sizes keyed by program name, with a mandatory `default` entry. + Accessed as a functor: `font.sizes "ghostty"` returns the + ghostty-specific size if defined, otherwise the default. + ''; + }; + }; + }; + + # Auto-discover font .nix files in a category directory. + discoverNames = + dir: + builtins.readDir dir + |> attrNames + |> filter (n: hasSuffix ".nix" n && n != "default.nix") + |> map (removeSuffix ".nix"); + + # Auto-discover stack names. + stackNames = discoverNames ./stacks; + + mkStackOption = + stackName: + mkOption { + type = types.submodule { + options = { + enable = mkEnableOption stackName; + + name = mkOption { + type = types.str; + default = stackName; + readOnly = true; + description = "The name of the font stack"; + }; + + fonts = mkOption { + type = types.attrsOf types.str; + default = import ./stacks/${stackName}.nix; + readOnly = true; + description = '' + Mapping from category to font name, e.g. + `{ monospace = "iosevka-term"; sansSerif = "iosevka-aile"; ... }`. + ''; + }; + }; + }; + default = { }; + }; + + stackOptions = + stackNames + |> map (name: nameValuePair name (mkStackOption name)) + |> listToAttrs; +in +{ + # Import all individual font modules from category directories. + imports = + categories + |> mapAttrsToList ( + dirName: _categoryKey: + let + dir = ./${dirName}; + names = discoverNames dir; + in + map (n: dir + "/${n}.nix") names + ) + |> concatLists; + + options.modules.fonts = stackOptions // { + # Per-category lists of all declared fonts. Individual font modules append + # to these via `config.modules.fonts.`. + monospace = mkOption { + type = types.attrsOf fontSubmodule; + default = { }; + description = "All declared monospace fonts"; + }; + + sansSerif = mkOption { + type = types.attrsOf fontSubmodule; + default = { }; + description = "All declared sans-serif fonts"; + }; + + serif = mkOption { + type = types.attrsOf fontSubmodule; + default = { }; + description = "All declared serif fonts"; + }; + + emoji = mkOption { + type = types.attrsOf fontSubmodule; + default = { }; + description = "All declared emoji fonts"; + }; + + current = mkOption { + type = types.attrs; + readOnly = true; + description = "The resolved font configuration for the enabled stack"; + }; + }; + + config = + let + enabledStacks = + removeAttrs cfg [ + "current" + "monospace" + "sansSerif" + "serif" + "emoji" + ] + |> mapAttrsToList (_: v: v) + |> filter (s: s.enable); + + enabledStack = head enabledStacks; + stackDef = enabledStack.fonts; + + # All declared fonts merged into a single flat attrset. This allows + # stacks to reference fonts across categories (e.g. using a monospace + # font for the sans-serif slot). + allFonts = cfg.monospace // cfg.sansSerif // cfg.serif // cfg.emoji; + + resolveFont = + fontName: + let + fontDef = allFonts.${fontName}; + in + { + inherit (fontDef) name package styles; + sizes = mkSizesFunctor fontDef.sizes; + }; + in + { + assertions = [ + { + assertion = length enabledStacks == 1; + message = "Exactly one font stack must be enabled"; + } + ]; + + modules.fonts.current = { + name = enabledStack.name; + monospace = resolveFont stackDef.monospace; + sansSerif = resolveFont stackDef.sansSerif; + serif = resolveFont stackDef.serif; + emoji = resolveFont stackDef.emoji; + }; + + home.packages = unique [ + cfg.current.monospace.package + cfg.current.sansSerif.package + cfg.current.serif.package + cfg.current.emoji.package + ]; + + fonts.fontconfig = { + enable = pkgs.stdenv.isLinux; + defaultFonts = { + serif = [ cfg.current.serif.name ]; + sansSerif = [ cfg.current.sansSerif.name ]; + monospace = [ cfg.current.monospace.name ]; + emoji = [ cfg.current.emoji.name ]; + }; + }; + }; +} diff --git a/modules/home/fonts/emoji/noto-color.nix b/modules/home/fonts/emoji/noto-color.nix new file mode 100644 index 00000000..4fe0a463 --- /dev/null +++ b/modules/home/fonts/emoji/noto-color.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + modules.fonts.emoji.noto-color = { + name = "Noto Color Emoji"; + package = pkgs.noto-fonts-color-emoji; + sizes.default = 16.5; + }; +} diff --git a/modules/home/fonts/monospace/blex-mono.nix b/modules/home/fonts/monospace/blex-mono.nix new file mode 100644 index 00000000..09577f21 --- /dev/null +++ b/modules/home/fonts/monospace/blex-mono.nix @@ -0,0 +1,12 @@ +{ config, pkgs, ... }: + +{ + modules.fonts.monospace.blex-mono = { + name = "BlexMono Nerd Font"; + package = pkgs.nerd-fonts.blex-mono; + sizes = { + default = 18.5; + ghostty = if config.machines."skunk@linux".isCurrent then 14.0 else 18.5; + }; + }; +} diff --git a/modules/home/fonts/monospace/fira-code.nix b/modules/home/fonts/monospace/fira-code.nix new file mode 100644 index 00000000..d1a56dee --- /dev/null +++ b/modules/home/fonts/monospace/fira-code.nix @@ -0,0 +1,11 @@ +{ config, pkgs, ... }: + +{ + modules.fonts.monospace.fira-code = { + name = "FiraCode Nerd Font"; + package = pkgs.nerd-fonts.fira-code; + sizes = { + default = if config.machines."skunk@darwin".isCurrent then 18.5 else 13.5; + }; + }; +} diff --git a/modules/home/fonts/monospace/inconsolata.nix b/modules/home/fonts/monospace/inconsolata.nix new file mode 100644 index 00000000..211c5919 --- /dev/null +++ b/modules/home/fonts/monospace/inconsolata.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + modules.fonts.monospace.inconsolata = { + name = "Inconsolata Nerd Font"; + package = pkgs.nerd-fonts.inconsolata; + sizes.default = 16.5; + }; +} diff --git a/modules/home/fonts/monospace/iosevka-term.nix b/modules/home/fonts/monospace/iosevka-term.nix new file mode 100644 index 00000000..bd430934 --- /dev/null +++ b/modules/home/fonts/monospace/iosevka-term.nix @@ -0,0 +1,23 @@ +{ config, pkgs, ... }: + +{ + modules.fonts.monospace.iosevka-term = { + name = "IosevkaTerm Nerd Font"; + package = pkgs.nerd-fonts.iosevka-term; + + styles = { + bold = "ExtraBold"; + boldItalic = "ExtraBold Italic"; + }; + + sizes = { + default = + if config.machines."skunk@darwin".isCurrent then + 22.75 + else if config.machines.stolen-bride.isCurrent then + 22.0 + else + 16.5; + }; + }; +} diff --git a/modules/home/fonts/monospace/jetbrains-mono.nix b/modules/home/fonts/monospace/jetbrains-mono.nix new file mode 100644 index 00000000..29582360 --- /dev/null +++ b/modules/home/fonts/monospace/jetbrains-mono.nix @@ -0,0 +1,15 @@ +{ pkgs, ... }: + +{ + modules.fonts.monospace.jetbrains-mono = { + name = "JetBrainsMono Nerd Font"; + package = pkgs.nerd-fonts.jetbrains-mono; + + styles = { + bold = "Extra Bold"; + boldItalic = "Extra Bold Italic"; + }; + + sizes.default = 16.5; + }; +} diff --git a/modules/home/fonts/monospace/source-code-pro.nix b/modules/home/fonts/monospace/source-code-pro.nix new file mode 100644 index 00000000..6adb1cd1 --- /dev/null +++ b/modules/home/fonts/monospace/source-code-pro.nix @@ -0,0 +1,12 @@ +{ config, pkgs, ... }: + +{ + modules.fonts.monospace.source-code-pro = { + name = "SourceCodePro Nerd Font"; + package = pkgs.nerd-fonts.sauce-code-pro; + sizes = { + default = 16.5; + ghostty = if config.machines."skunk@linux".isCurrent then 14.0 else 16.5; + }; + }; +} diff --git a/modules/home/fonts/sans-serif/fira-sans.nix b/modules/home/fonts/sans-serif/fira-sans.nix new file mode 100644 index 00000000..44f7fed5 --- /dev/null +++ b/modules/home/fonts/sans-serif/fira-sans.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + modules.fonts.sansSerif.fira-sans = { + name = "FiraSans"; + package = pkgs.fira-sans; + sizes.default = 14.5; + }; +} diff --git a/modules/home/fonts/sans-serif/iosevka-aile.nix b/modules/home/fonts/sans-serif/iosevka-aile.nix new file mode 100644 index 00000000..4911ce9e --- /dev/null +++ b/modules/home/fonts/sans-serif/iosevka-aile.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + modules.fonts.sansSerif.iosevka-aile = { + name = "Iosevka Aile"; + package = pkgs.iosevka-bin.override { variant = "Aile"; }; + sizes.default = 16.5; + }; +} diff --git a/modules/home/fonts/serif/source-serif.nix b/modules/home/fonts/serif/source-serif.nix new file mode 100644 index 00000000..aa995f55 --- /dev/null +++ b/modules/home/fonts/serif/source-serif.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: + +{ + modules.fonts.serif.source-serif = { + name = "Source Serif 4"; + package = pkgs.source-serif; + sizes.default = 16.5; + }; +} diff --git a/modules/home/fonts/stacks/blex.nix b/modules/home/fonts/stacks/blex.nix new file mode 100644 index 00000000..65461296 --- /dev/null +++ b/modules/home/fonts/stacks/blex.nix @@ -0,0 +1,6 @@ +{ + monospace = "blex-mono"; + sansSerif = "blex-mono"; + serif = "blex-mono"; + emoji = "noto-color"; +} diff --git a/modules/home/fonts/stacks/docsrs.nix b/modules/home/fonts/stacks/docsrs.nix new file mode 100644 index 00000000..3269d475 --- /dev/null +++ b/modules/home/fonts/stacks/docsrs.nix @@ -0,0 +1,6 @@ +{ + monospace = "source-code-pro"; + sansSerif = "fira-sans"; + serif = "source-serif"; + emoji = "noto-color"; +} diff --git a/modules/home/fonts/stacks/fira.nix b/modules/home/fonts/stacks/fira.nix new file mode 100644 index 00000000..35b66d2d --- /dev/null +++ b/modules/home/fonts/stacks/fira.nix @@ -0,0 +1,6 @@ +{ + monospace = "fira-code"; + sansSerif = "fira-sans"; + serif = "fira-sans"; + emoji = "noto-color"; +} diff --git a/modules/home/fonts/stacks/iosevka.nix b/modules/home/fonts/stacks/iosevka.nix new file mode 100644 index 00000000..5db16531 --- /dev/null +++ b/modules/home/fonts/stacks/iosevka.nix @@ -0,0 +1,6 @@ +{ + monospace = "iosevka-term"; + sansSerif = "iosevka-aile"; + serif = "iosevka-aile"; + emoji = "noto-color"; +} diff --git a/modules/home/ghostty/font.nix b/modules/home/ghostty/font.nix index 7f43a90a..b24e1c65 100644 --- a/modules/home/ghostty/font.nix +++ b/modules/home/ghostty/font.nix @@ -5,15 +5,15 @@ }: let - font = config.fonts.monospace; + font = config.modules.fonts.current.monospace; in { font-family = font.name; - font-size = font.size config "ghostty"; - font-style = font.normal; - font-style-bold = font.bold; - font-style-italic = font.italic; - font-style-bold-italic = font.bold_italic; + font-size = font.sizes "ghostty"; + font-style = font.styles.normal; + font-style-bold = font.styles.bold; + font-style-italic = font.styles.italic; + font-style-bold-italic = font.styles.boldItalic; } // lib.optionalAttrs isDarwin { font-thicken = true; diff --git a/modules/home/qutebrowser/default.nix b/modules/home/qutebrowser/default.nix index c2a81993..00b9daa9 100644 --- a/modules/home/qutebrowser/default.nix +++ b/modules/home/qutebrowser/default.nix @@ -179,11 +179,11 @@ in fonts = let - font = config.fonts.sansSerif; + font = config.modules.fonts.current.sansSerif; in { default_family = font.name; - default_size = toString (font.size config "qutebrowser") + "pt"; + default_size = toString (font.sizes "qutebrowser") + "pt"; }; hints = { diff --git a/modules/home/zathura/default.nix b/modules/home/zathura/default.nix index 77a3575b..3d3bd4ea 100644 --- a/modules/home/zathura/default.nix +++ b/modules/home/zathura/default.nix @@ -32,8 +32,8 @@ in font = let - font = config.fonts.serif; - size = toString (font.size config "zathura"); + font = config.modules.fonts.current.serif; + size = toString (font.sizes "zathura"); in "${font.name} ${size}"; diff --git a/modules/home/zed/settings.nix b/modules/home/zed/settings.nix index f8c188d6..f9b66434 100644 --- a/modules/home/zed/settings.nix +++ b/modules/home/zed/settings.nix @@ -5,14 +5,14 @@ }: let - bufferFont = config.fonts.monospace; + bufferFont = config.modules.fonts.current.monospace; preferredLineLength = 80; in { auto_update = false; autosave = "on_focus_change"; buffer_font_family = bufferFont.name; - buffer_font_size = bufferFont.size config "zed"; + buffer_font_size = bufferFont.sizes "zed"; current_line_highlight = "none"; cursor_blink = false; diagnostics.inline.enabled = true; From 02b8b5289fa0b5fc238f8ecf8376b0f92247fc4a Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Fri, 20 Mar 2026 15:01:48 +0100 Subject: [PATCH 2/5] wip --- flake.nix | 2 +- modules/flake/machines/default.nix | 4 +- modules/home/fonts/default.nix | 207 ++++---------------------- modules/home/fonts/font-module.nix | 83 +++++++++++ modules/home/fonts/stacks/blex.nix | 15 +- modules/home/fonts/stacks/default.nix | 105 +++++++++++++ modules/home/fonts/stacks/docsrs.nix | 15 +- modules/home/fonts/stacks/fira.nix | 15 +- modules/home/fonts/stacks/iosevka.nix | 15 +- modules/home/ghostty/font.nix | 2 +- modules/home/qutebrowser/default.nix | 10 +- modules/home/zathura/default.nix | 2 +- modules/home/zed/settings.nix | 2 +- 13 files changed, 271 insertions(+), 206 deletions(-) create mode 100644 modules/home/fonts/font-module.nix create mode 100644 modules/home/fonts/stacks/default.nix diff --git a/flake.nix b/flake.nix index c90129d5..fbf537af 100644 --- a/flake.nix +++ b/flake.nix @@ -64,7 +64,7 @@ _module.args = { colorscheme = "gruvbox"; - fontStack = "iosevka"; + fontstack = "iosevka"; username = "noib3"; }; diff --git a/modules/flake/machines/default.nix b/modules/flake/machines/default.nix index dce6309a..86ecaf4a 100644 --- a/modules/flake/machines/default.nix +++ b/modules/flake/machines/default.nix @@ -3,7 +3,7 @@ config, lib, colorscheme, - fontStack, + fontstack, username, ... }: @@ -105,7 +105,7 @@ in home.username = username; machines.current.name = machineName; modules.colorscheme.${colorscheme}.enable = true; - modules.fonts.${fontStack}.enable = true; + modules.fonts.stacks.${fontstack}.enable = true; } ]; diff --git a/modules/home/fonts/default.nix b/modules/home/fonts/default.nix index dc9dcd42..6321a3e2 100644 --- a/modules/home/fonts/default.nix +++ b/modules/home/fonts/default.nix @@ -9,6 +9,9 @@ with lib; let cfg = config.modules.fonts; + fontModule = import ./font-module.nix { inherit lib; }; + inherit (fontModule) fontSubmodule; + # The set of font categories, mapping directory names to option keys. categories = { monospace = "monospace"; @@ -17,135 +20,33 @@ let emoji = "emoji"; }; - # A sizes attrset that uses __functor to fall back to `default` for any - # program that doesn't have an explicit override. - mkSizesFunctor = - sizesAttr: - let - overrides = removeAttrs sizesAttr [ "default" ]; - in - { - inherit (sizesAttr) default; - __functor = self: program: overrides.${program} or self.default; - } - // overrides; - - fontSubmodule = types.submodule { - options = { - name = mkOption { - type = types.str; - example = "IosevkaTerm Nerd Font"; - description = "The font family name as known to fontconfig"; - }; - - package = mkOption { - type = types.package; - example = "pkgs.nerd-fonts.iosevka-term"; - description = "The Nix package providing the font"; - }; - - styles = { - normal = mkOption { - type = types.str; - default = "Regular"; - description = "The normal font style"; - }; - - bold = mkOption { - type = types.str; - default = "Bold"; - description = "The bold font style"; - }; - - italic = mkOption { - type = types.str; - default = "Italic"; - description = "The italic font style"; - }; - - boldItalic = mkOption { - type = types.str; - default = "Bold Italic"; - description = "The bold italic font style"; - }; - }; - - sizes = mkOption { - type = types.attrsOf types.float; - example = { - default = 16.5; - ghostty = 14.0; - }; - description = '' - Font sizes keyed by program name, with a mandatory `default` entry. - Accessed as a functor: `font.sizes "ghostty"` returns the - ghostty-specific size if defined, otherwise the default. - ''; - }; - }; - }; - - # Auto-discover font .nix files in a category directory. + # Auto-discover .nix files in a directory (excluding default.nix). discoverNames = dir: builtins.readDir dir |> attrNames - |> filter (n: hasSuffix ".nix" n && n != "default.nix") + |> filter (n: hasSuffix ".nix" n && n != "default.nix" && n != "font-module.nix") |> map (removeSuffix ".nix"); - - # Auto-discover stack names. - stackNames = discoverNames ./stacks; - - mkStackOption = - stackName: - mkOption { - type = types.submodule { - options = { - enable = mkEnableOption stackName; - - name = mkOption { - type = types.str; - default = stackName; - readOnly = true; - description = "The name of the font stack"; - }; - - fonts = mkOption { - type = types.attrsOf types.str; - default = import ./stacks/${stackName}.nix; - readOnly = true; - description = '' - Mapping from category to font name, e.g. - `{ monospace = "iosevka-term"; sansSerif = "iosevka-aile"; ... }`. - ''; - }; - }; - }; - default = { }; - }; - - stackOptions = - stackNames - |> map (name: nameValuePair name (mkStackOption name)) - |> listToAttrs; in { - # Import all individual font modules from category directories. imports = - categories - |> mapAttrsToList ( - dirName: _categoryKey: - let - dir = ./${dirName}; - names = discoverNames dir; - in - map (n: dir + "/${n}.nix") names - ) - |> concatLists; + let + fontImports = + categories + |> mapAttrsToList ( + dirName: _: + let + dir = ./${dirName}; + in + discoverNames dir |> map (n: dir + "/${n}.nix") + ) + |> concatLists; + in + [ ./stacks ] ++ fontImports; - options.modules.fonts = stackOptions // { - # Per-category lists of all declared fonts. Individual font modules append - # to these via `config.modules.fonts.`. + options.modules.fonts = { + # Per-category attrsets of all declared fonts. Individual font modules + # append to these via `config.modules.fonts..`. monospace = mkOption { type = types.attrsOf fontSubmodule; default = { }; @@ -169,75 +70,27 @@ in default = { }; description = "All declared emoji fonts"; }; - - current = mkOption { - type = types.attrs; - readOnly = true; - description = "The resolved font configuration for the enabled stack"; - }; }; config = let - enabledStacks = - removeAttrs cfg [ - "current" - "monospace" - "sansSerif" - "serif" - "emoji" - ] - |> mapAttrsToList (_: v: v) - |> filter (s: s.enable); - - enabledStack = head enabledStacks; - stackDef = enabledStack.fonts; - - # All declared fonts merged into a single flat attrset. This allows - # stacks to reference fonts across categories (e.g. using a monospace - # font for the sans-serif slot). - allFonts = cfg.monospace // cfg.sansSerif // cfg.serif // cfg.emoji; - - resolveFont = - fontName: - let - fontDef = allFonts.${fontName}; - in - { - inherit (fontDef) name package styles; - sizes = mkSizesFunctor fontDef.sizes; - }; + current = cfg.stacks.current; in { - assertions = [ - { - assertion = length enabledStacks == 1; - message = "Exactly one font stack must be enabled"; - } - ]; - - modules.fonts.current = { - name = enabledStack.name; - monospace = resolveFont stackDef.monospace; - sansSerif = resolveFont stackDef.sansSerif; - serif = resolveFont stackDef.serif; - emoji = resolveFont stackDef.emoji; - }; - home.packages = unique [ - cfg.current.monospace.package - cfg.current.sansSerif.package - cfg.current.serif.package - cfg.current.emoji.package + current.monospace.package + current.sansSerif.package + current.serif.package + current.emoji.package ]; fonts.fontconfig = { enable = pkgs.stdenv.isLinux; defaultFonts = { - serif = [ cfg.current.serif.name ]; - sansSerif = [ cfg.current.sansSerif.name ]; - monospace = [ cfg.current.monospace.name ]; - emoji = [ cfg.current.emoji.name ]; + serif = [ current.serif.name ]; + sansSerif = [ current.sansSerif.name ]; + monospace = [ current.monospace.name ]; + emoji = [ current.emoji.name ]; }; }; }; diff --git a/modules/home/fonts/font-module.nix b/modules/home/fonts/font-module.nix new file mode 100644 index 00000000..6c9f0e56 --- /dev/null +++ b/modules/home/fonts/font-module.nix @@ -0,0 +1,83 @@ +# Shared type definitions for the font module system. +{ lib }: + +with lib; +let + fontSubmodule = types.submodule { + options = { + name = mkOption { + type = types.str; + example = "IosevkaTerm Nerd Font"; + description = "The font family name as known to fontconfig"; + }; + + package = mkOption { + type = types.package; + example = "pkgs.nerd-fonts.iosevka-term"; + description = "The Nix package providing the font"; + }; + + styles = { + normal = mkOption { + type = types.str; + default = "Regular"; + description = "The normal font style"; + }; + + bold = mkOption { + type = types.str; + default = "Bold"; + description = "The bold font style"; + }; + + italic = mkOption { + type = types.str; + default = "Italic"; + description = "The italic font style"; + }; + + boldItalic = mkOption { + type = types.str; + default = "Bold Italic"; + description = "The bold italic font style"; + }; + }; + + sizes = mkOption { + type = types.attrsOf types.float; + example = { + default = 16.5; + ghostty = 14.0; + }; + description = '' + Font sizes keyed by program name, with a mandatory `default` entry. + Accessed as a functor: `font.sizes "ghostty"` returns the + ghostty-specific size if defined, otherwise the default. + ''; + }; + }; + }; + + # A sizes attrset that uses __functor to fall back to `default` for any + # program that doesn't have an explicit override. + mkSizesFunctor = + sizesAttr: + let + overrides = removeAttrs sizesAttr [ "default" ]; + in + { + inherit (sizesAttr) default; + __functor = self: program: overrides.${program} or self.default; + } + // overrides; + + wrapFont = + fontDef: + fontDef + // { + sizes = mkSizesFunctor fontDef.sizes; + }; +in +{ + inherit fontSubmodule mkSizesFunctor wrapFont; +} diff --git a/modules/home/fonts/stacks/blex.nix b/modules/home/fonts/stacks/blex.nix index 65461296..b4ef4159 100644 --- a/modules/home/fonts/stacks/blex.nix +++ b/modules/home/fonts/stacks/blex.nix @@ -1,6 +1,13 @@ +{ config, ... }: + +let + fonts = config.modules.fonts; +in { - monospace = "blex-mono"; - sansSerif = "blex-mono"; - serif = "blex-mono"; - emoji = "noto-color"; + modules.fonts.stacks.blex = { + monospace = fonts.monospace.blex-mono; + sansSerif = fonts.monospace.blex-mono; + serif = fonts.monospace.blex-mono; + emoji = fonts.emoji.noto-color; + }; } diff --git a/modules/home/fonts/stacks/default.nix b/modules/home/fonts/stacks/default.nix new file mode 100644 index 00000000..2a4fd38b --- /dev/null +++ b/modules/home/fonts/stacks/default.nix @@ -0,0 +1,105 @@ +{ + config, + lib, + ... +}: + +with lib; +let + cfg = config.modules.fonts; + fontModule = import ../font-module.nix { inherit lib; }; + inherit (fontModule) fontSubmodule wrapFont; + + stackSubmodule = types.submodule { + options = { + enable = mkEnableOption "this font stack"; + + monospace = mkOption { + type = fontSubmodule; + description = "The monospace font for this stack"; + }; + + sansSerif = mkOption { + type = fontSubmodule; + description = "The sans-serif font for this stack"; + }; + + serif = mkOption { + type = fontSubmodule; + description = "The serif font for this stack"; + }; + + emoji = mkOption { + type = fontSubmodule; + description = "The emoji font for this stack"; + }; + }; + }; +in +{ + imports = [ + ./blex.nix + ./docsrs.nix + ./fira.nix + ./iosevka.nix + ]; + + options.modules.fonts.stacks = mkOption { + type = types.submodule { + freeformType = types.attrsOf stackSubmodule; + + options.current = mkOption { + type = types.attrs; + readOnly = true; + description = '' + The resolved font configuration for the enabled stack, with sizes + wrapped in a functor for program-specific fallback access. + ''; + }; + }; + default = { }; + description = '' + Font stacks, each mapping categories to specific fonts. + `stacks.current` is reserved for the resolved enabled stack. + ''; + }; + + config = + let + stacks = + assert + !(removeAttrs cfg.stacks [ "current" ] ? "current") + || throw '' + A font stack cannot be named "current" because + `modules.fonts.stacks.current` is reserved for the enabled stack. + ''; + removeAttrs cfg.stacks [ "current" ]; + + enabledStacks = + stacks + |> mapAttrsToList ( + name: stack: { + inherit name; + value = stack; + } + ) + |> filter (s: s.value.enable); + + enabled = head enabledStacks; + in + { + assertions = [ + { + assertion = length enabledStacks == 1; + message = "Exactly one font stack must be enabled"; + } + ]; + + modules.fonts.stacks.current = { + monospace = wrapFont enabled.value.monospace; + sansSerif = wrapFont enabled.value.sansSerif; + serif = wrapFont enabled.value.serif; + emoji = wrapFont enabled.value.emoji; + }; + }; +} diff --git a/modules/home/fonts/stacks/docsrs.nix b/modules/home/fonts/stacks/docsrs.nix index 3269d475..8759dbc9 100644 --- a/modules/home/fonts/stacks/docsrs.nix +++ b/modules/home/fonts/stacks/docsrs.nix @@ -1,6 +1,13 @@ +{ config, ... }: + +let + fonts = config.modules.fonts; +in { - monospace = "source-code-pro"; - sansSerif = "fira-sans"; - serif = "source-serif"; - emoji = "noto-color"; + modules.fonts.stacks.docsrs = { + monospace = fonts.monospace.source-code-pro; + sansSerif = fonts.sansSerif.fira-sans; + serif = fonts.serif.source-serif; + emoji = fonts.emoji.noto-color; + }; } diff --git a/modules/home/fonts/stacks/fira.nix b/modules/home/fonts/stacks/fira.nix index 35b66d2d..2a68358b 100644 --- a/modules/home/fonts/stacks/fira.nix +++ b/modules/home/fonts/stacks/fira.nix @@ -1,6 +1,13 @@ +{ config, ... }: + +let + fonts = config.modules.fonts; +in { - monospace = "fira-code"; - sansSerif = "fira-sans"; - serif = "fira-sans"; - emoji = "noto-color"; + modules.fonts.stacks.fira = { + monospace = fonts.monospace.fira-code; + sansSerif = fonts.sansSerif.fira-sans; + serif = fonts.sansSerif.fira-sans; + emoji = fonts.emoji.noto-color; + }; } diff --git a/modules/home/fonts/stacks/iosevka.nix b/modules/home/fonts/stacks/iosevka.nix index 5db16531..ac71ad55 100644 --- a/modules/home/fonts/stacks/iosevka.nix +++ b/modules/home/fonts/stacks/iosevka.nix @@ -1,6 +1,13 @@ +{ config, ... }: + +let + fonts = config.modules.fonts; +in { - monospace = "iosevka-term"; - sansSerif = "iosevka-aile"; - serif = "iosevka-aile"; - emoji = "noto-color"; + modules.fonts.stacks.iosevka = { + monospace = fonts.monospace.iosevka-term; + sansSerif = fonts.sansSerif.iosevka-aile; + serif = fonts.sansSerif.iosevka-aile; + emoji = fonts.emoji.noto-color; + }; } diff --git a/modules/home/ghostty/font.nix b/modules/home/ghostty/font.nix index b24e1c65..150d097f 100644 --- a/modules/home/ghostty/font.nix +++ b/modules/home/ghostty/font.nix @@ -5,7 +5,7 @@ }: let - font = config.modules.fonts.current.monospace; + font = config.modules.fonts.stacks.current.monospace; in { font-family = font.name; diff --git a/modules/home/qutebrowser/default.nix b/modules/home/qutebrowser/default.nix index 00b9daa9..116a3d6e 100644 --- a/modules/home/qutebrowser/default.nix +++ b/modules/home/qutebrowser/default.nix @@ -9,9 +9,7 @@ with lib; let cfg = config.modules.qutebrowser; colors = import ./colors.nix { inherit config; }; - add-torrent = pkgs.writeShellScriptBin "add-torrent" ( - builtins.readFile ./scripts/add-torrent.sh - ); + add-torrent = pkgs.writeShellScriptBin "add-torrent" (builtins.readFile ./scripts/add-torrent.sh); homePage = "https://start.duckduckgo.com"; in { @@ -23,9 +21,7 @@ in programs.qutebrowser = { enable = true; - package = lib.mkIf pkgs.stdenv.isLinux ( - pkgs.qutebrowser.override { enableWideVine = true; } - ); + package = lib.mkIf pkgs.stdenv.isLinux (pkgs.qutebrowser.override { enableWideVine = true; }); searchEngines = { "DEFAULT" = "https://duckduckgo.com/?q={}"; @@ -179,7 +175,7 @@ in fonts = let - font = config.modules.fonts.current.sansSerif; + font = config.modules.fonts.stacks.current.sansSerif; in { default_family = font.name; diff --git a/modules/home/zathura/default.nix b/modules/home/zathura/default.nix index 3d3bd4ea..aa6d9829 100644 --- a/modules/home/zathura/default.nix +++ b/modules/home/zathura/default.nix @@ -32,7 +32,7 @@ in font = let - font = config.modules.fonts.current.serif; + font = config.modules.fonts.stacks.current.serif; size = toString (font.sizes "zathura"); in "${font.name} ${size}"; diff --git a/modules/home/zed/settings.nix b/modules/home/zed/settings.nix index f9b66434..28c71b6f 100644 --- a/modules/home/zed/settings.nix +++ b/modules/home/zed/settings.nix @@ -5,7 +5,7 @@ }: let - bufferFont = config.modules.fonts.current.monospace; + bufferFont = config.modules.fonts.stacks.current.monospace; preferredLineLength = 80; in { From 80b6be0bfc75be212f11598bd8e2fa0b466354c6 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Fri, 20 Mar 2026 15:30:12 +0100 Subject: [PATCH 3/5] wip --- modules/home/fonts/default.nix | 41 +++-------- modules/home/fonts/emoji/default.nix | 3 + modules/home/fonts/font-module.nix | 83 ----------------------- modules/home/fonts/font-submodule.nix | 57 ++++++++++++++++ modules/home/fonts/monospace/default.nix | 10 +++ modules/home/fonts/sans-serif/default.nix | 6 ++ modules/home/fonts/serif/default.nix | 3 + modules/home/fonts/stacks/default.nix | 23 ++++++- 8 files changed, 108 insertions(+), 118 deletions(-) create mode 100644 modules/home/fonts/emoji/default.nix delete mode 100644 modules/home/fonts/font-module.nix create mode 100644 modules/home/fonts/font-submodule.nix create mode 100644 modules/home/fonts/monospace/default.nix create mode 100644 modules/home/fonts/sans-serif/default.nix create mode 100644 modules/home/fonts/serif/default.nix diff --git a/modules/home/fonts/default.nix b/modules/home/fonts/default.nix index 6321a3e2..0170f091 100644 --- a/modules/home/fonts/default.nix +++ b/modules/home/fonts/default.nix @@ -8,41 +8,16 @@ with lib; let cfg = config.modules.fonts; - - fontModule = import ./font-module.nix { inherit lib; }; - inherit (fontModule) fontSubmodule; - - # The set of font categories, mapping directory names to option keys. - categories = { - monospace = "monospace"; - sans-serif = "sansSerif"; - serif = "serif"; - emoji = "emoji"; - }; - - # Auto-discover .nix files in a directory (excluding default.nix). - discoverNames = - dir: - builtins.readDir dir - |> attrNames - |> filter (n: hasSuffix ".nix" n && n != "default.nix" && n != "font-module.nix") - |> map (removeSuffix ".nix"); + fontSubmodule = import ./font-submodule.nix { inherit lib; }; in { - imports = - let - fontImports = - categories - |> mapAttrsToList ( - dirName: _: - let - dir = ./${dirName}; - in - discoverNames dir |> map (n: dir + "/${n}.nix") - ) - |> concatLists; - in - [ ./stacks ] ++ fontImports; + imports = [ + ./emoji + ./monospace + ./sans-serif + ./serif + ./stacks + ]; options.modules.fonts = { # Per-category attrsets of all declared fonts. Individual font modules diff --git a/modules/home/fonts/emoji/default.nix b/modules/home/fonts/emoji/default.nix new file mode 100644 index 00000000..8408711b --- /dev/null +++ b/modules/home/fonts/emoji/default.nix @@ -0,0 +1,3 @@ +{ + imports = [ ./noto-color.nix ]; +} diff --git a/modules/home/fonts/font-module.nix b/modules/home/fonts/font-module.nix deleted file mode 100644 index 6c9f0e56..00000000 --- a/modules/home/fonts/font-module.nix +++ /dev/null @@ -1,83 +0,0 @@ -# Shared type definitions for the font module system. -{ lib }: - -with lib; -let - fontSubmodule = types.submodule { - options = { - name = mkOption { - type = types.str; - example = "IosevkaTerm Nerd Font"; - description = "The font family name as known to fontconfig"; - }; - - package = mkOption { - type = types.package; - example = "pkgs.nerd-fonts.iosevka-term"; - description = "The Nix package providing the font"; - }; - - styles = { - normal = mkOption { - type = types.str; - default = "Regular"; - description = "The normal font style"; - }; - - bold = mkOption { - type = types.str; - default = "Bold"; - description = "The bold font style"; - }; - - italic = mkOption { - type = types.str; - default = "Italic"; - description = "The italic font style"; - }; - - boldItalic = mkOption { - type = types.str; - default = "Bold Italic"; - description = "The bold italic font style"; - }; - }; - - sizes = mkOption { - type = types.attrsOf types.float; - example = { - default = 16.5; - ghostty = 14.0; - }; - description = '' - Font sizes keyed by program name, with a mandatory `default` entry. - Accessed as a functor: `font.sizes "ghostty"` returns the - ghostty-specific size if defined, otherwise the default. - ''; - }; - }; - }; - - # A sizes attrset that uses __functor to fall back to `default` for any - # program that doesn't have an explicit override. - mkSizesFunctor = - sizesAttr: - let - overrides = removeAttrs sizesAttr [ "default" ]; - in - { - inherit (sizesAttr) default; - __functor = self: program: overrides.${program} or self.default; - } - // overrides; - - wrapFont = - fontDef: - fontDef - // { - sizes = mkSizesFunctor fontDef.sizes; - }; -in -{ - inherit fontSubmodule mkSizesFunctor wrapFont; -} diff --git a/modules/home/fonts/font-submodule.nix b/modules/home/fonts/font-submodule.nix new file mode 100644 index 00000000..ded9a563 --- /dev/null +++ b/modules/home/fonts/font-submodule.nix @@ -0,0 +1,57 @@ +{ lib }: + +with lib; +types.submodule { + options = { + name = mkOption { + type = types.str; + example = "IosevkaTerm Nerd Font"; + description = "The font family name as known to fontconfig"; + }; + + package = mkOption { + type = types.package; + example = "pkgs.nerd-fonts.iosevka-term"; + description = "The Nix package providing the font"; + }; + + styles = { + normal = mkOption { + type = types.str; + default = "Regular"; + description = "The normal font style"; + }; + + bold = mkOption { + type = types.str; + default = "Bold"; + description = "The bold font style"; + }; + + italic = mkOption { + type = types.str; + default = "Italic"; + description = "The italic font style"; + }; + + boldItalic = mkOption { + type = types.str; + default = "Bold Italic"; + description = "The bold italic font style"; + }; + }; + + sizes = mkOption { + type = types.attrsOf types.float; + example = { + default = 16.5; + ghostty = 14.0; + }; + description = '' + Font sizes keyed by program name, with a mandatory `default` entry. + Accessed as a functor: `font.sizes "ghostty"` returns the + ghostty-specific size if defined, otherwise the default. + ''; + }; + }; +} diff --git a/modules/home/fonts/monospace/default.nix b/modules/home/fonts/monospace/default.nix new file mode 100644 index 00000000..b1dcde77 --- /dev/null +++ b/modules/home/fonts/monospace/default.nix @@ -0,0 +1,10 @@ +{ + imports = [ + ./blex-mono.nix + ./fira-code.nix + ./inconsolata.nix + ./iosevka-term.nix + ./jetbrains-mono.nix + ./source-code-pro.nix + ]; +} diff --git a/modules/home/fonts/sans-serif/default.nix b/modules/home/fonts/sans-serif/default.nix new file mode 100644 index 00000000..a5f8e977 --- /dev/null +++ b/modules/home/fonts/sans-serif/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./fira-sans.nix + ./iosevka-aile.nix + ]; +} diff --git a/modules/home/fonts/serif/default.nix b/modules/home/fonts/serif/default.nix new file mode 100644 index 00000000..549302e1 --- /dev/null +++ b/modules/home/fonts/serif/default.nix @@ -0,0 +1,3 @@ +{ + imports = [ ./source-serif.nix ]; +} diff --git a/modules/home/fonts/stacks/default.nix b/modules/home/fonts/stacks/default.nix index 2a4fd38b..8a0b9df3 100644 --- a/modules/home/fonts/stacks/default.nix +++ b/modules/home/fonts/stacks/default.nix @@ -7,8 +7,27 @@ with lib; let cfg = config.modules.fonts; - fontModule = import ../font-module.nix { inherit lib; }; - inherit (fontModule) fontSubmodule wrapFont; + fontSubmodule = import ../font-submodule.nix { inherit lib; }; + + # A sizes attrset that uses __functor to fall back to `default` for any + # program that doesn't have an explicit override. + mkSizesFunctor = + sizesAttr: + let + overrides = removeAttrs sizesAttr [ "default" ]; + in + { + inherit (sizesAttr) default; + __functor = self: program: overrides.${program} or self.default; + } + // overrides; + + wrapFont = + fontDef: + fontDef + // { + sizes = mkSizesFunctor fontDef.sizes; + }; stackSubmodule = types.submodule { options = { From eb8d03cf6aadd457ddc1aa3cf7f25fc77f0100a5 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Fri, 20 Mar 2026 15:39:39 +0100 Subject: [PATCH 4/5] wip --- modules/home/fonts/default.nix | 2 - modules/home/fonts/font-submodule.nix | 14 +-- modules/home/fonts/stacks/default.nix | 132 +++++++++----------------- 3 files changed, 55 insertions(+), 93 deletions(-) diff --git a/modules/home/fonts/default.nix b/modules/home/fonts/default.nix index 0170f091..50772f19 100644 --- a/modules/home/fonts/default.nix +++ b/modules/home/fonts/default.nix @@ -20,8 +20,6 @@ in ]; options.modules.fonts = { - # Per-category attrsets of all declared fonts. Individual font modules - # append to these via `config.modules.fonts..`. monospace = mkOption { type = types.attrsOf fontSubmodule; default = { }; diff --git a/modules/home/fonts/font-submodule.nix b/modules/home/fonts/font-submodule.nix index ded9a563..755a2781 100644 --- a/modules/home/fonts/font-submodule.nix +++ b/modules/home/fonts/font-submodule.nix @@ -42,16 +42,18 @@ types.submodule { }; sizes = mkOption { - type = types.attrsOf types.float; + type = types.submodule { + freeformType = types.attrsOf types.float; + options.default = mkOption { + type = types.float; + description = "The default font size, used when no program-specific override exists"; + }; + }; example = { default = 16.5; ghostty = 14.0; }; - description = '' - Font sizes keyed by program name, with a mandatory `default` entry. - Accessed as a functor: `font.sizes "ghostty"` returns the - ghostty-specific size if defined, otherwise the default. - ''; + description = "Font sizes keyed by program name"; }; }; } diff --git a/modules/home/fonts/stacks/default.nix b/modules/home/fonts/stacks/default.nix index 8a0b9df3..c7a6370b 100644 --- a/modules/home/fonts/stacks/default.nix +++ b/modules/home/fonts/stacks/default.nix @@ -7,53 +7,34 @@ with lib; let cfg = config.modules.fonts; - fontSubmodule = import ../font-submodule.nix { inherit lib; }; - # A sizes attrset that uses __functor to fall back to `default` for any - # program that doesn't have an explicit override. - mkSizesFunctor = - sizesAttr: + stackSubmodule = let - overrides = removeAttrs sizesAttr [ "default" ]; + fontSubmodule = import ../font-submodule.nix { inherit lib; }; in - { - inherit (sizesAttr) default; - __functor = self: program: overrides.${program} or self.default; - } - // overrides; - - wrapFont = - fontDef: - fontDef - // { - sizes = mkSizesFunctor fontDef.sizes; - }; - - stackSubmodule = types.submodule { - options = { - enable = mkEnableOption "this font stack"; - - monospace = mkOption { - type = fontSubmodule; - description = "The monospace font for this stack"; - }; - - sansSerif = mkOption { - type = fontSubmodule; - description = "The sans-serif font for this stack"; - }; - - serif = mkOption { - type = fontSubmodule; - description = "The serif font for this stack"; - }; - - emoji = mkOption { - type = fontSubmodule; - description = "The emoji font for this stack"; + types.submodule { + options = { + enable = mkEnableOption "this font stack"; + monospace = mkOption { + type = fontSubmodule; + description = "The monospace font for this stack"; + }; + sansSerif = mkOption { + type = fontSubmodule; + description = "The sans-serif font for this stack"; + }; + serif = mkOption { + type = fontSubmodule; + description = "The serif font for this stack"; + }; + emoji = mkOption { + type = fontSubmodule; + description = "The emoji font for this stack"; + }; }; }; - }; + + enabledStacks = removeAttrs cfg.stacks [ "current" ] |> filterAttrs (_: stack: stack.enable); in { imports = [ @@ -66,59 +47,40 @@ in options.modules.fonts.stacks = mkOption { type = types.submodule { freeformType = types.attrsOf stackSubmodule; - options.current = mkOption { type = types.attrs; readOnly = true; description = '' - The resolved font configuration for the enabled stack, with sizes - wrapped in a functor for program-specific fallback access. + The resolved font configuration for the enabled stack. ''; }; }; default = { }; - description = '' - Font stacks, each mapping categories to specific fonts. - `stacks.current` is reserved for the resolved enabled stack. - ''; }; - config = - let - stacks = - assert - !(removeAttrs cfg.stacks [ "current" ] ? "current") - || throw '' - A font stack cannot be named "current" because - `modules.fonts.stacks.current` is reserved for the enabled stack. - ''; - removeAttrs cfg.stacks [ "current" ]; - - enabledStacks = - stacks - |> mapAttrsToList ( - name: stack: { - inherit name; - value = stack; - } - ) - |> filter (s: s.value.enable); + config = { + assertions = [ + { + assertion = (enabledStacks |> attrNames |> length) == 1; + message = "Exactly one font stack must be enabled"; + } + ]; - enabled = head enabledStacks; - in - { - assertions = [ - { - assertion = length enabledStacks == 1; - message = "Exactly one font stack must be enabled"; + modules.fonts.stacks.current = + enabledStacks + |> attrValues + |> head + # Turn the `sizes` attrset of all the fonts on the enabled stack into + # a functor which takes a program name and returns the font size for that + # program (e.g. `font.sizes "ghostty"`). + |> mapAttrs ( + _category: font: + font + // { + sizes = { + __functor = _: program: font.sizes.${program} or font.sizes.default; + }; } - ]; - - modules.fonts.stacks.current = { - monospace = wrapFont enabled.value.monospace; - sansSerif = wrapFont enabled.value.sansSerif; - serif = wrapFont enabled.value.serif; - emoji = wrapFont enabled.value.emoji; - }; - }; + ); + }; } From 42acf8a383c02a784bb5e305a1a10be34bdcb2fd Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Fri, 20 Mar 2026 15:58:42 +0100 Subject: [PATCH 5/5] formatting & lints --- modules/flake/machines/default.nix | 12 ++++-------- modules/home/fonts/stacks/default.nix | 3 ++- modules/home/qutebrowser/default.nix | 8 ++++++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/flake/machines/default.nix b/modules/flake/machines/default.nix index 86ecaf4a..a7626d74 100644 --- a/modules/flake/machines/default.nix +++ b/modules/flake/machines/default.nix @@ -90,22 +90,18 @@ in cfg |> lib.mapAttrs ( machineName: machine: - inputs.home-manager.lib.homeManagerConfiguration rec { - pkgs = import inputs.nixpkgs { - inherit (machine) system; - overlays = [ - inputs.brew-nix.overlays.default - ]; - }; + inputs.home-manager.lib.homeManagerConfiguration { + pkgs = inputs.nixpkgs.legacyPackages.${machine.system}; modules = [ - homeManagerMachinesModule ../../home + homeManagerMachinesModule { home.username = username; machines.current.name = machineName; modules.colorscheme.${colorscheme}.enable = true; modules.fonts.stacks.${fontstack}.enable = true; + nixpkgs.overlays = [ inputs.brew-nix.overlays.default ]; } ]; diff --git a/modules/home/fonts/stacks/default.nix b/modules/home/fonts/stacks/default.nix index c7a6370b..9356d50a 100644 --- a/modules/home/fonts/stacks/default.nix +++ b/modules/home/fonts/stacks/default.nix @@ -34,7 +34,8 @@ let }; }; - enabledStacks = removeAttrs cfg.stacks [ "current" ] |> filterAttrs (_: stack: stack.enable); + enabledStacks = + removeAttrs cfg.stacks [ "current" ] |> filterAttrs (_: stack: stack.enable); in { imports = [ diff --git a/modules/home/qutebrowser/default.nix b/modules/home/qutebrowser/default.nix index 116a3d6e..a038563a 100644 --- a/modules/home/qutebrowser/default.nix +++ b/modules/home/qutebrowser/default.nix @@ -9,7 +9,9 @@ with lib; let cfg = config.modules.qutebrowser; colors = import ./colors.nix { inherit config; }; - add-torrent = pkgs.writeShellScriptBin "add-torrent" (builtins.readFile ./scripts/add-torrent.sh); + add-torrent = pkgs.writeShellScriptBin "add-torrent" ( + builtins.readFile ./scripts/add-torrent.sh + ); homePage = "https://start.duckduckgo.com"; in { @@ -21,7 +23,9 @@ in programs.qutebrowser = { enable = true; - package = lib.mkIf pkgs.stdenv.isLinux (pkgs.qutebrowser.override { enableWideVine = true; }); + package = lib.mkIf pkgs.stdenv.isLinux ( + pkgs.qutebrowser.override { enableWideVine = true; } + ); searchEngines = { "DEFAULT" = "https://duckduckgo.com/?q={}";