#67 hierarchy: nord subtext dimmer than text; tokyo-night btop Night blues; ethereal surface raise + muted darker than overlay; retro-82 surface raised; ristretto subtext distinct + btop main_bg align; flexoki-light distinct bright-black; gruvbox overlay/subtext ladder (no rename). #68 residual ANSI: vantablack ansi[8]=muted; osaka-jade ansi[3]=warn. Verified: python3 tools/check-theme-contrast.py themes/ → 24×7 all pass.
Nomarchy
The rock-solid reproducibility of NixOS 26.05. The out-of-the-box polish of Omarchy/Omakub. One JSON file rules the look of the entire desktop; every theme change is a Home Manager generation — atomic, rollbackable, never partial.
┌──────────────────────────────────────────────────────────────────────────┐
│ theme-state.json (single source of truth) │
│ lives INSIDE your flake checkout, git-tracked │
└───────────────────────────────────┬──────────────────────────────────────┘
│
nomarchy-theme-sync apply gruvbox
1. merges the preset into the JSON (atomic write)
2. runs `home-manager switch` (no sudo, no system rebuild)
│
▼ pure read (nomarchy.stateFile)
┌──────────────────────────────────────────────────────────────────────────┐
│ Home Manager bakes EVERYTHING into one read-only generation: │
│ Hyprland (colors/gaps/borders) Waybar (palette or whole-swap) │
│ Ghostty (full ANSI palette) btop (asset or generated) │
│ Stylix → GTK, Qt, cursors, fonts │
└───────────────────────────────────┬──────────────────────────────────────┘
▼
wallpaper via awww (the one runtime piece:
applied post-switch + at session start, `bg next` cycles)
1. Layout
Flat on purpose. Two module trees, one options file each, no hidden layers.
.
├── flake.nix # inputs + the downstream API (exports below)
├── lib.nix # nomarchy.lib.mkFlake — one-call downstream wrapper
├── theme-state.json # ★ THE single source of truth (git-tracked!)
├── themes/ # 24 presets: <slug>.json + optional <slug>/ assets
│ ├── nord.json # palette (required, works alone)
│ └── nord/ # assets (optional, fixed filenames)
│ ├── backgrounds/ # wallpapers (auto-picked, SUPER+SHIFT+T cycles)
│ ├── btop.theme # hand-made config drop (else generated)
│ └── waybar.css # whole-swap: replaces the generated bar style
├── modules/
│ ├── nixos/ # the distro, system side
│ │ ├── default.nix # Hyprland session, Pipewire, greetd, fonts
│ │ ├── options.nix # nomarchy.system.* toggles
│ │ ├── plymouth.nix # boot splash, tinted from the JSON
│ │ ├── file-manager.nix # Thunar GUI + gvfs/tumbler/udisks2
│ │ └── power.nix # power-profiles-daemon/TLP, thermald, charge limit
│ └── home/ # the distro, user side
│ ├── default.nix # entry point
│ ├── options.nix # nomarchy.* option surface
│ ├── theme.nix # JSON ingestion + wallpaper hook
│ ├── stylix.nix # GTK/Qt/cursors/fonts from the same JSON
│ ├── hyprland.nix # all JSON-driven
│ ├── waybar.nix
│ ├── ghostty.nix
│ ├── btop.nix
│ ├── rofi.nix # launcher + nomarchy-menu (calc, emoji, clip…)
│ ├── keybinds.nix # single source: Hyprland binds + SUPER+? sheet
│ ├── swaync.nix # notifications, same JSON
│ ├── idle.nix # hyprlock + hypridle, same JSON
│ ├── battery-notify.nix # low-battery toasts at the bar's thresholds
│ ├── yazi.nix # flagship TUI file manager + plugins
│ ├── osd.nix # swayosd volume/brightness OSD
│ ├── shell.nix # zsh + starship + bat/eza/zoxide
│ ├── keys.nix # gpg-agent fronting SSH, pinentry-qt
│ └── fastfetch.nix # themed Nomarchy logo + system info
├── hosts/
│ ├── default/ # reference machine (thin: boot, user, hostname)
│ └── live.nix # bootable live ISO (try the distro, no install)
├── pkgs/
│ ├── nomarchy-theme-sync/ # state writer + rebuild dispatcher (Python)
│ ├── nomarchy-install/ # live-ISO installer (gum + disko + mkFlake)
│ ├── nomarchy-doctor/ # read-only health sheet, one command per failure
│ ├── nomarchy-control-center/ # TUI settings front-end over nomarchy-theme-sync (gum)
│ └── nomarchy-battery-notify/ # low-battery toast watcher backing battery-notify.nix
├── templates/downstream/ # machine flake SoT (`flake init` + installer copy/patch)
├── docs/ # human docs — map: docs/README.md
│ ├── VISION.md # product themes toward v1.0 (not a queue)
│ ├── ROADMAP.md # design/decision records + shipped log
│ ├── HARDWARE.md # firmware, profiles, drivers
│ ├── TESTING.md · RECOVERY.md · OVERRIDES.md · MIGRATION.md
├── agent/ # agent loop state — map: agent/README.md
│ # BACKLOG (executable queue), LOOP, GOALS, …
├── CLAUDE.md # agent harness entry (points at agent/ + docs/)
├── .claude/ # Claude Code only: permissions + subagents
└── tools/ # maintainer-only
├── import-palettes.py # converts old-distro themes → JSON + assets
├── test-live-iso.sh # build the ISO + boot it in QEMU
├── test-install.sh # full offline-install regression in QEMU
└── vm/ # headless-VM helpers (QMP keys, VNC shots,
# offline-pin gap analysis)
Rule of thumb: modules/ is the distro (reusable, no machine specifics),
hosts/ is a machine, themes/ is data, pkgs/ is code, tools/ is
maintainer-only. If a new file doesn't obviously belong to one of those, it
probably shouldn't exist.
2. Try it first (live ISO)
Boot the full desktop from a USB stick or VM without installing anything:
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
# → result/iso/*.iso — dd to a stick, or boot it in QEMU:
tools/test-live-iso.sh
The live session auto-logs-in, seeds the flake at ~/.nomarchy, and pins
the locked inputs into the ISO store — so theme switching (including the
home-manager switch it triggers) works offline, exactly like on an
installed system. Verification checklist: docs/TESTING.md.
Like what you see? nomarchy-install (in a terminal) walks you through
installing to disk: pick a disk, LUKS2 full-disk encryption by default
(in exchange the desktop logs in passwordless — the passphrase already
gates the machine), user + hostname + timezone, hardware autodetection
(DMI → nixos-hardware profile — see docs/HARDWARE.md),
a hibernation-ready swapfile sized to RAM,
then disko partitions (GPT + ESP + BTRFS subvolumes incl. @snapshots —
snapper timeline snapshots are on) and nixos-install runs — without a
network when the ISO was built from a clean tree (the target's
flake.lock is composed from the rev the ISO carries). The installed
machine gets the standard downstream layout: the flake at ~/.nomarchy
(/etc/nixos symlinks to it), one mkFlake call, your
system.nix/home.nix. First boot lands in the fully themed desktop —
the installer pre-activates the Home Manager generation. UEFI only for now.
3. Using Nomarchy on your machine (downstream)
Nomarchy is consumed as a flake input — you never fork or edit this repo:
mkdir my-machine && cd my-machine
nix flake init -t "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1"
The template's own README then walks the first-run steps: a real
hardware-configuration.nix, git init (flakes only see tracked
files), the two first rebuilds, and landing the checkout at
~/.nomarchy so the theme CLI finds it. (The installer does all of
this for you.)
Already running NixOS on this machine? No reinstall needed — adopt
Nomarchy in place, reusing your existing hardware-configuration.nix:
docs/MIGRATION.md.
You own two files day-to-day: system.nix and home.nix (plus
theme-state.json, written by the CLI). Your flake.nix is set up once —
later by the installer — and never hand-edited; it's a single call:
outputs = { nomarchy, ... }:
nomarchy.lib.mkFlake {
src = ./.;
username = "me";
hardwareProfile = "framework-13-7040-amd"; # optional, nixos-hardware name
};
mkFlake arg |
Default | Purpose |
|---|---|---|
src |
— (required) | Your flake directory (./.) |
username |
— (required) | Login name; flows into system.nix and names the HM config |
hardwareProfile |
null |
One nixos-hardware module name, or a list of them (pinned + tested by Nomarchy; unknown names fail with suggestions) |
system |
"x86_64-linux" |
Platform |
(Power users can skip mkFlake and wire nixosModules.nomarchy /
homeModules.nomarchy / overlays.default by hand — the wrapper is sugar.)
Two deliberately separate rebuild paths:
sudo nixos-rebuild switch --flake .#default # system: rare
home-manager switch --flake .#me # desktop: every theme change, no sudo
Day-to-day you'll use the shipped shortcuts instead:
sys-update # nix flake update + system rebuild (BTRFS snapshot first when available)
sys-rebuild # system rebuild against the CURRENT lock (config changes only, no update)
# …both end with a package-level diff of what the rebuild changed (nvd)
home-update # home-manager switch (no flake update, no sudo)
Order matters when pulling distro updates. home-update does not
touch the lock — it rebuilds the desktop against the current
flake.lock. A new Nomarchy revision (new keybinds, theming, modules)
arrives only when the lock is updated, which sys-update does
(nix flake update). So to pull an update that affects the desktop layer:
run sys-update first (updates the lock + rebuilds the system), then
home-update (re-applies the desktop against the new lock). Doing them in
the other order rebuilds the desktop against the old inputs and silently
skips the new home-side changes. After a home-side keybind/config change,
also hyprctl reload (or relogin) so the running session re-reads it.
Override anything via the nomarchy.* surface or plain NixOS/HM options:
appearance (gaps/colors/fonts) changes through nomarchy-theme-sync,
behaviour (input/misc/monitor/chrome) is mkDefault so a plain home.nix
assignment wins, and bind/exec-once lists concatenate. Full guide with
examples: docs/OVERRIDES.md.
Where each option goes. nomarchy.system.*, nomarchy.hardware.*, and
nomarchy.services.* are NixOS options — set them in system.nix. Everything
else under nomarchy.* is a Home Manager option — set it in home.nix. The
two tables below are split along exactly that line.
home.nix (Home Manager — the desktop):
| Option | Default | Purpose |
|---|---|---|
nomarchy.stateFile |
— (required) | Path to your theme-state.json |
nomarchy.terminal |
"ghostty" |
Terminal for keybinds and $TERMINAL |
nomarchy.keyboard.layout |
"us" |
XKB layout for the Hyprland session (installer writes the matching services.xserver.xkb in system.nix; the console + LUKS prompt follow via the distro default) |
nomarchy.keyboard.variant |
"" |
XKB variant for the session |
nomarchy.keyboard.devices |
{} |
Per-device layout overrides (Hyprland device blocks keyed by hyprctl devices name) — e.g. an external keyboard with its own layout/variant |
nomarchy.keyboard.layouts |
[] |
Extra candidate layouts; when set, a watcher prompts (rofi) for a layout on a newly-connected keyboard and remembers it per-device |
nomarchy.nightlight.enable |
false |
Scheduled blue-light filter (hyprsunset) — warm at night (.temperature, default 4000K) between .sunset/.sunrise, no shift by day; off by default — enable it from the menu (System › Night light; the first enable rebuilds), then toggle on/off instantly from the menu or the Waybar moon indicator (writes settings.nightlight.on in your flake, no rebuild) and it survives reboot, so it stays reproducible |
nomarchy.updates.enable |
false |
Passive update awareness: a background check (.interval, default daily) that flags when flake inputs (nixpkgs, the Nomarchy input, …) are behind upstream — and, with Flatpak on, when apps have updates (.flatpak) — via a Waybar indicator + notification. Never changes anything; click the indicator to run the upgrade flow |
nomarchy.hyprland.enable |
true |
Nomarchy's Hyprland config |
nomarchy.waybar.enable |
true |
Nomarchy's Waybar |
nomarchy.rofi.enable |
true |
Themed rofi launcher + nomarchy-menu dispatcher |
nomarchy.swaync.enable |
true |
swaync notifications, themed |
nomarchy.batteryNotify.enable |
true |
Low-battery toasts at the bar's thresholds — 25% low, 10% critical (stays up until dismissed); silent no-op on machines without a battery |
nomarchy.idle.enable |
true |
hyprlock + hypridle (idle lock 5 min, display off 10, suspend 15 min — battery-only) |
nomarchy.yazi.enable |
true |
yazi TUI file manager, themed + curated plugins |
nomarchy.osd.enable |
true |
swayosd on-screen display for volume/brightness/mute |
nomarchy.shell.enable |
true |
zsh + starship prompt + bat/eza/zoxide (zsh is the default login shell) |
nomarchy.ghostty.enable |
true |
Nomarchy's Ghostty |
nomarchy.btop.enable |
true |
btop with per-theme colors |
nomarchy.stylix.enable |
true |
GTK/Qt/cursor theming |
nomarchy.fastfetch.enable |
true |
fastfetch fronted by the themed Nomarchy logo |
nomarchy.keys.enable |
true |
gpg-agent fronting SSH + pinentry-qt |
nomarchy.displays.enable |
true |
nwg-displays interactive monitor arranger (helper for nomarchy.monitors) |
nomarchy.viewers.enable |
true |
Document/image viewers: zathura (Stylix-themed PDF) + imv |
nomarchy.mime.enable |
true |
Default "open with" associations (PDF/image/video/text/browser/directory); entries for apps you removed are skipped, so it degrades with the suite |
nomarchy.monitors |
[] |
Declarative per-output layout → Hyprland monitor rules (applied on hotplug); ,preferred,auto,1 wildcard kept as fallback |
nomarchy.displayProfiles |
{} |
Named layouts for the same outputs (docked/undocked/…), switched from System › Display › Profiles: instant via hyprctl, persisted in-flake (settings.displayProfile), baked over nomarchy.monitors at the next rebuild. The menu's Auto-switch row (settings.displayProfileAuto) makes plugging/unplugging outputs apply the matching profile instantly (driven by a dedicated Hyprland IPC socket watcher, no polling). A profile can also pin workspaces to outputs (workspaces = { "1" = "DP-3"; }) — moved instantly on switch, baked as Hyprland workspace rules |
nomarchy.launchOrFocus |
[] |
Launch-or-focus binds: SUPER+<key> focuses the app's window (case-insensitive class match) or launches it; entries land in the SUPER+? cheatsheet, and a bind whose app was removed notifies instead of failing silently |
nomarchy.themesDir |
Nomarchy's themes/ |
Where per-theme app overrides are probed |
nomarchy.package |
overlay's nomarchy-theme-sync |
The theme/state tool package, overridable if you fork it |
Always-on, no toggle by design: services.cliphist, services.udiskie
(automount + safe-removal toasts) and services.easyeffects (mic noise
cancellation) ship unconditionally — small, low-risk pieces with no
Nomarchy-specific config behind them (the toggle-vs-package rule in
agent/CONVENTIONS.md). Disable one from home.nix with the plain HM
option, e.g. services.easyeffects.enable = lib.mkForce false; — see
docs/OVERRIDES.md.
system.nix (NixOS — the machine):
| Option | Default | Purpose |
|---|---|---|
nomarchy.system.plymouth.enable |
true |
Branded boot splash, background from the theme JSON (recolors on system rebuilds) |
nomarchy.system.stateFile |
null |
theme-state.json for the system-side consumers (the Plymouth tint); lib.mkFlake wires it for you — set it only when composing the modules by hand |
nomarchy.system.fileManager.enable |
true |
Thunar GUI + gvfs/tumbler/udisks2 (the "open folder" handler) |
nomarchy.system.greeter.enable |
true |
greetd/tuigreet |
nomarchy.system.greeter.autoLogin |
null |
Auto-login this user into Hyprland (installer sets it on LUKS machines) |
nomarchy.system.audio.enable |
true |
Pipewire stack |
nomarchy.system.bluetooth.enable |
true |
Bluetooth + blueman |
nomarchy.system.autoTimezone.enable |
false |
Automatic timezone (geoclue + automatic-timezoned) — the clock follows your location; toggle from System › Auto timezone (a menu enable rebuilds: it has to unset the static time.timeZone) |
nomarchy.system.snapper.enable |
false |
Hourly/daily BTRFS timeline snapshots + nixos-rebuild-snap (installer enables it; no-op unless root is BTRFS) |
nomarchy.system.power.enable |
true |
Active power management (see below) |
nomarchy.system.power.backend |
"ppd" |
"ppd" (power-profiles-daemon + menu/Waybar switcher) or "tlp" (deeper battery tuning, no switcher) — mutually exclusive |
nomarchy.system.power.laptop |
false |
Marks a laptop, gating battery-only features; the installer sets it when a battery is present |
nomarchy.system.power.thermal.enable |
false |
thermald (Intel-only); the installer enables it on a GenuineIntel CPU |
nomarchy.system.power.batteryChargeLimit |
null |
Stop charging at this % (e.g. 80) where the hardware supports it; needs power.laptop |
nomarchy.hardware.intel.enable |
false |
Intel enablement above nixos-hardware (GuC/HuC firmware via i915.enable_guc=3 — .guc toggles just that; the installer unsets it on xe-driver GPUs); the installer sets it on an Intel CPU/GPU |
nomarchy.hardware.intel.computeRuntime |
false |
Opt-in: Intel GPU compute — OpenCL/Level-Zero (intel-compute-runtime) + oneVPL (vpl-gpu-rt) |
nomarchy.hardware.amd.enable |
false |
AMD enablement above nixos-hardware (amd-pstate EPP + radeonsi VA-API, each toggleable via .pstate / .vaapi); installer-set on an AMD CPU/GPU |
nomarchy.hardware.amd.rocm.enable |
false |
Opt-in: ROCm HIP/OpenCL GPU compute (multi-GB); pair with .gfxOverride (e.g. "11.0.0") for an unlisted iGPU |
nomarchy.hardware.fingerprint.enable |
false |
fprintd for a detected fingerprint reader (installer-set); enroll with fprintd-enroll |
nomarchy.hardware.fingerprint.pam |
false |
Opt-in: use the fingerprint for login + sudo (PAM) |
nomarchy.hardware.npu.enable |
false |
Opt-in/experimental: load the on-die NPU driver (amdxdna/intel_vpu); userspace runtime is BYO |
nomarchy.hardware.latestKernel |
false |
Opt-in: ship linuxPackages_latest instead of the default kernel — for very new hardware whose drivers landed recently |
nomarchy.hardware.camera.hideIrSensor |
false |
Hide a dual-sensor webcam's IR node from PipeWire so apps only ever see the colour camera (the "second, dark Integrated Camera"); installer-set on a paired RGB+IR webcam. /dev/video* stays open, so Howdy-style face unlock still works; .irMatch overrides the IR-name regex |
nomarchy.hardware.i2c.enable |
false |
I2C devices support — access to /dev/i2c-* (RGB controllers, sensors, DDC/CI monitor control) |
nomarchy.hardware.i2c.ddcci |
false (distro default: true) |
the ddcci-driver kernel module, exposing external monitors as standard backlight devices via DDC/CI so brightness keys and swayosd natively control them |
nomarchy.services.tailscale.enable |
false |
Opt-in: Tailscale mesh VPN — the login user is made the operator, so tailscale up/down/set and the System › VPN menu work without sudo |
nomarchy.services.syncthing.enable |
false |
Opt-in: Syncthing file sync as the login user (GUI at 127.0.0.1:8384) |
nomarchy.services.podman.enable |
false |
Opt-in: rootless Podman (docker aliased to it) |
nomarchy.services.flatpak.enable |
false |
Opt-in: Flatpak + the Flathub remote |
nomarchy.services.pika.enable |
false |
Opt-in: Pika Backup (GUI Borg backups) |
nomarchy.services.steam.enable |
false |
Opt-in: Steam via programs.steam (32-bit libs, controller udev, Remote-Play ports) |
nomarchy.services.libvirt.enable |
false |
Opt-in: libvirt/KVM + virt-manager GUI (login user added to libvirtd) |
nomarchy.services.obs.enable |
false |
Opt-in: OBS Studio + a v4l2loopback virtual camera (selectable as a webcam in Zoom/Teams) |
nomarchy.services.docker.enable |
false |
Opt-in: rootful Docker (user in docker group); don't enable alongside podman |
nomarchy.services.kdeconnect.enable |
false |
Opt-in: KDE Connect phone integration (opens its firewall ports) |
nomarchy.services.gamemode.enable |
false |
Opt-in: Feral GameMode performance daemon (gamemoderun) |
nomarchy.services.adb.enable |
false |
Opt-in: Android adb/fastboot tools (systemd handles device udev rules) |
nomarchy.services.wireshark.enable |
false |
Opt-in: Wireshark Qt GUI (user in wireshark group, capture without root) |
nomarchy.services.ollama.enable |
false |
Opt-in: Ollama local LLM runtime on 127.0.0.1:11434 (CPU; GPU via services.ollama.acceleration) |
nomarchy.services.printing.enable |
false |
Opt-in: CUPS + Avahi/mDNS network printer discovery |
nomarchy.services.openrgb.enable |
false |
Opt-in: OpenRGB daemon for RGB peripheral/motherboard lighting |
nomarchy.services.restic.enable |
false |
Opt-in: scheduled daily restic backup (set .repository + .passwordFile; backs up .paths, default /home; 7/4/6 retention; list/restore via the restic-nomarchy wrapper) |
Beyond the nomarchy.* surface, the system layer turns on the usual
desktop services with lib.mkDefault (override natively) and enforces
baseline security/stability defaults. A few worth calling out:
security.apparmor.enable is on by default for mandatory access
control confinement. Kernel panics are configured to auto-reboot the
machine after 10 seconds (panic=10 oops=panic) rather than hanging
indefinitely. services.fwupd.enable is on by default for firmware
updates via LVFS — it only refreshes metadata, never flashes on its own,
so run fwupdmgr update to apply; disable with services.fwupd.enable = false; on machines without real firmware (VMs/headless). Full hardware
story (profiles, fingerprint, NVIDIA, unsupported machines, contributing
a DMI line): docs/HARDWARE.md. And
services.earlyoom is on by default so running out of memory kills
the offending process (with a desktop notification) instead of freezing
the desktop — process-level on purpose, since a Hyprland session is one
cgroup and systemd-oomd would kill all of it (oomd is disabled
accordingly). Opt out with services.earlyoom.enable = false;.
4. How theming works
Pure JSON ingestion
The trap with "read a mutable file from Nix" is pure evaluation: flakes
cannot read arbitrary $HOME paths without --impure (the old prototype
required it — never again). Nomarchy's convention: the state file lives
inside the consuming flake and is wired via
nomarchy.stateFile = ./theme-state.json;. Reading it is pure — it's flake
source. It must be git-tracked (nomarchy-theme-sync runs
git add --intent-to-add after every write as a safety net).
One change = one generation
nomarchy-theme-sync apply <theme> merges the preset into the JSON and runs
home-manager switch (override the command with $NOMARCHY_REBUILD, or pass
--no-switch to only write). Everything is baked: Hyprland, Waybar, Ghostty,
btop, and — via Stylix, mapped onto base16 roles — GTK, Qt, cursors and
fonts. No runtime patching means no partial states, and home-manager generations is also your theme history. Waybar even restyles in place: it
re-reads style.css when the symlink flips.
The wallpaper is the one runtime piece (awww — nixpkgs' swww — is
imperative; nothing in Nix consumes the path): applied at session start and
after every switch via a tiny activation hook, cycled instantly with
bg next.
Config the menu writes (not just themes)
The in-flake-state model isn't only for appearance. Feature toggles you flip
from the menu are written into a settings.* section of the same state file
— git-tracked, reproducible, never stashed in ~/.local/state. The menu is just
an ergonomic writer for your flake, so version-controlling your downstream
reproduces the machine, settings and all. Where a toggle can take effect without
a rebuild it does: the menu writes the key (--no-switch) and flips the running
service, which reads the live flake state at session start, so the choice is
both instant and survives reboot.
Night light is the first to use this — enable it from the menu (System ›
Night light; the first enable rebuilds to install hyprsunset), then on/off is
instant. Internally it's two keys: settings.nightlight.installed (sticky —
gates the unit, the first enable rebuilds) and settings.nightlight.on (the
instant on/off). Expect more nomarchy.* toggles to migrate to this pattern.
Auto-commit (opt-in): System › Auto-commit makes every apply/set
mutation also git commit theme-state.json in your flake — only that
file, so unrelated dirty work is never swept up — turning your settings
history into git log. Off by default; the toggle is instant (nothing in
Nix consumes the flag), the off-write is itself committed so history stays
consistent, and wallpaper cycling (bg next) is deliberately excluded —
the current wallpaper rides along with the next real commit.
Per-theme app assets (themes/<slug>/)
Recoloring covers 95% of theming; the rest is one optional assets directory per theme — a single place to look, unlike the old distro's split:
| Asset | Mechanism |
|---|---|
backgrounds/ |
wallpapers; empty wallpaper in the state means "first one"; bg next cycles |
btop.theme |
baked into the generation (generated from the palette when absent) |
waybar.css |
whole-swap: replaces the generated bar style entirely (probed at eval time, self-contained) |
waybar.jsonc |
whole-swap for the bar layout (must be plain JSON) |
rofi.rasi |
whole-swap: replaces the generated launcher/menu theme entirely |
Four themes ship a waybar.css identity (summer-day, summer-night,
executive-slate, boreal). Custom user themes can live in
$NOMARCHY_PATH/themes/ (preset lookup) and nomarchy.themesDir (eval-time
asset probe).
5. Day-to-day
nomarchy-theme-sync list # 24 presets (nord, gruvbox, rose-pine, …)
nomarchy-theme-sync apply kanagawa # whole desktop, one generation (~a switch)
nomarchy-theme-sync set ui.gapsOut 16 # tweak one knob (also a switch)
nomarchy-theme-sync bg next # cycle wallpapers — instant, no rebuild
nomarchy-theme-sync bg auto # back to the theme's default wallpaper
nomarchy-theme-sync get colors.accent
sys-update # update inputs + rebuild the system (snapshots first)
sys-rebuild # rebuild the system, current lock (no update)
home-update # rebuild just the desktop layer
nomarchy-doctor # read-only health sheet (also: menu › System › Doctor)
Something broke anyway? Every rebuild is a generation and (on BTRFS) every hour is a snapshot — the undo story, from a bad theme to a machine that won't boot, is docs/RECOVERY.md.
Keybinds: SUPER+Return terminal · SUPER+D launcher · SUPER+T theme
picker · SUPER+SHIFT+T next wallpaper · SUPER+X power menu ·
SUPER+E file manager (yazi) · SUPER+N notifications · SUPER+CTRL+V
clipboard history · SUPER+SHIFT+C color picker (hyprpicker) · SUPER+Q
close · SUPER+1..9 workspaces · SUPER+ALT+arrow move workspace to monitor · Print region screenshot.
Shell aliases (zsh, gated on nomarchy.shell.enable) — alias lists them
all; the curated set:
# navigation
.. ... .... # cd up 1 / 2 / 3 levels
# git
g gst # git · git status -sb
ga gaa # git add · git add -A
gc gcm # git commit · git commit -m
gco gsw gb # checkout · switch · branch
gd gds # diff · diff --staged
gl glg # log graph (last 20 · all branches)
gp gpl gf # push · pull · fetch --all --prune
# nix (system/home rebuilds keep their full sys-update / home-update names)
ns nr # nix shell · nix run (e.g. ns nixpkgs#ripgrep)
nfu nfc # nix flake update · check
nsearch ngc # nix search nixpkgs · nix-collect-garbage -d
# misc
path # print $PATH, one entry per line
reload # exec zsh (reload the shell)
6. Extending
- New theme: drop a JSON into
themes/(schema = any existing preset), plus an optionalthemes/<slug>/assets directory. - New themed value: add the key to
theme-state.jsonand consume it in the Nix modules. One place — there is no second renderer to keep in sync. - Importing more old-distro palettes:
tools/import-palettes.py <palettes-dir> themes/. - New opt-in feature (convention): when a feature is off by default and
needs the user to set
nomarchy.*options (e.g. night light, per-device keyboard layouts, monitor layout, power management), ship a commented example of it intemplates/downstream/home.nixorsystem.nix. New installs then enable it by uncommenting + tweaking, rather than hunting the docs for the option name. - Applications: the starter complete-workstation set (LibreOffice, VS
Code, GIMP, Inkscape) lives in the
home.packageslist in yourhome.nix— it's a plain package list, so curate it directly: delete a line to slim the machine, uncomment an extra (a browser, email, full TeX Live), or add your own. Nonomarchy.apps.*toggles — a package list is already its own opt-out, so the distro doesn't impose these or wrap them.
Roadmap & known issues
| Doc | Role |
|---|---|
| agent/BACKLOG.md | What to do next (agent queue) |
| docs/VISION.md | Product themes toward v1.0 |
| docs/ROADMAP.md | Design history + shipped log |
| docs/README.md · agent/README.md | Maps |
| agent/LOOP.md | Autonomous iteration protocol |
Kept out of the README body so this stays a focused entry point.