Sweets
Configuration

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 } })
FormMeaning
opacity = 0.88Every 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",
})
FieldTypeDefaultMeaning
enabledbooleanfalseDraw shadows at all
range01000How far the falloff reaches, in logical pixels
render_power143How fast it decays; higher is faster
offset{ x, y }{ 0, 0 }Move the shadow, -512 to 512 per axis
scale0.052.01.0Grow or shrink the shadow about its centre
sharpbooleanfalseReplace the falloff with a hard edge
floating_onlybooleanfalseOnly floating windows cast a shadow
colorcolor or gradient"#000000B0"Shadow paint; its alpha is the strength
color_inactivecolor or gradientunsetPaint 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:

FieldTypeMeaning
opacitynumber or tableContent opacity, one value or per state
dim_inactivenumber, colour, or falseColoured overlay while another window holds focus; window rules only
blurbooleanForce or suppress blur for this window
blur_modestringOverride the global mode: "optimized" or "scene"
shadowbooleanForce or suppress the drop shadow; window rules only
popupstableopacity, 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,
})
FieldTypeDefaultDescription
enabledbooleanfalseMaster switch
modestring"optimized""optimized" or "scene"
strengthinteger8Blur level, 1 to 15
noisenumber0.02Added noise, 0.0 to 0.25
saturationnumber1.0Colour strength, 0.0 (grey) to 4.0
brightnessnumber1.0Brightness multiplier, 0.0 to 2.0
contrastnumber1.0Contrast, 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 opacity

The 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.

On this page