diff --git a/common/fonts.nix b/common/fonts.nix
index 80d3a04..056e62a 100644
--- a/common/fonts.nix
+++ b/common/fonts.nix
@@ -1,11 +1,5 @@
 { pkgs, ... }: {
   stylix.fonts = {
-
-    # monospace = {
-    #   name = "Cascadia Code";
-    #   package = pkgs.cascadia-code;
-    # };
-
     monospace = {
       name = "Iosevka";
       package = pkgs.iosevka;
@@ -21,8 +15,9 @@
       package = pkgs.cm_unicode;
     };
 
-    # Why would you not want sansSerif
-    # (that's what I used to think, but I have since changed my mind)
-    # serif = sansSerif;
+    sizes = {
+      desktop = 14;
+      applications = 17;
+    };
   };
 }
diff --git a/common/themes/default.nix b/common/themes/default.nix
index b082d3f..bd8aa19 100644
--- a/common/themes/default.nix
+++ b/common/themes/default.nix
@@ -1,15 +1,23 @@
 { inputs, ... }:
 let
+  transparency = amount: {
+    desktop = 1.0;
+    applications = amount;
+    terminal = amount;
+    popups = amount;
+  };
+
   themes = {
     # {{{ Catppuccin variants
     catppuccin-mocha = {
       stylix = {
         image = ./wallpapers/breaking_phos.jpg;
         base16Scheme = "${inputs.catppuccin-base16}/base16/mocha.yaml";
+        opacity = transparency 0.7;
         polarity = "dark";
       };
+
       satellite = {
-        transparency.alpha = 0.7;
         rounding.radius = 8.0;
       };
     };
@@ -18,10 +26,11 @@ let
       stylix = {
         image = ./wallpapers/happy_phos.png;
         base16Scheme = "${inputs.catppuccin-base16}/base16/latte.yaml";
+        opacity = transparency 0.6;
         polarity = "light";
       };
+
       satellite = {
-        transparency.alpha = 0.6;
         rounding.radius = 8.0;
       };
     };
@@ -30,10 +39,10 @@ let
       stylix = {
         image = ./wallpapers/lapis_lazuli.jpg;
         base16Scheme = "${inputs.catppuccin-base16}/base16/macchiato.yaml";
+        opacity = transparency 0.7;
         polarity = "dark";
       };
       satellite = {
-        transparency.alpha = 0.7;
         rounding.radius = 8.0;
       };
     };
@@ -53,10 +62,11 @@ let
       stylix = {
         image = ./wallpapers/watercag.png;
         base16Scheme = ./schemes/bluloco-light.yaml;
+        opacity = transparency 0.6;
         polarity = "light";
       };
+
       satellite = {
-        transparency.alpha = 0.6;
         rounding.radius = 8.0;
       };
     };
@@ -76,10 +86,11 @@ let
         stylix = {
           image = ./wallpapers/spaceship.jpg;
           base16Scheme = ./schemes/gpt-themes/purplepink-light.yaml;
+          transparency.alpha = 0.6;
           polarity = "light";
         };
+
         satellite = {
-          transparency.alpha = 0.6;
           rounding.radius = 8.0;
         };
       };
diff --git a/home/features/cli/fish/default.nix b/home/features/cli/fish/default.nix
index 3508aeb..e6f171f 100644
--- a/home/features/cli/fish/default.nix
+++ b/home/features/cli/fish/default.nix
@@ -20,7 +20,12 @@
   # {{{ Fish
   programs.fish = {
     enable = true;
-    interactiveShellInit = builtins.readFile ./config.fish;
+    interactiveShellInit = ''
+      ${builtins.readFile ./config.fish}
+
+      # Modify nix-shell to use `fish` as it's default shell
+      ${lib.getExe pkgs.nix-your-shell} fish | source
+    '';
 
     # {{{ Plugins 
     plugins =
diff --git a/home/features/desktop/dunst.nix b/home/features/desktop/dunst.nix
index 5a08fa8..da6e563 100644
--- a/home/features/desktop/dunst.nix
+++ b/home/features/desktop/dunst.nix
@@ -1,6 +1,4 @@
 {
-  # TODO: use base16 theme
-  services.dunst = {
-    enable = true;
-  };
+  services.dunst.enable = true;
+  stylix.targets.hyprland.enable = true;
 }
diff --git a/home/features/desktop/wezterm/default.nix b/home/features/desktop/wezterm/default.nix
index d9c3734..38fddc3 100644
--- a/home/features/desktop/wezterm/default.nix
+++ b/home/features/desktop/wezterm/default.nix
@@ -2,8 +2,10 @@
   # REASON: the unstable version crashes on launch
   home.packages = [ inputs.wezterm.packages.${pkgs.system}.default ];
 
-  # Create link to config
-  xdg.configFile."wezterm/colorscheme.lua".text = config.satellite.colorscheme.lua;
+  xdg.configFile."wezterm/nix".source =
+    config.satellite.lib.lua.writeFile
+      "." "colorscheme"
+      config.satellite.colorscheme.lua;
   xdg.configFile."wezterm/wezterm.lua".source =
     config.satellite.dev.path "home/features/desktop/wezterm/wezterm.lua";
 }
