diff --git a/modules/misc/specialization.nix b/modules/misc/specialization.nix
new file mode 100644
index 00000000..67e593e2
--- /dev/null
+++ b/modules/misc/specialization.nix
@@ -0,0 +1,71 @@
+{ config, extendModules, lib, ... }:
+
+with lib;
+
+{
+ options.specialization = mkOption {
+ type = types.attrsOf (types.submodule {
+ options = {
+ configuration = mkOption {
+ type = let
+ stopRecursion = { specialization = mkOverride 0 { }; };
+ extended = extendModules { modules = [ stopRecursion ]; };
+ in extended.type;
+ default = { };
+ visible = "shallow";
+ description = ''
+ Arbitrary Home Manager configuration settings.
+ '';
+ };
+ };
+ });
+ default = { };
+ description = ''
+ A set of named specialized configurations. These can be used to extend
+ your base configuration with additional settings. For example, you can
+ have specializations named light
and dark
+ that applies light and dark color theme configurations.
+
+
+
+ Note, this is an experimental option for now and you therefore have to
+ activate the specialization by looking up and running the activation
+ script yourself. Note, running the activation script will create a new
+ Home Manager generation.
+
+
+
+ For example, to activate the dark
specialization. You can
+ first look up your current Home Manager generation by running
+
+
+ $ home-manager generations | head -1
+ 2022-05-02 22:49 : id 1758 -> /nix/store/jy…ac-home-manager-generation
+
+
+ then run
+
+
+ $ /nix/store/jy…ac-home-manager-generation/specialization/dark/activate
+ Starting Home Manager activation
+ …
+
+
+
+
+ WARNING! Since this option is experimental, the activation process may
+ change in backwards incompatible ways.
+ '';
+ };
+
+ config = mkIf (config.specialization != { }) {
+ home.extraBuilderCommands = let
+ link = n: v:
+ let pkg = v.configuration.home.activationPackage;
+ in "ln -s ${pkg} $out/specialization/${n}";
+ in ''
+ mkdir $out/specialization
+ ${concatStringsSep "\n" (mapAttrsToList link config.specialization)}
+ '';
+ };
+}
diff --git a/modules/modules.nix b/modules/modules.nix
index 91e1654d..51db04be 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -30,6 +30,7 @@ let
./misc/numlock.nix
./misc/pam.nix
./misc/qt.nix
+ ./misc/specialization.nix
./misc/submodule-support.nix
./misc/tmpfiles.nix
./misc/version.nix
diff --git a/tests/default.nix b/tests/default.nix
index 03a86aec..d89ebfbd 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -46,6 +46,7 @@ import nmt {
./modules/home-environment
./modules/misc/fontconfig
./modules/misc/nix
+ ./modules/misc/specialization
./modules/programs/alacritty
./modules/programs/alot
./modules/programs/aria2
diff --git a/tests/modules/misc/specialization/default.nix b/tests/modules/misc/specialization/default.nix
new file mode 100644
index 00000000..ddbc22c6
--- /dev/null
+++ b/tests/modules/misc/specialization/default.nix
@@ -0,0 +1 @@
+{ specialization = ./specialization.nix; }
diff --git a/tests/modules/misc/specialization/specialization.nix b/tests/modules/misc/specialization/specialization.nix
new file mode 100644
index 00000000..9d6149a0
--- /dev/null
+++ b/tests/modules/misc/specialization/specialization.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ home.file.testfile.text = "not special";
+ specialization.test.configuration = {
+ home.file.testfile.text = "very special";
+ };
+
+ nmt.script = ''
+ assertFileExists home-files/testfile
+ assertFileContains home-files/testfile "not special"
+
+ assertFileExists specialization/test/home-files/testfile
+ assertFileContains specialization/test/home-files/testfile "not special"
+ '';
+}