Make application transparency opt-in (#14253)

* Make application transparency opt-in

* Use the transparency helper for Kanagawa

* Address transparency review findings

* Toggle transparency for opaque windows too
This commit is contained in:
David Heinemeier Hansson authored and GitHub committed 2026-10-04 12:44:15 -04:00
1 parent 6520ac7d25
commit 54e3b53b88
19 files changed
+55 -62

No files matched your search

@@ -2,6 +2,18 @@
# omarchy:summary=Toggles transparency for the currently focused window.
addr=$(hyprctl activewindow -j | jq -r '.address')
hyprctl dispatch "hl.dsp.window.set_prop({ window = \"address:$addr\", prop = \"opaque\", value = \"toggle\" })" >/dev/null 2>&1 || \
hyprctl dispatch setprop "address:$addr" opaque toggle
set -euo pipefail
addr=$(hyprctl activewindow -j | jq -r '.address // empty')
[[ -n $addr ]] || exit 0
window="address:$addr"
opacity=$(hyprctl getprop "$window" opacity)
inactive_opacity=$(hyprctl getprop "$window" opacity_inactive)
if [[ $opacity == "1" && $inactive_opacity == "1" ]]; then
hyprctl dispatch "hl.dsp.window.tag({ window = \"$window\", tag = \"+transparent\" })"
hyprctl dispatch "hl.dsp.window.set_prop({ window = \"$window\", prop = \"opaque\", value = \"false\" })"
else
hyprctl dispatch "hl.dsp.window.set_prop({ window = \"$window\", prop = \"opaque\", value = \"toggle\" })"
fi
+5
View File
@@ -1,5 +1,10 @@
-- Change the default Omarchy look'n'feel.
-- Opt another application in to Omarchy's standard transparency.
-- Find its class with: hyprctl clients
-- o.transparent_window("my-app")
-- o.transparent_window("my-app", "0.9 0.85") -- Custom active/inactive opacity.
-- https://wiki.hypr.land/Configuring/Basics/Variables/#general
-- hl.config({
-- general = {
+2 -4
View File
@@ -1,12 +1,10 @@
-- Browser tags and styling.
o.window("((google-)?[cC]hrom(e|ium)|[bB]rave-browser|[mM]icrosoft-edge|Vivaldi-stable|helium)", { tag = "+chromium-based-browser" })
o.window("([fF]irefox|zen|librewolf)", { tag = "+firefox-based-browser" })
o.window({ tag = "chromium-based-browser" }, { tag = "-default-opacity", tile = true, opacity = "1.0 0.985" })
o.window({ tag = "firefox-based-browser" }, { tag = "-default-opacity", opacity = "1.0 0.985" })
o.window({ tag = "chromium-based-browser" }, { tile = true })
-- Video apps: remove chromium browser tag so they don't get opacity applied.
-- Video apps: remove the chromium browser tag so they can float.
o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-chromium-based-browser" })
o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-default-opacity" })
-- Hide screen sharing notification windows.
o.window({ title = ".*is sharing.*" }, { workspace = "special silent" })
+1 -4
View File
@@ -1,12 +1,9 @@
-- DaVinci Resolve window focus handling. Kept fully opaque: the default
-- translucency distorts colour-critical grading work.
-- DaVinci Resolve window focus handling.
o.window(".*[Rr]esolve.*", {
float = true,
stay_focused = true,
-- Prevent modal dialog pointer warps when focus follows the mouse.
no_follow_mouse = true,
tag = "-default-opacity",
opacity = "1 1",
})
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve( Studio)? - .+$" }, { fullscreen = true })
-2
View File
@@ -1,7 +1,5 @@
-- Hermes Desktop's frameless HUD manages its own geometry.
o.window({ class = "^Hermes$", title = "^Hermes HUD$" }, {
tag = "-default-opacity",
float = true,
border_size = 0,
opacity = "1 1",
})
+4 -2
View File
@@ -1,3 +1,5 @@
-- Float LocalSend and fzf file picker.
o.window("(Share|localsend)", { float = true, center = true })
o.window("localsend", { size = { 1100, 700 } })
local localsend = "(org\\.localsend\\.)?localsend(_app)?"
o.window("Share", { float = true, center = true })
o.window(localsend, { float = true, center = true, size = { 1100, 700 } })
o.transparent_window(localsend)
-4
View File
@@ -1,24 +1,20 @@
-- Picture-in-picture overlays.
o.window({ title = "(Picture.?in.?[Pp]icture)" }, { tag = "+pip" })
o.window({ tag = "pip" }, {
tag = "-default-opacity",
float = true,
pin = true,
size = { 600, 338 },
keep_aspect_ratio = true,
border_size = 0,
opacity = "1 1",
move = { "(monitor_w-window_w-40)", "(monitor_h*0.04)" },
})
-- Google Meet PiP uses the meeting title instead of "Picture-in-Picture".
o.window({ tag = "chromium-based-browser", title = "^Meet - .+" }, {
tag = "-default-opacity",
float = true,
pin = true,
size = { 600, 338 },
keep_aspect_ratio = true,
border_size = 0,
opacity = "1 1",
move = { "(monitor_w-window_w-40)", "(monitor_h-window_h-40)" },
})
-1
View File
@@ -1 +0,0 @@
o.window("qemu", { tag = "-default-opacity", opacity = "1 1" })
-2
View File
@@ -1,6 +1,4 @@
o.window("com.libretro.RetroArch", {
fullscreen = true,
tag = "-default-opacity",
opacity = "1 1",
idle_inhibit = "fullscreen",
})
-1
View File
@@ -1,4 +1,3 @@
o.window("steam", { float = true, idle_inhibit = "fullscreen" })
o.window({ class = "steam", title = "Steam" }, { center = true, size = { 1100, 700 } })
o.window("steam.*", { tag = "-default-opacity", opacity = "1 1" })
o.window({ class = "steam", title = "Friends List" }, { size = { 460, 800 } })
-14
View File
@@ -37,20 +37,6 @@ o.window("org.omarchy.screensaver", { animation = "slide" })
-- where its fullscreen rule cannot take fullscreen from a window.
o.window("org.omarchy.screensaver", { workspace = "special:screensaver silent" })
-- No transparency on media windows.
o.window(
"^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$",
{
tag = "-default-opacity",
}
)
o.window(
"^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$",
{
opacity = "1 1",
}
)
-- Popped window rounding.
o.window({ tag = "pop" }, { rounding = 8 })
-2
View File
@@ -16,10 +16,8 @@ o.window("^WebcamOverlay-large$", {
-- The dedicated app id keeps this out of mpv's generic centered floating rules,
-- so the camera appears at its final corner position.
o.window({ class = "^WebcamOverlay-(small|medium|large)$", title = "^WebcamOverlay$" }, {
tag = "-default-opacity",
float = true,
pin = true,
no_initial_focus = true,
no_dim = true,
opacity = "1 1",
})
-5
View File
@@ -1,5 +0,0 @@
-- Keep the Windows VM display opaque instead of applying the default window opacity.
o.window({ class = "^xfreerdp$", title = "^Windows VM - Omarchy$" }, {
tag = "-default-opacity",
opacity = "1 1",
})
+1 -1
View File
@@ -16,7 +16,7 @@ o.bind("XF86Calculator", "Calculator", "omacalc")
o.bind_toggle("SUPER + SHIFT + SPACE", "Toggle top bar", "bar")
o.bind("SUPER + CTRL + SPACE", "Background switcher", { menu = "background" })
o.bind("SUPER + SHIFT + CTRL + SPACE", "Theme menu", { menu = "theme" })
o.bind("SUPER + BACKSPACE", "Toggle window transparency", "omarchy-hyprland-window-transparency-toggle")
o.bind("SUPER + BACKSPACE", "Toggle transparency on a window", "omarchy-hyprland-window-transparency-toggle")
o.bind("SUPER + SHIFT + BACKSPACE", "Toggle window gaps", "omarchy-hyprland-window-gaps-toggle")
o.bind("SUPER + CTRL + BACKSPACE", "Toggle single-window square aspect", "omarchy-hyprland-window-single-square-aspect-toggle")
o.bind_toggle("SUPER + CTRL + ALT + F", "Toggle full screen desktop", "fullscreen-desktop")
+5
View File
@@ -223,3 +223,8 @@ function o.window(match, rules)
hl.window_rule(rules)
end
-- Opt a window in to Omarchy's standard active/inactive transparency.
function o.transparent_window(match, opacity)
o.window(match, { opacity = opacity or "0.985 0.96" })
end
+7 -6
View File
@@ -2,9 +2,6 @@
o.window(".*", { suppress_event = "maximize" })
-- Tag all windows for default opacity (apps can override with -default-opacity tag).
o.window(".*", { tag = "+default-opacity" })
-- Fix some dragging issues with XWayland.
o.window(
{
@@ -18,8 +15,12 @@ o.window(
{ no_focus = true }
)
-- App-specific tweaks (may remove default-opacity tag).
-- App-specific tweaks and terminal tags.
require("default.hypr.apps")
-- Apply default opacity after apps have had a chance to opt out.
o.window({ tag = "default-opacity" }, { opacity = "0.985 0.96" })
-- Terminals (including TUIs) and these desktop apps opt in to transparency.
o.transparent_window({ tag = "terminal" })
o.transparent_window("(omawrite|1[pP]assword|com\\.onepassword\\.OnePassword|org\\.gnome\\.Nautilus)")
-- The transparency hotkey opts otherwise opaque windows in with this tag.
o.transparent_window({ tag = "transparent" })
+12
View File
@@ -0,0 +1,12 @@
echo "Add an example for opting applications in to transparency"
looknfeel="${XDG_CONFIG_HOME:-$HOME/.config}/hypr/looknfeel.lua"
if [[ -f $looknfeel ]] && ! rg -Fq 'o.transparent_window("my-app")' "$looknfeel"; then
cat >>"$looknfeel" <<'LUA'
-- Opt another application in to Omarchy's standard transparency.
-- Find its class with: hyprctl clients
-- o.transparent_window("my-app")
-- o.transparent_window("my-app", "0.9 0.85") -- Custom active/inactive opacity.
LUA
fi
+2 -10
View File
@@ -5,7 +5,6 @@ set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
windows_vm_command="$ROOT/bin/omarchy-windows-vm"
windows_vm_rules="$ROOT/default/hypr/apps/windows-vm.lua"
rg -q '^ restart: "no"$' "$windows_vm_command" ||
fail "Windows VM uses manual startup by default"
@@ -16,14 +15,7 @@ if rg -q '^ restart: unless-stopped$' "$windows_vm_command"; then
fi
pass "Windows VM does not restart automatically at boot"
# Tolerate either shell quoting of the argument -- what must not drift is the
# title itself, since the Hyprland rule below matches on it.
# Tolerate either shell quoting of the argument.
rg -q 'title:"?Windows VM - Omarchy"' "$windows_vm_command" ||
fail "Windows VM launches FreeRDP with its expected title"
rg -q 'class = "\^xfreerdp\$", title = "\^Windows VM - Omarchy\$"' "$windows_vm_rules" ||
fail "Windows VM opacity rule targets its FreeRDP window"
rg -q 'tag = "-default-opacity"' "$windows_vm_rules" ||
fail "Windows VM opts out of default opacity"
rg -q 'opacity = "1 1"' "$windows_vm_rules" ||
fail "Windows VM stays fully opaque"
pass "Windows VM stays fully opaque"
pass "Windows VM launches FreeRDP with its expected title"
+1 -1
View File
@@ -15,4 +15,4 @@ hl.config({
})
-- Kanagawa backdrop is too strong for default opacity.
o.window({ tag = "terminal" }, { opacity = "0.99 0.985" })
o.transparent_window({ tag = "terminal" }, "0.99 0.985")