diff --git a/home/features/desktop/wezterm/wezterm.lua b/home/features/desktop/wezterm/wezterm.lua
index 99e2bb4..702fe5f 100644
--- a/home/features/desktop/wezterm/wezterm.lua
+++ b/home/features/desktop/wezterm/wezterm.lua
@@ -1,6 +1,6 @@
 -- {{{ Import stuff & create config object
 local wezterm = require("wezterm")
-local colorscheme = require("colorscheme") -- injected by nix!
+local colorscheme = require("nix.colorscheme") -- injected by nix!
 
 -- This table will hold the configuration.
 local config = {}
@@ -19,7 +19,7 @@ config.colors = wezterm.color.load_base16_scheme(colorscheme.source)
 
 -- {{{ Window frame
 config.window_frame = {
-  font = wezterm.font({ family = colorscheme.fonts.monospace }),
+  font = wezterm.font({ family = colorscheme.fonts.sansSerif }),
   font_size = font_size,
   active_titlebar_bg = colorscheme.base00,
   inactive_titlebar_bg = colorscheme.base00,
@@ -52,7 +52,7 @@ config.colors.tab_bar = {
 }
 -- }}}
 -- {{{ Other visual things
-config.window_background_opacity = colorscheme.transparency.value
+config.window_background_opacity = colorscheme.opacity.terminal
 -- }}}
 -- }}}
 -- {{{ Main config options
@@ -62,6 +62,7 @@ config.check_for_updates = false
 -- {{{ Fonts
 config.adjust_window_size_when_changing_font_size = false -- Makes it work with fixed window sizes.
 config.font_size = font_size
+config.font = wezterm.font(colorscheme.fonts.monospace)
 -- }}}
 -- {{{ Tab bar
 config.use_fancy_tab_bar = true
@@ -69,12 +70,21 @@ config.use_fancy_tab_bar = true
 config.hide_tab_bar_if_only_one_tab = true
 -- }}}
 -- {{{ Keycodes
-config.disable_default_key_bindings = true
+config.disable_default_key_bindings = false
 -- config.enable_kitty_keyboard = true -- Let's apps recognise more distinct keys
 config.enable_csi_u_key_encoding = true -- For some reason I need this for all keybinds to work inside neovim.
 -- }}}
 -- }}}
 -- {{{ Keybinds
+config.keys = {
+  -- {{{ Disable certain default keybinds
+  {
+    key = "f",
+    mods = "CTRL|SHIFT",
+    action = wezterm.action.DisableDefaultAssignment,
+  },
+  -- }}}
+}
 -- }}}
 
 -- and finally, return the configuration to wezterm
diff --git a/home/features/neovim/config/lua/my/neovide.lua b/home/features/neovim/config/lua/my/neovide.lua
index b560dd3..50a62a4 100644
--- a/home/features/neovim/config/lua/my/neovide.lua
+++ b/home/features/neovim/config/lua/my/neovide.lua
@@ -4,7 +4,7 @@ function M.setup()
   local default_length = 0.04
   -- vim.g.neovide_floating_blur_amount_x = 10.0
   -- vim.g.neovide_floating_blur_amount_y = 10.0
-  vim.g.neovide_transparency = 0.5
+  vim.g.neovide_transparency = require("nix.theme").opacity.applications
   -- vim.g.transparency = 0.6
   -- vim.g.pumblend = 0
   vim.g.neovide_cursor_animation_length = default_length
diff --git a/home/features/neovim/config/lua/my/plugins/themes/bluloco.lua b/home/features/neovim/config/lua/my/plugins/themes/bluloco.lua
index f7f8491..ba898f0 100644
--- a/home/features/neovim/config/lua/my/plugins/themes/bluloco.lua
+++ b/home/features/neovim/config/lua/my/plugins/themes/bluloco.lua
@@ -12,7 +12,7 @@ function M.config()
   local bluloco = require("bluloco")
 
   bluloco.setup({
-    transparent = T.transparency.enable,
+    transparent = T.opacity.terminal < 1.0,
     style = H.variant("Bluloco"),
   })
 
diff --git a/home/features/neovim/config/lua/my/plugins/themes/catppuccin.lua b/home/features/neovim/config/lua/my/plugins/themes/catppuccin.lua
index f38140b..6f04a8d 100644
--- a/home/features/neovim/config/lua/my/plugins/themes/catppuccin.lua
+++ b/home/features/neovim/config/lua/my/plugins/themes/catppuccin.lua
@@ -13,7 +13,7 @@ function M.config()
   vim.g.catppuccin_flavour = H.variant("Catppuccin")
 
   catppuccin.setup({
-    transparent_background = T.transparency.enable,
+    transparent_background = T.transparent.terminal,
     integrations = { nvimtree = true, telescope = true },
   })
 
@@ -22,7 +22,7 @@ function M.config()
 
   vim.cmd("colorscheme catppuccin")
 
-  if T.transparency.enable then
+  if T.transparent.terminal then
     vim.cmd([[highlight FloatBorder blend=0]])
   end
 end
diff --git a/home/features/neovim/default.nix b/home/features/neovim/default.nix
index edfc51f..f68fd44 100644
--- a/home/features/neovim/default.nix
+++ b/home/features/neovim/default.nix
@@ -78,8 +78,8 @@ let
   # {{{ extraRuntime
   extraRuntimePaths = env: [
     # Base16 theme
-    (pkgs.writeTextDir
-      "lua/nix/theme.lua"
+    (config.satellite.lib.lua.writeFile
+      "lua/nix" "theme"
       config.satellite.colorscheme.lua
     )
 
@@ -210,7 +210,7 @@ in
     };
   # }}}
   # {{{ Plugins
-  satellite.neovim.styluaConfig = ../../../stylua.toml;
+  satellite.lua.styluaConfig = ../../../stylua.toml;
   satellite.neovim.runtime = {
     env = "my.helpers.env";
     languageServerOnAttach = "my.plugins.lspconfig";
diff --git a/home/features/wayland/hyprland/default.nix b/home/features/wayland/hyprland/default.nix
index b71843e..19fe334 100644
--- a/home/features/wayland/hyprland/default.nix
+++ b/home/features/wayland/hyprland/default.nix
@@ -1,4 +1,4 @@
-{ pkgs, lib, config, inputs, ... }:
+{ pkgs, lib, config, ... }:
 
 let
   hyprland-monitors = lib.concatStringsSep "\n" (lib.forEach config.satellite.monitors (m: ''
@@ -9,10 +9,10 @@ in
 {
   imports = [ ../global.nix ./hyprpaper.nix ];
 
+  stylix.targets.hyprland.enable = true;
   wayland.windowManager.hyprland = {
     enable = true;
     package = pkgs.hyprland;
-    settings = { };
     extraConfig = ''
       ${builtins.readFile ./hyprland.conf}
       ${hyprland-monitors}
diff --git a/modules/common/default.nix b/modules/common/default.nix
index 99affe4..995487e 100644
--- a/modules/common/default.nix
+++ b/modules/common/default.nix
@@ -2,6 +2,7 @@
 
 {
   # example = import ./example.nix;
+  lua-encoders = import ./lua-encoders.nix;
   lua-colorscheme = import ./lua-colorscheme.nix;
   theming = import ./theming.nix;
   toggles = import ./toggles.nix;
diff --git a/modules/common/lua-colorscheme.nix b/modules/common/lua-colorscheme.nix
index 87474e9..9383b7c 100644
--- a/modules/common/lua-colorscheme.nix
+++ b/modules/common/lua-colorscheme.nix
@@ -1,43 +1,56 @@
 # Lua file containing the current colorscheme.
+# TODO: use the lua encoders I've written for neovim
 { lib, config, ... }: {
   options.satellite.colorscheme.lua = lib.mkOption {
     type = lib.types.lines;
     description = "Lua file containing the current colorscheme";
   };
 
-  config.satellite.colorscheme.lua = /* lua */ ''
-    return {
-      name = "${config.lib.stylix.scheme.scheme}",
-      base00 = "${config.lib.stylix.scheme.base00}",
-      base01 = "${config.lib.stylix.scheme.base01}",
-      base02 = "${config.lib.stylix.scheme.base02}",
-      base03 = "${config.lib.stylix.scheme.base03}",
-      base04 = "${config.lib.stylix.scheme.base04}",
-      base05 = "${config.lib.stylix.scheme.base05}",
-      base06 = "${config.lib.stylix.scheme.base06}",
-      base07 = "${config.lib.stylix.scheme.base07}",
-      base08 = "${config.lib.stylix.scheme.base07}",
-      base09 = "${config.lib.stylix.scheme.base09}",
-      base0A = "${config.lib.stylix.scheme.base0A}",
-      base0B = "${config.lib.stylix.scheme.base0B}",
-      base0C = "${config.lib.stylix.scheme.base0C}",
-      base0D = "${config.lib.stylix.scheme.base0D}",
-      base0E = "${config.lib.stylix.scheme.base0E}",
-      base0F = "${config.lib.stylix.scheme.base0F}",
-      -- TODO: check if this works with the genetic algorithm
-      source = "${config.stylix.base16Scheme}",
-      fonts = {
-        normal = "${config.stylix.fonts.sansSerif.name}",
-        monospace = "${config.stylix.fonts.monospace.name}"
-      },
-      transparency = {
-        enable = ${toString config.satellite.theming.transparency.enable} == 1,
-        value = ${toString config.satellite.theming.transparency.alpha},
-      },
-      rounding = {
-        enable = ${toString config.satellite.theming.rounding.enable} == 1,
-        radius = ${toString config.satellite.theming.rounding.radius},
-      }
-    }
-  '';
+  config.satellite.colorscheme.lua =
+    let
+      theme = {
+        name = config.lib.stylix.scheme.scheme;
+        base00 = config.lib.stylix.scheme.base00;
+        base01 = config.lib.stylix.scheme.base01;
+        base02 = config.lib.stylix.scheme.base02;
+        base03 = config.lib.stylix.scheme.base03;
+        base04 = config.lib.stylix.scheme.base04;
+        base05 = config.lib.stylix.scheme.base05;
+        base06 = config.lib.stylix.scheme.base06;
+        base07 = config.lib.stylix.scheme.base07;
+        base08 = config.lib.stylix.scheme.base07;
+        base09 = config.lib.stylix.scheme.base09;
+        base0A = config.lib.stylix.scheme.base0A;
+        base0B = config.lib.stylix.scheme.base0B;
+        base0C = config.lib.stylix.scheme.base0C;
+        base0D = config.lib.stylix.scheme.base0D;
+        base0E = config.lib.stylix.scheme.base0E;
+        base0F = config.lib.stylix.scheme.base0F;
+        # TODO: check if this works with the genetic algorithm
+        source = config.stylix.base16Scheme;
+        fonts = {
+          serif = config.stylix.fonts.serif.name;
+          sansSerif = config.stylix.fonts.sansSerif.name;
+          monospace = config.stylix.fonts.monospace.name;
+        };
+        opacity = {
+          terminal = config.stylix.opacity.terminal;
+          applications = config.stylix.opacity.applications;
+          desktop = config.stylix.opacity.desktop;
+          popups = config.stylix.opacity.popups;
+        };
+        # Whether to enable transparency for different targets
+        transparent = {
+          terminal = config.stylix.opacity.terminal < 1.0;
+          applcations = config.stylix.opacity.applications < 1.0;
+          desktop = config.stylix.opacity.desktop < 1.0;
+          popups = config.stylix.opacity.popups < 1.0;
+        };
+        rounding = {
+          enable = toString config.satellite.theming.rounding.enable;
+          radius = toString config.satellite.theming.rounding.radius;
+        };
+      };
+    in
+    "return ${config.satellite.lib.lua.encoders.anything theme}";
 }
diff --git a/modules/common/lua-encoders.nix b/modules/common/lua-encoders.nix
new file mode 100644
index 0000000..1df6a0b
--- /dev/null
+++ b/modules/common/lua-encoders.nix
@@ -0,0 +1,184 @@
+{ config, lib, pkgs, ... }:
+let
+  # {{{ Lua encoders
+  # We provide a custom set of helpers for generating lua code for nix.enable
+  #
+  # An encoder is a function from some nix value to a string containing lua code. 
+  # This object provides combinators for writing such encoders.
+  luaEncoders = {
+    # {{{ "Raw" helpers 
+    mkRawLuaObject = chunks:
+      ''
+        {
+          ${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
+        }
+      '';
+    # }}}
+    # {{{ General helpers 
+    identity = given: given;
+    # `const` is mostly useful together with `bind`. See the lua encoder for 
+    # lazy modules for example usage.
+    const = code: _: code;
+    # Conceptually, this is the monadic bind operation for encoders.
+    # This implementation is isomoprhic to that of the reader monad in haskell.
+    bind = encoder: given: encoder given given;
+    # This is probably the most useful combinnator defined in this entire object.
+    # Most of the combinators in the other categories are based on this.
+    conditional = predicate: caseTrue: caseFalse:
+      luaEncoders.bind (given: if predicate given then caseTrue else caseFalse);
+    # This is simply left-composition of functions
+    map = f: encoder: given: encoder (f given);
+    # This is simply right-composition of functions
+    postmap = f: encoder: given: f (encoder given);
+    # This is mostly useful for debugging
+    trace = message: luaEncoders.map (f: lib.traceSeq message (lib.traceVal f));
+    fail = mkMessage: v: builtins.throw (mkMessage v);
+    # }}}
+    # {{{ Base types
+    string = given: ''"${lib.escape ["\"" "\\"] (toString given)}"'';
+    bool = bool: if bool then "true" else "false";
+    number = toString;
+    nil = _: "nil";
+    stringOr = luaEncoders.conditional lib.isString luaEncoders.string;
+    boolOr = luaEncoders.conditional lib.isBool luaEncoders.bool;
+    numberOr = luaEncoders.conditional (e: lib.isFloat e || lib.isInt e) luaEncoders.number;
+    nullOr = luaEncoders.conditional (e: e == null) luaEncoders.nil;
+    # We pipe a combinator which always fail through a bunch of
+    # `(thing)or : encoder -> encoder` functions, building up a combinator which
+    # can handle more and more kinds of values, until we eventually build up
+    # something that should be able to handle everything we throw at it.
+    anything = lib.pipe (luaEncoders.fail (v: "Cannot figure out how to encode value ${builtins.toJSON v}")) [
+      (luaEncoders.attrsetOfOr luaEncoders.anything)
+      (luaEncoders.listOfOr luaEncoders.anything)
+      luaEncoders.nullOr
+      luaEncoders.boolOr
+      luaEncoders.numberOr
+      luaEncoders.stringOr
+      luaEncoders.luaCodeOr # Lua code expressions have priority over attrsets
+    ];
+    # }}}
+    # {{{ Lua code
+    # Tagged lua code can be combined with other combinators without worrying
+    # about conflicts regarding how strings are interpreted.
+    luaCodeOr =
+      luaEncoders.conditional (e: lib.isAttrs e && (e.__luaEncoderTag or null) == "lua")
+        (obj: obj.value);
+    # This is the most rudimentary (and currently only) way of handling paths.
+    luaImportOr = tag:
+      luaEncoders.conditional lib.isPath
+        (path: "dofile(${luaEncoders.string path}).${tag}");
+    # Accepts both tagged and untagged strings of lua code.
+    luaString = luaEncoders.luaCodeOr luaEncoders.identity;
+    # This simply combines the above combinators into one.
+    luaCode = tag: luaEncoders.luaImportOr tag luaEncoders.luaString;
+    # }}}
+    # {{{ Lists
+    listOf = encoder: list:
+      luaEncoders.mkRawLuaObject (lib.lists.map encoder list);
+    listOfOr = encoder:
+      luaEncoders.conditional
+        lib.isList
+        (luaEncoders.listOf encoder);
+    # Returns nil when given empty lists
+    tryNonemptyList = encoder: luaEncoders.conditional
+      (l: l == [ ])
+      luaEncoders.nil
+      (luaEncoders.listOf encoder);
+    oneOrMany = encoder: luaEncoders.listOfOr encoder encoder;
+    # Can encode:
+    # - zero values as nil
+    # - one value as itself
+    # - multiple values as a list
+    zeroOrMany = encoder: luaEncoders.nullOr (luaEncoders.oneOrMany encoder);
+    # Coerces non list values to lists of one element.
+    oneOrManyAsList = encoder: luaEncoders.map
+      (given: if lib.isList given then given else [ given ])
+      (luaEncoders.listOf encoder);
+    # Coerces lists of one element to said element.
+    listAsOneOrMany = encoder:
+      luaEncoders.map
+        (l: if lib.length l == 1 then lib.head l else l)
+        (luaEncoders.oneOrMany encoder);
+    # }}}
+    # {{{ Attrsets
+    attrsetOf = encoder: object:
+      luaEncoders.mkRawLuaObject (lib.mapAttrsToList
+        (name: value:
+          let result = encoder value;
+          in
+          lib.optionalString (result != "nil")
+            "${name} = ${result}"
+        )
+        object
+      );
+    attrsetOfOr = of: luaEncoders.conditional lib.isAttrs (luaEncoders.attrsetOf of);
+    # This is the most general combinator provided in this section.
+    #
+    # We accept:
+    # - a `noNils` flag which will automatically remove any nil properties
+    # - order of props that should be interpreted as list elements
+    # - spec of props that should be interpreted as list elements
+    # - record of props that should be interpreted as attribute props
+    attrset = noNils: listOrder: spec: attrset:
+      let
+        shouldKeep = given:
+          if noNils then
+            given != "nil"
+          else
+            true;
+
+        listChunks = lib.lists.map
+          (attr:
+            let result = spec.${attr} (attrset.${attr} or null);
+            in
+            lib.optionalString (shouldKeep result) result
+          )
+          listOrder;
+        objectChunks = lib.mapAttrsToList
+          (attr: encoder:
+            let result = encoder (attrset.${attr} or null);
+            in
+            lib.optionalString (!(lib.elem attr listOrder) && shouldKeep result)
+              "${attr} = ${result}"
+          )
+          spec;
+      in
+      luaEncoders.mkRawLuaObject (listChunks ++ objectChunks);
+    # }}}
+  };
+  # }}}
+in
+{
+  options.satellite.lib.lua = {
+    encoders = lib.mkOption {
+      # I am too lazy to make this typecheck
+      type = lib.types.anything;
+      description = "Combinators used to encode nix values as lua values";
+    };
+
+    writeFile = lib.mkOption {
+      type = with lib.types; functionTo (functionTo (functionTo path));
+      description = "Format and write a lua file to disk";
+    };
+  };
+
+  options.satellite.lua.styluaConfig = lib.mkOption {
+    type = lib.types.path;
+    description = "Config to use for formatting lua modules";
+  };
+
+  config.satellite.lib.lua = {
+    encoders = luaEncoders;
+
+    writeFile = path: name: text:
+      let
+        destination = "${path}/${name}.lua";
+        unformatted = pkgs.writeText "raw-lua-${name}" text;
+      in
+      pkgs.runCommand "formatted-lua-${name}" { } ''
+        mkdir -p $out/${path}
+        cp --no-preserve=mode ${unformatted} $out/${destination}
+        ${lib.getExe pkgs.stylua} --config-path ${config.satellite.lua.styluaConfig} $out/${destination}
+      '';
+  };
+}
diff --git a/modules/common/neovim.nix b/modules/common/neovim.nix
index 1a5b154..0be7c94 100644
--- a/modules/common/neovim.nix
+++ b/modules/common/neovim.nix
@@ -4,6 +4,7 @@
 let
   inherit (lib) types;
 
+  e = config.satellite.lib.lua.encoders;
   cfg = config.satellite.neovim;
 
   # {{{ Custom types 
@@ -341,169 +342,6 @@ let
     # }}}
   };
   # }}}
-  # {{{ Lua encoders
-  # We provide a custom set of helpers for generating lua code for nix.enable
-  #
-  # An encoder is a function from some nix value to a string containing lua code. 
-  # This object provides combinators for writing such encoders.
-  luaEncoders = {
-    # {{{ "Raw" helpers 
-    mkRawLuaObject = chunks:
-      ''
-        {
-          ${lib.concatStringsSep "," (lib.filter (s: s != "") chunks)}
-        }
-      '';
-    # }}}
-    # {{{ General helpers 
-    identity = given: given;
-    # `const` is mostly useful together with `bind`. See the lua encoder for 
-    # lazy modules for example usage.
-    const = code: _: code;
-    # Conceptually, this is the monadic bind operation for encoders.
-    # This implementation is isomoprhic to that of the reader monad in haskell.
-    bind = encoder: given: encoder given given;
-    # This is probably the most useful combinnator defined in this entire object.
-    # Most of the combinators in the other categories are based on this.
-    conditional = predicate: caseTrue: caseFalse:
-      luaEncoders.bind (given: if predicate given then caseTrue else caseFalse);
-    # This is simply left-composition of functions
-    map = f: encoder: given: encoder (f given);
-    # This is simply right-composition of functions
-    postmap = f: encoder: given: f (encoder given);
-    # This is mostly useful for debugging
-    trace = message: luaEncoders.map (f: lib.traceSeq message (lib.traceVal f));
-    fail = mkMessage: v: builtins.throw (mkMessage v);
-    # }}}
-    # {{{ Base types
-    string = given: ''"${lib.escape ["\"" "\\"] (toString given)}"'';
-    bool = bool: if bool then "true" else "false";
-    number = toString;
-    nil = _: "nil";
-    stringOr = luaEncoders.conditional lib.isString luaEncoders.string;
-    boolOr = luaEncoders.conditional lib.isBool luaEncoders.bool;
-    numberOr = luaEncoders.conditional (e: lib.isFloat e || lib.isInt e) luaEncoders.number;
-    nullOr = luaEncoders.conditional (e: e == null) luaEncoders.nil;
-    # We pipe a combinator which always fail through a bunch of
-    # `(thing)or : encoder -> encoder` functions, building up a combinator which
-    # can handle more and more kinds of values, until we eventually build up
-    # something that should be able to handle everything we throw at it.
-    anything = lib.pipe (luaEncoders.fail (v: "Cannot figure out how to encode value ${builtins.toJSON v}")) [
-      (luaEncoders.attrsetOfOr luaEncoders.anything)
-      (luaEncoders.listOfOr luaEncoders.anything)
-      luaEncoders.nullOr
-      luaEncoders.boolOr
-      luaEncoders.numberOr
-      luaEncoders.stringOr
-      luaEncoders.luaCodeOr # Lua code expressions have priority over attrsets
-    ];
-    # }}}
-    # {{{ Lua code
-    # Tagged lua code can be combined with other combinators without worrying
-    # about conflicts regarding how strings are interpreted.
-    luaCodeOr =
-      luaEncoders.conditional (e: lib.isAttrs e && (e.__luaEncoderTag or null) == "lua")
-        (obj: obj.value);
-    # This is the most rudimentary (and currently only) way of handling paths.
-    luaImportOr = tag:
-      luaEncoders.conditional lib.isPath
-        (path: "dofile(${luaEncoders.string path}).${tag}");
-    # Accepts both tagged and untagged strings of lua code.
-    luaString = luaEncoders.luaCodeOr luaEncoders.identity;
-    # This simply combines the above combinators into one.
-    luaCode = tag: luaEncoders.luaImportOr tag luaEncoders.luaString;
-    # }}}
-    # {{{ Lists
-    listOf = encoder: list:
-      luaEncoders.mkRawLuaObject (lib.lists.map encoder list);
-    listOfOr = encoder:
-      luaEncoders.conditional
-        lib.isList
-        (luaEncoders.listOf encoder);
-    # Returns nil when given empty lists
-    tryNonemptyList = encoder: luaEncoders.conditional
-      (l: l == [ ])
-      luaEncoders.nil
-      (luaEncoders.listOf encoder);
-    oneOrMany = encoder: luaEncoders.listOfOr encoder encoder;
-    # Can encode:
-    # - zero values as nil
-    # - one value as itself
-    # - multiple values as a list
-    zeroOrMany = encoder: luaEncoders.nullOr (luaEncoders.oneOrMany encoder);
-    # Coerces non list values to lists of one element.
-    oneOrManyAsList = encoder: luaEncoders.map
-      (given: if lib.isList given then given else [ given ])
-      (luaEncoders.listOf encoder);
-    # Coerces lists of one element to said element.
-    listAsOneOrMany = encoder:
-      luaEncoders.map
-        (l: if lib.length l == 1 then lib.head l else l)
-        (luaEncoders.oneOrMany encoder);
-    # }}}
-    # {{{ Attrsets
-    attrsetOf = encoder: object:
-      luaEncoders.mkRawLuaObject (lib.mapAttrsToList
-        (name: value:
-          let result = encoder value;
-          in
-          lib.optionalString (result != "nil")
-            "${name} = ${result}"
-        )
-        object
-      );
-    attrsetOfOr = of: luaEncoders.conditional lib.isAttrs (luaEncoders.attrsetOf of);
-    # This is the most general combinator provided in this section.
-    #
-    # We accept:
-    # - a `noNils` flag which will automatically remove any nil properties
-    # - order of props that should be interpreted as list elements
-    # - spec of props that should be interpreted as list elements
-    # - record of props that should be interpreted as attribute props
-    attrset = noNils: listOrder: spec: attrset:
-      let
-        shouldKeep = given:
-          if noNils then
-            given != "nil"
-          else
-            true;
-
-        listChunks = lib.lists.map
-          (attr:
-            let result = spec.${attr} (attrset.${attr} or null);
-            in
-            lib.optionalString (shouldKeep result) result
-          )
-          listOrder;
-        objectChunks = lib.mapAttrsToList
-          (attr: encoder:
-            let result = encoder (attrset.${attr} or null);
-            in
-            lib.optionalString (!(lib.elem attr listOrder) && shouldKeep result)
-              "${attr} = ${result}"
-          )
-          spec;
-      in
-      luaEncoders.mkRawLuaObject (listChunks ++ objectChunks);
-    # }}}
-  };
-
-  e = luaEncoders;
-  # }}}
-  # {{{ Helpers 
-  # Format and write a lua file to disk
-  writeLuaFile = path: name: text:
-    let
-      directory = "lua/${path}";
-      destination = "${directory}/${name}.lua";
-      unformatted = pkgs.writeText "raw-lua-${name}" text;
-    in
-    pkgs.runCommand "formatted-lua-${name}" { } ''
-      mkdir -p $out/${directory}
-      cp --no-preserve=mode ${unformatted} $out/${destination}
-      ${lib.getExe pkgs.stylua} --config-path ${cfg.styluaConfig} $out/${destination}
-    '';
-  # }}}
 in
 {
   # {{{ Option declaration 
@@ -563,7 +401,7 @@ in
       # }}}
       # {{{ Encoders 
       encode = lib.mkOption {
-        default = luaEncoders.anything;
+        default = e.anything;
         type = types.functionTo types.str;
         description = "Encode a nix value to a lua string";
       };
@@ -662,11 +500,6 @@ in
       };
     };
     # }}}
-
-    styluaConfig = lib.mkOption {
-      type = types.path;
-      description = "Config to use for formatting lua modules";
-    };
   };
   # }}}
   # {{{ Config generation 
@@ -738,7 +571,7 @@ in
     lib.attrsets.mapAttrs
       (name: opts: rec {
         raw = makeLazyScript opts;
-        module = writeLuaFile "nix/plugins" name raw;
+        module = config.satellite.lib.lua.writeFile "lua/nix/plugins" name raw;
       })
       cfg.lazy;
 
diff --git a/modules/common/theming.nix b/modules/common/theming.nix
index 90bb184..babcdd1 100644
--- a/modules/common/theming.nix
+++ b/modules/common/theming.nix
@@ -4,16 +4,6 @@ let cfg = config.satellite.theming;
 in
 {
   options.satellite.theming = {
-    transparency = {
-      enable = lib.mkEnableOption "transparency for desktop apps";
-      alpha = lib.mkOption {
-        description = "How transparent windows should be by default";
-        default = 1.0;
-        example = 0.6;
-        type = lib.types.float;
-      };
-    };
-
     rounding = {
       enable = lib.mkEnableOption "rounded corners for desktop apps";
       radius = lib.mkOption {
@@ -27,12 +17,11 @@ in
 
     get = lib.mkOption {
       # No generics:(
-      # The type of this is essentially (written in pseudocode):
+      # The type of this is essentially (written in ts-like -pseudocode):
       #
       # Record<String, T> 
-      #   & { default?: T
-      #         | {light?: T, dark?: T } }
-      # -> Option<T>
+      #   & { default?: T | {light?: T, dark?: T } }
+      #   -> Option<T>
       type = lib.types.functionTo lib.types.anything;
       description = "Index a theme map by the current theme";
     };
@@ -54,7 +43,6 @@ in
   };
 
   config.satellite.theming = {
-    transparency.enable = cfg.transparency.alpha < 1.0;
     rounding.enable = cfg.rounding.radius > 0.0;
 
     get = themeMap:
@@ -69,6 +57,6 @@ in
       config.lib.stylix.scheme."${color}-rgb-b"
     ];
 
-    colors.rgba = color: "${cfg.colors.rgb color},${toString cfg.transparency.alpha}";
+    colors.rgba = color: "${cfg.colors.rgb color},${toString config.stylix.opacity.applications}";
   };
 }