-- ============================================================================= -- hyprland.lua — Root Hyprland Lua configuration entry point -- -- This is the top-level config file loaded by Hyprland when the Lua backend -- is active. It is intentionally kept thin: device-specific settings (monitors, -- keybinds, environment variables, input, window rules, autostart programs) all -- live in the usr/ subdirectory so they can be deployed per-machine without -- touching this shared file. -- -- Reference: https://wiki.hypr.land/Configuring/Start/ -- Device-specific files live in ~/.config/hypr/usr/ (deployed from hypr/usr/). -- ============================================================================= -- Hyprland Lua config — https://wiki.hypr.land/Configuring/Start/ -- Device-specific files live in ~/.config/hypr/usr/ (deployed from hypr/usr/). -- Pull in per-device configuration modules from the usr/ directory. -- Each require() maps to a file on the Lua search path (usr/.lua). -- They are loaded in this specific order so that later modules can safely -- depend on earlier ones (e.g., binds may reference programs launched by -- autostart; input-device-exceptions builds on the global input baseline). require("usr.monitors") -- monitor layout, resolution, scale, and XWayland zero-scaling require("usr.envvars") -- environment variables injected into every child process require("usr.input") -- keyboard layout, mouse sensitivity, touchpad behaviour require("usr.binds") -- all keybindings and mouse button bindings require("usr.windowrules") -- per-app float/pin/opacity/tag/size rules require("usr.autostart") -- programs launched once when the compositor starts -------------------- ---- MY PROGRAMS --- -------------------- -- Convenience variables naming the preferred application for each role. -- These are referenced by keybind definitions in usr/binds.lua so that -- changing the preferred terminal, editor, etc. requires only a single edit here. local terminal = "kitty" -- GPU-accelerated terminal emulator local fileManager = "thunar" -- GTK file manager (Xfce/GNOME ecosystem) local editor = "kitty nvim" -- Neovim inside kitty; avoids a separate GUI wrapper local menu = "vicinae toggle" -- vicinae is the custom app-launcher; toggle shows/hides it --------------------- ---- LOOK & FEEL ---- --------------------- -- hl.config() is the Lua API equivalent of Hyprland's declarative keyword blocks. -- All visual and layout settings for the compositor are collected here so the full -- theming picture is visible in one place rather than spread across multiple files. hl.config({ general = { -- Inner gap between tiled windows (px). -- Kept small (3 px) so screen real-estate is not wasted on multi-monitor setups. gaps_in = 3, -- Outer gap between the outermost windows and the monitor edge (px). -- Slightly larger than gaps_in (6 px) to give the desktop a "framed" look, -- ensuring windows never touch the absolute screen edge. gaps_out = 6, -- Border width around every window (px). 4 px is thick enough to show the -- animated gradient colour clearly at 1.5x HiDPI scaling. border_size = 4, col = { -- Active window border: an animated gradient cycling through red hues at 35 degrees. -- Five colour stops alternating between #E40046 and #f50505 create a slow pulsing -- effect. The angle = 35 rotates the gradient continuously (see borderangle animation). active_border = { colors = { "rgb(E40046)", "rgb(f50505)", "rgb(E40046)", "rgb(f50505)", "rgb(E40046)" }, angle = 35 }, -- Inactive border: deep electric blue (#5018dd). Blue recedes visually relative to -- the hot red active border, making focus immediately obvious without being distracting. inactive_border = "rgb(5018dd)", }, -- Disable resize-by-grabbing the window border. This prevents accidental resizes -- when the cursor clips the edge during fast workspace switching or window dragging. resize_on_border = false, -- Tearing introduces screen-tear artefacts in exchange for lower latency. -- Disabled here for a clean image; enable per-window via windowrules for games. allow_tearing = false, -- Default tiling algorithm. Dwindle splits the remaining space in half each time -- a new window opens, producing a predictable Fibonacci spiral layout that keeps -- all windows visible without needing manual arrangement. layout = "dwindle", }, group = { -- Window groups stack multiple windows into one tiled slot with a tab bar. -- These colours keep the group tabs visually consistent with the main border theme. col = { -- Active tab in a window group: accent red to match the active window border. border_active = "rgb(E40046)", -- Inactive tabs fade to dark blue to de-emphasise them. border_inactive = "rgb(5018dd)", -- "Locked" groups cannot be accidentally moved between workspaces. -- A slightly different red (#f50505) distinguishes them from ordinary active groups. border_locked_active = "rgb(f50505)", border_locked_inactive = "rgb(5018dd)", }, groupbar = { -- Agave NerdFont provides both monospace text and NerdFont icon glyphs, -- letting the bar display icons without requiring a separate symbol font. font_family = "Agave NerdFont", font_size = 20, -- px; large enough to be readable at 1.5x HiDPI scale height = 25, -- px; matches indicator_height for a full-height flat pill -- round_only_edges = false: every tab gets the same border-radius, not just -- the first and last, giving a uniform pill row rather than a single rounded strip. round_only_edges = false, -- indicator_height = height (25 px): the selected-tab indicator fills the entire -- bar height instead of being a thin underline, making the active tab unmissable. indicator_height = 25, -- stacked = false: tabs appear side-by-side horizontally (breadcrumb style), -- not vertically stacked. Horizontal layout conserves vertical screen space. stacked = false, -- Tab label text colour: accent red, consistent with the active border colour. text_color = "rgb(E40046)", -- priority = 3 ensures the groupbar is rendered above regular window decorations -- (shadows, rounded corners) so it is never visually clipped. priority = 3, -- Rounded corners on the groupbar container (13 px radius). The value is chosen -- to complement the 20 px window rounding without looking disproportionate. rounding = 13, col = { -- Active tab fill: solid accent red, clearly selected. active = "rgb(E40046)", -- Inactive tab fill: blue background recedes visually. inactive = "rgb(5018dd)", -- Locked-group tab colours mirror the border locked colours for consistency. locked_active = "rgb(E40046)", locked_inactive = "rgb(5018dd)", }, }, }, decoration = { -- Window corner radius (px). 20 px gives a modern "card" look that matches -- the GTK cyberqueer theme's rounded widgets. rounding = 20, -- Focused window: fully opaque (1.0) so content is crisp and readable. active_opacity = 1, -- Unfocused windows: 80% opacity hints at the depth/focus hierarchy and lets -- the wallpaper or background windows bleed through subtly without obscuring content. inactive_opacity = 0.8, blur = { -- Gaussian blur applied behind transparent/translucent surfaces (terminals, -- unfocused windows). This creates the "frosted glass" depth effect. enabled = true, size = 3, -- blur kernel radius in pixels; small = tight, localised blur halo passes = 3, -- number of successive blur passes; more = smoother but higher GPU cost -- vibrancy boosts colour saturation of the blurred background so the wallpaper -- colours stay vivid even when blurred under a translucent surface. vibrancy = 0.1696, }, }, animations = { -- Master switch for all compositor animations. -- Individual animation types are configured below in the animations section. enabled = true, }, dwindle = { -- preserve_split = true: when a split window is closed and then reopened, -- Hyprland restores the previous split direction instead of always defaulting -- to horizontal. This prevents the layout from constantly collapsing and re-splitting. preserve_split = true, -- Windows in the special (scratchpad) workspace are scaled down to 95% so they -- visually "float" above the regular workspace content when toggled in. special_scale_factor = 0.95, }, master = { -- New windows claim the master (largest) pane rather than being added as a slave, -- so the most recently launched app always gets the most prominent position. new_status = "master", -- Same 95% scratchpad scale as dwindle, for visual consistency. special_scale_factor = 0.95, }, misc = { -- force_default_wallpaper = 0 disables Hyprland's built-in anime wallpaper so -- hyprpaper (or any other wallpaper daemon) has exclusive control of the background. force_default_wallpaper = 0, -- Remove the Hyprland logo / splash animation that plays on startup. -- The custom wallpaper is shown instead from the first frame. disable_hyprland_logo = true, }, }) ----------------- ---- ANIMATIONS -- ----------------- -- Define a custom cubic Bezier easing curve named "myBezier". -- Control points P1 = {0.05, 0.9} and P2 = {0.1, 1.05} produce a slight overshoot -- (spring-like bounce) at the end of the motion — windows "pop" snappily into their -- final position rather than gliding to a dead stop. This gives the DE an energetic feel. hl.curve("myBezier", { type = "bezier", points = { {0.05, 0.9}, {0.1, 1.05} } }) -- Window open and resize animation uses the custom spring curve. -- speed = 7 (Hyprland internal units; higher = faster) keeps the open feeling snappy. hl.animation({ leaf = "windows", enabled = true, speed = 7, bezier = "myBezier" }) -- Window close animation: "popin 80%" shrinks the window to 80% of its original size -- while it fades out, giving a quick snappy dismiss effect rather than a slow full collapse. hl.animation({ leaf = "windowsOut", enabled = true, speed = 7, bezier = "default", style = "popin 80%" }) -- Border colour crossfade when focus changes (active red <-> inactive blue). -- speed = 10 makes this almost instantaneous so focus changes feel immediate. hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" }) -- Animated gradient angle for the active border (the rotating multi-stop red gradient). -- speed = 8 produces a slow, ambient-pulse sweep rather than a distracting fast spin. hl.animation({ leaf = "borderangle", enabled = true, speed = 8, bezier = "default" }) -- Opacity crossfade when a window gains or loses focus (1.0 <-> 0.8 transition). hl.animation({ leaf = "fade", enabled = true, speed = 7, bezier = "default" }) -- Special (scratchpad) workspace entrance/exit animation. -- "slidevert" makes it drop in from the top / slide back up, distinguishing it clearly -- from regular workspace transitions which use horizontal slides by default. hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 10, bezier = "default", style = "slidevert" }) -------------- ---- DEVICE --- -------------- -- Per-device input override for a specific mouse identified by its kernel event name. -- Only fields that differ from the global input settings need to be listed here. -- sensitivity = -0.5 (valid range -1 to 1) reduces pointer speed for this mouse model -- without touching the global sensitivity, allowing different mice to have different feels. hl.device({ name = "epic-mouse-v1", sensitivity = -0.5, })