Hyprland Configuration (Lua)
I write my Hyprland configuration in Lua (hyprland.lua), migrated from the older .conf format. The value of Lua lies in its repetitive structures — monitors, animations, and keybindings are expressed via functions, which is clearer than writing out monitor=... line by line. However, the most useful part of this configuration for me is actually the comments: they document the hardware quirks for each monitor.
The three 27" monitors are matched by EDID description (desc:), so changing cables doesn't require modifying the configuration. Each has its own physical rotation:
hl.monitor({ output="desc:LG Electronics LG HDR 4K ...", -- Top-left, physically mounted upside down (180°)
mode="3840x2160@60", position="0x0", scale=1.5, transform=2 })
hl.monitor({ output="desc:ASUSTek ... XG27UCS ...", -- Bottom-left primary, 4K@160
mode="3840x2160@160", position="0x1440", scale=1.5, vrr=1 })
hl.monitor({ output="desc:CGC S27UHDIPSSR ...", -- Right, portrait mode, rotated 90° clockwise
mode="3840x2160@60", position="2560x0", scale=1.5, transform=1 })
The notes beside them are hard-learned lessons: VRR combined with direct_scanout causes black screens in fullscreen on AMD, so VRR is enabled per-monitor; the right portrait monitor via HDMI tops out at 4K@30, so achieving 60Hz requires an HDMI 2.1 cable or switching to DP (while avoiding DP-2/DP-3 CRTC conflicts). Writing these down means I won't have to relearn them next time I change cables or reinstall — the configuration file doubles as hardware notes.
The main modifier key is SUPER. Most bindings are standard (T for terminal, Q to close windows, F for fullscreen, M for launcher, SUPER+hjkl to switch focus, SUPER+SHIFT+hjkl to move workspaces to the corresponding direction). There are two intentional changes:
hl.bind("ALT + Tab", hl.dsp.focus({ workspace = "m+1" })) -- Switch workspaces on the "current monitor only", not across all screens
hl.bind(mainMod.." + V", hl.dsp.exec_cmd(
"cliphist list | fuzzel --dmenu | cliphist decode | wl-copy")) -- Clipboard history
For multi-monitor setups, I prefer switching workspaces on the current monitor rather than hunting for windows across the entire desktop; the classic window-switching binding was moved to ALT+\``. SUPER+Vinvokes the clipboard history stored bycliphist, allowing selection via fuzzeland pasting viawl-copy`.
Window rules and animations are also written using Lua tables, which is clearer than writing windowrulev2 = ... line by line. Rules match by class/title, pinning specific applications to fixed behaviors: calculators, file pickers, etc., are set to float and center; some apps are forced to specific workspaces; chat software has its opacity adjusted. For animations, I kept a set of faster curves (bezier custom easing + shorter durations) — tiling switches need to be "smooth" but not "slow"; excessive trailing is distracting. Combined with the VRR-enabled monitors, the priority is responsiveness. Corners, borders, and blur (decoration) are also handled at this layer, so applications like Ghostty can remain bare, with decorations uniformly applied by the compositor.
One more minor note: Chrome's binding uses the absolute path ~/.local/bin/google-chrome-stable because Hyprland's PATH does not include ~/.local/bin, so invoking it by name fails; the wrapper also pins the discrete GPU's renderD node. Color schemes are injected by matugen.