Veil
Per-app window transparency for Omarchy. Every app gets its own focused and unfocused opacity, adjustable from a bar widget, the Omarchy menu, or the keyboard — and the settings survive a reboot.
<img src="preview.png" alt="The Veil panel: a focused-app section with Focused and Unfocused sliders, and a defaults section below" width="420">Why
Hyprland can set per-window opacity, but out of the box on Omarchy you get a
static rule in windows.lua and a binary "make this window opaque" toggle on
SUPER + BACKSPACE. There's no way to say "Firefox at 85% when I'm in it and
40% when I'm not, but leave my terminal alone" without hand-editing config and
reloading.
Veil makes that a slider.
Requirements
- Omarchy with the Quickshell-based
omarchy-shell(bar plugins,omarchy plugincommands) - Hyprland 0.55+ — this uses the Lua config format and the
set_propdispatcher that replacedhyprctl setprop jq(already present on Omarchy)
Install
omarchy plugin add https://github.com/Carasibana/omarchy-veil --enable
Pick right when it asks which bar section to use.
Then add one line to the end of ~/.config/hypr/hyprland.lua:
require("hypr.veil")
This is required, not optional. It loads the window rules Veil generates, which is what applies your settings to newly-opened windows and what makes everything survive a reboot. It must come after Omarchy's defaults — the end of the file is correct — because the last matching rule wins.
That's the whole install. The bar widget is fully functional at this point.
Optional: keybinds
Append examples/bindings.lua to ~/.config/hypr/bindings.lua:
| Key | Action |
|---|---|
SUPER + numpad - / + |
Focused opacity −5% / +5% |
SUPER + SHIFT + numpad - / + |
Unfocused opacity −5% / +5% |
SUPER + \ |
Toggle this app (fully opaque, values remembered) |
SUPER + SHIFT + \ |
Put this app back on the defaults |
Numpad +/- are KP_Add / KP_Subtract — distinct keysyms from the top-row
MINUS / EQUAL that Omarchy already binds to window resizing, and unaffected
by NumLock. Each press shows a brief notification with the new value, since 5%
steps are hard to eyeball.
Optional: Omarchy menu rows
Paste examples/omarchy-menu.jsonc into
~/.config/omarchy/extensions/omarchy-menu.jsonc. The file hot-reloads on save.
You get Style › Transparency with ✓-marked presets for both sections.
Using it
Click the bar icon for the panel. Right-click it to toggle the current app
without opening anything. With the panel open, a toggles the app and d puts it
back on defaults. No key is bound to a full reset — that stays on the explicit
button, since it is not undoable.
Sliders commit on release rather than on every frame — dragging otherwise regenerates the window rules and reloads Hyprland dozens of times per drag.
The model
Every app has a focused and an unfocused value. An app you haven't touched inherits Defaults. Turning an app off makes it fully opaque in both states while remembering its numbers, so switching it back on resumes where you left off.
Defaults ship at 98/96, the same window opacity Omarchy sets, so installing Veil changes nothing you can see until you move a slider.
The panel targets the window you were last in. The panel is a Wayland layer surface rather than a window, so opening it doesn't steal the target.
CLI
The engine is a plain script; the widget, menu, and keybinds all shell out to it, so no two surfaces can disagree about your settings.
veil app focused set 85 # current app, while focused
veil app unfocused set 40 # current app, while unfocused
veil app focused nudge +5
veil app toggle # opaque <-> transparent
veil app reset # back to defaults
veil defaults unfocused set 50
veil list # * marks inherited values
veil status # raw JSON
veil apply # re-apply state after a hand edit or a Hyprland restart
veil reset # everything back to 98/96
The app subcommands take an optional trailing window class to target something
other than the app you are currently in: veil app focused set 90 firefox.
(defaults, list, status, apply and reset take no class.)
Put it on your PATH if you want it handy:
ln -s ~/.config/omarchy/plugins/io.github.carasibana.veil/bin/veil ~/.local/bin/veil
Find a window's class with hyprctl clients -j | jq -r '.[].class'.
How it works
State lives in ~/.local/state/veil/opacity.json and is the single source of
truth:
{
"defaults": { "focused": 50, "unfocused": 50 },
"apps": { "firefox": { "enabled": true, "focused": 85, "unfocused": 40 } }
}
Every change does two things, because Hyprland needs both:
set_propdispatches against live windows, for instant effect.- Regenerates
~/.config/hypr/veil.luaand reloads, so windows opened later — including everything after a reboot — inherit the current state.
Both are necessary. A set_prop override outranks window rules and is permanent
for that window, so once a window has been touched the rules can never reclaim
it; live windows must always be set explicitly. Conversely the rules are the only
thing that reaches windows that don't exist yet.
Nothing runs at login, and the widget locates the engine relative to its own
QML file rather than relying on PATH. The engine itself calls the usual
userland tools — jq, hyprctl, notify-send, flock, and a handful of
coreutils — from PATH as any script does.
Uninstall
Put everything back to fully opaque first, while veil is still installed:
veil reset
veil defaults focused set 100
veil defaults unfocused set 100
In that order. reset clears the per-app entries but leaves the defaults at
98/96, so raise them afterwards; raising them on their own does nothing for apps
that have an entry of their own.
Anything still transparent when the plugin goes stays that way until you close
the window or restart Hyprland — a set_prop override lasts the life of the
window, and the tool that would undo it is gone.
Then:
omarchy plugin remove io.github.carasibana.veil
Remove require("hypr.veil") from ~/.config/hypr/hyprland.lua, delete
~/.config/hypr/veil.lua, and drop the keybind/menu snippets if you added them.
~/.local/state/veil/ holds your settings — delete it to forget them.
Notes
SUPER + BACKSPACE outranks Veil. That's an Omarchy default bound to
omarchy-hyprland-window-transparency-toggle, which sets Hyprland's per-window
opaque prop — a hard override that beats any opacity value. If one window
seems immune to Veil, check that first.
Transparency is subtler than you'd expect on dark themes. Window alpha
blends toward whatever is behind, and if that's a dark wallpaper the shift can be
a couple of points out of 255 — real, but nearly invisible. Also, uniform window
alpha fades text and background together, so it reads as dim rather than
see-through; terminals look genuinely transparent because they alpha only their
background and leave glyphs opaque. And if decoration:blur:xray is on in your
looknfeel.lua, transparency reveals only the blurred wallpaper, never the
window behind. Try around 50% to see the effect clearly, then back off.
Omarchy's opacity opt-outs still hold. The default applies to the same
default-opacity tag Omarchy uses, so windows its app files exempt —
picture-in-picture, the webcam overlay, Steam, video windows — stay opaque.
Giving one of those apps its own value overrides the exemption, which is the
point of setting it.
Compatibility
This leans on Omarchy and Hyprland internals that carry no stability promise —
the set_prop dispatcher, the o.window() Lua helper, the shell's Ui/
components, and the bar's widget-sizing contract. hyprctl setprop already
disappeared once, in Hyprland 0.55. Expect occasional breakage across major
Omarchy releases; open an issue and I'll chase it.
Changelog
See CHANGELOG.md.
License
MIT