description: Scrollable-tiling Wayland compositor config — input, layout, window rules, per-display profiles, keybindings, and launcher scripts.
---
[niri](https://github.com/YaLTeR/niri) is a scrollable-tiling Wayland compositor. Here it runs with the Noctalia shell (bar, panels, OSD, notifications), vicinae as the launcher, and `niri-dynamic-workspaces` for named workspaces.
:::note
Linux `desktop` and `laptop` roles only. `.chezmoiignore.tmpl` skips `~/.config/niri/**`, `~/.local/bin/niri-launch`, and `~/.local/bin/cp-appid` on other roles. The `niri-launch-*` wrappers are not in the ignore list and are applied everywhere.
`dot_config/niri` is a git submodule ([prdlk/niri](https://github.com/prdlk/niri)). Edit inside the submodule, commit there, then commit the updated pointer in the chezmoi repo.
`exact_config.d` is a chezmoi **exact** directory. On apply, chezmoi deletes any file in `~/.config/niri/config.d/` that the repo does not manage, so stale fragments cannot linger. None of these files are templates.
- `niri`, `xwayland-satellite`, `polkit-gnome`, `cliphist`, `wl-clipboard` and `xdg-user-dirs` from pacman, `noctalia-git` and `vicinae-bin` from the AUR. Declared under `packages.arch.desktop` in `.chezmoidata/packages.yaml` and installed by `run_onchange_before_10-packages.sh.tmpl` for `desktop`/`laptop` roles. See [chezmoi scripts](/chezmoi/scripts).
- `.chezmoiscripts/run_once_after_30-user-services.sh.tmpl` runs once on Linux. If `~/.config/systemd/user/nirinit.service` exists, it runs `systemctl --user daemon-reload` and `systemctl --user enable nirinit.service`. The script's comment says other personal units (brain-lens, omp-loop, rclone, hermes, voxtype) ship as files only and are enabled by hand. The repo does not currently contain `dot_config/systemd`, so the unit must come from elsewhere for the enable step to run.
- Tools the config calls but does not install: `kbuildsycoca6`, `niri-dynamic-workspaces`, `dot-screenshot`, `niri-smart-close`, `niri-smart-move`, and the `capitaine-cursors-light` cursor theme. `niri-smart-close`, `niri-smart-move`, and `dot-screenshot` are called by path or name but are not in this repo. `voxtype-bin` comes from the `[omarchy]` repo when present.
`config.kdl` sets two top-level options and then includes the fragments in numeric order:
```kdl ~/.config/niri/config.kdl
hotkey-overlay {
skip-at-startup
}
screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
include "config.d/10-input-and-cursor.kdl"
// … 20 through 90 …
include "config.d/90-outputs.kdl"
```
After the includes, `config.kdl` repeats inline `input`, `layout`, `gestures`, `cursor`, `overview`, `window-rule`, `animations`, and `environment` blocks, a leftover of the iNiR (illogical-impulse) base it started from. It then sets `debug { honor-xdg-activation-with-invalid-serial }`, which lets Noctalia v5 activate windows from notification actions. It includes `./noctalia.kdl` and ends with `prefer-no-csd`. niri merges repeated sections and later values win, so the inline blocks override the fragments where they differ. Those differences are listed in each section below.
The inline `input` block in `config.kdl` repeats the same keyboard and touchpad values without `accel-speed` or the tablet/touch mappings, so those two settings from the fragment still apply.
## Layout and overview
`config.d/20-layout-and-overview.kdl`:
| Setting | Value |
| --- | --- |
| `center-focused-column` | `never` |
| `background-color` | `transparent` (the Noctalia wallpaper shows through as backdrop) |
| Tab indicator | `place-within-column`, `hide-when-single-tab`, position `left` |
| Overview | `zoom 0.4`, workspace shadow off |
The `gestures { hot-corners { } }` block is empty in the fragment. The inline block in `config.kdl` sets `hot-corners { off }`, so hot corners are disabled.
### Colors (Noctalia)
`noctalia.kdl` is included last, so its colors override the fragment's placeholder colors (`#7fc8ff` ring, `#ffc87f` border):
| Element | Active | Inactive | Urgent |
| --- | --- | --- | --- |
| Focus ring | `#a398b3` | `#1e1c21` | `#fd4663` |
| Title `Picture-in-Picture` (app `zen`) or `Picture in picture` | Floating, not focused, opacity 1.0, 24 px from the top-right corner, on `eDP-1` |
| `zed`, `obsidian`, `sticky-notest-canvas`, title `Sticky Notes`, `vacuumtube`, `Fladder`, `kitty`, `kitty-herdr`, `kitty-tmux` | Open on the Apple Studio Display |
Global blur settings: `passes 4`, `offset 5.0`, `noise 0.02`, `saturation 2.0`. The inline `window-rule` in `config.kdl` sets `geometry-corner-radius 8` after the fragment, so the effective radius is 8.
## Environment
`config.d/40-environment.kdl` (repeated verbatim inline in `config.kdl`):
| `noctalia` | Shell: bar, panels, notifications, OSD, wallpaper |
| `niri-dynamic-workspaces daemon` | Named dynamic workspaces |
| `vicinae server` | Launcher backend |
| `wl-paste --watch cliphist store` | Clipboard history |
## Animations
`config.d/60-animations.kdl` makes every animation a critically damped spring (`damping-ratio=1.0`, `epsilon=0.0001`) with high stiffness: workspace-switch 4000, window-open 5000, window-close 6000, horizontal-view-movement 4500, window-movement 5000, window-resize 5500, config-notification-open-close 5000, screenshot-ui-open 5000.
The inline `animations` block in `config.kdl` comes later and overrides these with softer springs tuned to match quickshell's Material motion:
| Animation | Damping ratio | Stiffness |
| --- | --- | --- |
| workspace-switch | 0.78 | 1350 |
| window-open | 0.82 | 1125 |
| window-close | 0.88 | 2025 |
| horizontal-view-movement | 0.80 | 1238 |
| window-movement | 0.85 | 1463 |
| window-resize | 0.88 | 1575 |
| config-notification-open-close | 0.90 | 1800 |
| screenshot-ui-open | 0.85 | 1688 |
## Keybindings
All bindings from `config.d/70-binds.kdl`. `Mod` is `Super` (`Alt` when niri runs nested). `Super+…` and `Mod+…` are the same modifier. The table groups bindings by the file's comment headers.
| `Mod+J` | Focus window below, or workspace below |
| `Mod+K` | Focus window above, or workspace above |
| `Mod+Shift+H` | `niri-smart-move left` |
| `Mod+Shift+L` | `niri-smart-move right` |
| `Mod+Shift+J` | Move column to workspace below |
| `Mod+Shift+K` | Move column to workspace above |
| `Super+Ctrl+H` | Focus first column |
| `Super+Ctrl+L` | Focus last column |
| `Super+Ctrl+Shift+H` | Move column to first |
| `Super+Ctrl+Shift+L` | Move column to last |
| `Mod+Q` | `niri-smart-close` (no repeat) |
### Monitors
| Keys | Action |
| --- | --- |
| `Mod+Left` | Focus monitor left |
| `Mod+Up` | Focus monitor up |
| `Mod+Right` | Focus monitor right |
| `Mod+Shift+Down` | Move column to monitor right |
| `Mod+Shift+Up` | Move column to monitor left |
| `Super+O` | Focus next monitor |
| `Super+Ctrl+J` | Focus next monitor |
| `Super+Ctrl+K` | Focus previous monitor |
| `Super+Shift+O` | Move column to next monitor |
| `Mod+Ctrl+Shift+J` | Move column to next monitor |
| `Mod+Ctrl+Shift+K` | Move column to previous monitor |
### Layout and sizing
| Keys | Action |
| --- | --- |
| `Mod+R` | Cycle preset column widths |
| `Mod+Shift+R` | Cycle preset window heights |
| `Mod+F` | Expand column to available width |
| `Mod+Shift+F` | Maximize column |
| `Mod+Ctrl+F` | Fullscreen window |
| `Super+C` | Center column |
| `Mod+Ctrl+C` | Center visible columns |
| `Mod+Ctrl+O` | Toggle window floating |
| `Mod+W` | Toggle tabbed column display |
| `Alt+Shift+H` | Column width −10% |
| `Alt+Shift+L` | Column width +10% |
| `Alt+Shift+J` | Window height −10% |
| `Alt+Shift+K` | Window height +10% |
### Hardware keys
All of these work while the screen is locked (`allow-when-locked=true`) and go through Noctalia IPC, so the Noctalia OSD shows feedback. The commented `v4:` lines in the file are the old `qs -c noctalia-shell ipc call …` forms.
- Noctalia surfaces matching `^noctalia-(bar-…|notification|dock|panel|attached-panel|osd)$` get `background-effect { xray false }`, so their blur samples what is actually behind them. Noctalia publishes blur regions itself when `ext-background-effects` is available.
- `^noctalia-wallpaper` is placed within the backdrop (`place-within-backdrop true`). The wallpaper stays still while workspaces move and shows in the overview.
- `layout { background-color "transparent" }` and overview `workspace-shadow { off }` are restated here so the backdrop is always visible.
## Outputs and display profiles
`config.d/90-outputs.kdl` turns `DP-5` off and includes the per-display profiles. An older Apple Display block and a `DP-4 off` block remain as comments.
```kdl ~/.config/niri/config.d/90-outputs.kdl
include "91-internal-display.kdl"
// include "92-ultrawide.kdl"
include "93-apple-display.kdl"
include "94-portable-hdmi.kdl"
// include "95-portable-usbc.kdl"
```
The Apple Studio Display also exposes a phantom connector (DP-4/5/6, varies per boot) whose only mode is 640×480. A commented `spawn-at-startup` for `scripts/kill-phantom-display.fish` handles it by mode signature, since connector name and EDID both shift.
<Tabs>
<Tab title="Internal (91)">
`Tianma Microelectronics Ltd. TL134ADXP03 Unknown`, the laptop panel. **Active.**
Named workspaces `1`, `2`, `3` open on this panel. Three window rules open these apps maximized and focused on workspace `1`, without border backgrounds (all three target `"1"` as written):
To switch a profile, uncomment or comment its `include` line in `90-outputs.kdl`. niri reloads config on save.
## Scripts
Deployed to `~/.config/niri/scripts/`. `70-binds.kdl` binds none of them, so they run by hand or from other tools.
| Script | Usage | Behavior |
| --- | --- | --- |
| `kill-phantom-display.fish` | no args | Polls `niri msg -j outputs` up to 10 times, 1 s apart. Any output whose only mode is 640×480 gets `niri msg output <name> off`. Does not handle mid-session hotplug. |
| `ndw-route-workspace.sh` | `$NDW_WORKSPACE_KEY` or `$1` | niri-dynamic-workspaces `on_create` hook. Keys `y g m x z p l a` move the workspace to `HDMI-A-1`. Keys `b f s w c d o` move it to the Apple Studio Display (resolved by make/model through `niri msg --json outputs`), or to `HDMI-A-1` if that display is absent. Other keys exit silently. |
| `niri-floating-sidebar.sh` | `toggle` \| `hide` \| `flip` \| `reorder` \| `help` | Per-workspace floating sidebar on the right edge. Windows are 550 px wide and stack into 4 slots (more as needed) with 15 px gaps and 30/50/20 px top/bottom/side margins. `toggle` floats the focused window into the sidebar or restores it to tiling at its original size. `hide` slides the sidebar off-screen, leaving a 70 px sliver. `flip` switches bottom-up and top-down stacking. State is kept in `/tmp/niri.toggle.<workspace>.*`. |
| `niri-focus.sh` | `up` \| `down` | Cascading focus: next window in the column, then the next *occupied* workspace (skipping the trailing empty one), then the adjacent monitor. Wraps to the far-end monitor at the edge. |
| `niri-move.sh` | `up` \| `down` | Cascading move: reorder within the column, then move to the next workspace (including the empty trailing one), then the adjacent monitor. Wraps at the edge. |
## Launchers
### niri-launch
`~/.local/bin/niri-launch <app>` focuses an app's window, cycles through its windows on repeated presses (sorted by window id), or launches it when none is open.
| Argument | Windows matched (app-id) | Launched when none |
| --- | --- | --- |
| `webapp` | `^(webapp-\|chrome-)` (Firefox and Chromium/Brave site-specific apps) | Re-runs as `webapp-youtube` |
| `webapp-NAME` | `WebApp-Name` (case-insensitive) | `gtk-launch` on `~/.local/share/applications/WebApp-Name*.desktop` |
| `kitty` | any app-id starting with `kitty` | `kitty` |
| `kitty-CLASS` | exactly that class | `kitty --class kitty-CLASS` (optional command from the empty `KITTY_CMDS` map) |
| `ghostty` | `ghostty` | nothing, prints a message |
`cp-appid` lists every `.desktop` file (via `fd`) in `/usr/share/applications`, `/usr/local/share/applications`, `~/.local/share/applications`, the system and user Flatpak export dirs, and `/var/lib/snapd/desktop/applications`. It shows each entry's `Name=` and ID in `fzf`, then copies the chosen ID to the clipboard with `wl-copy`. Use it to find app-ids for window rules and `niri-launch`.
## Related
- [Hyprland](/desktop/hyprland): the Omarchy setup with the same key layout.
- [kitty](/terminal/kitty): `kitty-goliath`, `kitty-herdr`, and `kitty-tmux` classes referenced by the rules.