Visual Effects
Window and layer opacity, inactive dimming, backdrop blur, and client surface alpha
Opacity and blur are set globally with sweets.window and sweets.blur, then
overridden per window with
sweets.window_rule or per layer surface
with sweets.layer_rule.
These properties are live. A valid reload restyles windows that are already open without moving, resizing, or refocusing them.
Window opacity
-- Every window dims slightly when it loses focus.
sweets.window({ opacity = { focused = 1.0, unfocused = 0.92 } })
-- One window stays fully opaque.
sweets.window_rule({ app_id = "mpv", opacity = 1.0 })
-- One window sets each of its states.
sweets.window_rule({ app_id = "zen", opacity = { focused = 0.95, unfocused = 0.8 } })| Form | Meaning |
|---|---|
opacity = 0.88 | Every state |
opacity = { focused = 0.95, unfocused = 0.8 } | Each state named |
opacity = { unfocused = 0.8 } | Only unfocused; focused is inherited |
opacity = { dragged = 0.7 } | Only while a drag carries the window |
Values are between 0.0 and 1.0. A table naming no state is rejected.
Opacity multiplies the window content — text, images, and background together.
Borders, the blur behind the window, and popups are excluded. Use the nested
popups table to give popups their own opacity.
dragged is the exception: it fades the blur behind the window too. Otherwise
the blur would still hide the windows you are dragging over, and a blurred
window would look brighter rather than more transparent.
0.0 makes a window invisible but not unmapped: focus, clicking, and popup
grabs all still work.
Use animations.window_opacity
to crossfade the step instead of switching it in one frame.
Dragged windows
dragged is the opacity a window uses while you are moving it with the pointer.
It makes the windows underneath visible, so you can see where the window will
land.
-- Windows fade while you drag them, over 200ms.
sweets.window({ opacity = { dragged = 0.7 } })
sweets.animations({ window_opacity = { duration = 200 } })
-- One window stays opaque while dragged.
sweets.window_rule({ app_id = "mpv", opacity = 1.0 })It applies to tiled and floating windows, whether you drag with a compositor binding or by a window's own titlebar. The step begins once the drag actually moves. The window starts returning to its normal opacity when you release it.
There is no separate switch. dragged has no default: leave it out and the
window keeps its focused or unfocused opacity throughout, so nothing changes.
Set animations.window_opacity to enabled = false, or duration = 0, for an
instant change instead of a fade.
Client alpha and Sweets opacity multiply. A Foot background pixel at 0.70
client alpha with opacity = 0.75 ends up at 0.525, while its opaque text
ends up at 0.75.
Dimming inactive windows
dim_inactive darkens a window while another window holds focus.
-- Every window darkens slightly when it loses focus.
sweets.window({ dim_inactive = 0.15 })
-- Use a coloured overlay; its alpha is the strength.
sweets.window_rule({ app_id = "foot", dim_inactive = "#30105066" })
-- One window never darkens.
sweets.window_rule({ app_id = "mpv", dim_inactive = false })A number between 0.0 and 1.0 draws black at that alpha, preserving the
original form of this setting. A #RRGGBB or #RRGGBBAA colour controls both
the hue and strength; the six-digit form is opaque. 0.0 and false disable
the dim, and a later window rule can use false to clear an earlier rule.
The default is no dim.
Dimming is not the same as opacity.unfocused. Opacity shows the desktop
through the window, which makes it harder to read. Dimming pushes the window
back and leaves it readable. You can use both.
The dim covers the window content and the blur behind it, inside the same rounded corners. Borders and popups are excluded, and the blur itself is not weakened.
The focused window never dims, and neither does a fullscreen one. Maximized windows do.
animations.window_opacity crossfades the dim along with the opacity step.
Window shadows
sweets.shadow draws a drop shadow behind managed windows. It is off by
default.
sweets.shadow({
enabled = true,
range = 20,
offset = { 0, 6 },
color = "#000000B0",
color_inactive = "#00000060",
})| Field | Type | Default | Meaning |
|---|---|---|---|
enabled | boolean | false | Draw shadows at all |
range | 0–100 | 0 | How far the falloff reaches, in logical pixels |
render_power | 1–4 | 3 | How fast it decays; higher is faster |
offset | { x, y } | { 0, 0 } | Move the shadow, -512 to 512 per axis |
scale | 0.05–2.0 | 1.0 | Grow or shrink the shadow about its centre |
sharp | boolean | false | Replace the falloff with a hard edge |
floating_only | boolean | false | Only floating windows cast a shadow |
color | color or gradient | "#000000B0" | Shadow paint; its alpha is the strength |
color_inactive | color or gradient | unset | Paint while another window holds focus |
The shadow follows the window's outer rectangle and its corner radius. sharp
makes the shadow a hard silhouette, so range no longer applies to it.
Shadows are drawn around a window, never through it. A translucent window shows
the desktop behind it, not its own shadow, so opacity and blur keep working
unchanged. A scale below 1 with no offset is hidden behind its own
window.
Two adjacent tiled windows cast into the same gap, which darkens it twice over.
Set floating_only to keep shadows only for floating windows, or raise your
gaps.
-- Shadows for dialogs and launchers, none between tiles.
sweets.shadow({ enabled = true, range = 24, offset = { 0, 8 }, floating_only = true })
-- One window never casts one, whatever the global policy says.
sweets.window_rule({ app_id = "foot", shadow = false })Fullscreen, maximized, and smart-gap single windows draw no shadow, the same way they stay square.
Layer-shell surfaces
Bars, launchers, and notifications cast a shadow only where a rule asks:
sweets.layer_rule({ namespace = "waybar", shadow = true, corner_radius = 12 })A layer surface's rectangle is not always its visual shape. A notification daemon often owns a full-output surface and draws one small toast inside it, so Sweets does not assume a silhouette for every layer surface.
corner_radius is the shape you tell Sweets to assume. It rounds the shadow and
any blur Sweets draws for that surface. It never clips the client's own pixels
or its input region. A number rounds every corner. A table names the corners
that are round and leaves the rest square:
sweets.layer_rule({
namespace = "waybar",
shadow = true,
corner_radius = { bottom_right = 12, bottom_left = 12 },
})floating_only does not apply to layer surfaces.
The active and inactive colours crossfade on the animations.border timeline.
Appearance properties
These fields are available in both sweets.window_rule and
sweets.layer_rule:
| Field | Type | Meaning |
|---|---|---|
opacity | number or table | Content opacity, one value or per state |
dim_inactive | number, colour, or false | Coloured overlay while another window holds focus; window rules only |
blur | boolean | Force or suppress blur for this window |
blur_mode | string | Override the global mode: "optimized" or "scene" |
shadow | boolean | Force or suppress the drop shadow; window rules only |
popups | table | opacity, blur, and blur_mode for descendant popups |
Every rule needs at least one matcher and one property. When several rules match, a later rule replaces only the properties it declares. Installed rules run first, then yours.
Window rules and layer rules each have their own budget of 128 rules and 32 KiB of matcher text.
Backdrop blur
Blur is separate from transparency. Enable the renderer, then enable it per window:
sweets.blur({
enabled = true,
mode = "optimized",
strength = 8,
noise = 0.02,
saturation = 1.4,
})
sweets.window_rule({
app_id = "foot",
blur = true,
})| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Master switch |
mode | string | "optimized" | "optimized" or "scene" |
strength | integer | 8 | Blur level, 1 to 15 |
noise | number | 0.02 | Added noise, 0.0 to 0.25 |
saturation | number | 1.0 | Colour strength, 0.0 (grey) to 4.0 |
brightness | number | 1.0 | Brightness multiplier, 0.0 to 2.0 |
contrast | number | 1.0 | Contrast, 0.0 (flat grey) to 2.0 |
A blurred dark wallpaper can look like flat fog. A little extra saturation,
such as 1.2 to 1.5, makes it read as frosted glass. The colour settings
apply to every blurred window, layer surface, and popup.
optimized blurs one shared background per monitor, built from the clear
color and the background and bottom layer surfaces. It ignores ordinary
windows, so many blurred windows cost about the same as one.
scene blurs whatever is directly below each window, including the
wallpaper and other windows, in the order shown on screen. It never samples the
window that owns the effect or anything above it.
A rule with blur = false suppresses an earlier match. blur_mode overrides
the global mode for one window. A transparent application such as Foot stays
merely transparent until a rule turns blur on.
Blur never bleeds past the window's own bounds, so a neighbor across a gap cannot leak into its edge. A translucent window always shows the blurred backdrop rather than the sharp desktop.
Client-requested regions
Sweets supports ext_background_effect_manager_v1, so a Wayland client can
commit its own exact blur region for a surface. A non-empty client region wins
over the shape a rule would force; blur = true supplies the rounded window
shape only when the client asks for nothing.
The global switch gates every effect, and blur = false vetoes an entire
window subtree. Cursors, drag icons, input-method surfaces, unmanaged X11
surfaces, and lock screens never receive backdrop effects. Very complex regions
are ignored with a warning.
popups.corner_radius clips a popup's backdrop to a shape you configure. It
does not clip client content or change where clicks land.
Client surface alpha
Sweets supports alpha-modifier-v1, letting a client set a constant alpha on
its own surface. The result is:
client pixel alpha × client surface multiplier × Sweets opacityThe client value is per surface, while a Sweets rule covers the whole window.
Capture
Screen capture shows the same blur and transparency you see. Single-window capture keeps the window's opacity but excludes the wallpaper, other windows, and the backdrop blur.