Sweets
Configuration

Layer Rules

Style layer-shell surfaces such as panels, launchers, and wallpapers

sweets.layer_rule sets appearance and animation for layer-shell surfaces. They are separate from window rules because layer surfaces have their own identity and no opening policy.

sweets.layer_rule({
  namespace = "rofi",
  layer = "overlay",
  opacity = 0.92,
  blur = true,
  blur_ignore_alpha = 0.35,
  popups = { opacity = 0.85, blur = true, corner_radius = 12 },
})

Matchers

Every rule needs at least one matcher or exclusion, and at least one property.

FieldTypeMatches
namespacestring or patternThe namespace the client declares
layerstringThe current "background", "bottom", "top", or "overlay"

A bare namespace string matches exactly and case-sensitively. A namespace never changes, but a client can move between layers — a namespace-only rule follows it, while a rule naming layer applies only while the surface is on that layer.

namespace also accepts a table naming one of exact, prefix, suffix, or contains, with an optional ignore_case that defaults to false and folds ASCII case only:

sweets.layer_rule({ namespace = { suffix = "-notifications" }, animation_distance = 60 })
sweets.layer_rule({ namespace = { prefix = "waybar", ignore_case = true }, blur = true })

An empty pattern is rejected. There are no globs or regular expressions. When patterns overlap, every matching rule applies in declaration order and the last one to set a property wins.

Exclusions

exclude takes the same two matcher fields and skips the rule for any surface it accepts. Write several as a list, each clause complete in itself:

-- Blur every top-layer surface except the notification stack.
sweets.layer_rule({ layer = "top", exclude = { namespace = "mako" }, blur = true })

A rule may declare only exclusions, which applies it to every other surface. One rule accepts at most 16 exclusions. An empty exclude table, or a clause naming no field, is rejected.

Properties

FieldTypeEffect
opacitynumberWhole-surface opacity, 0.0 to 1.0
blurbooleanForce or suppress blur
blur_modestring"optimized" or "scene"
blur_ignore_alphanumberSkip forced blur where client alpha is at or below this
shadowbooleanCast a drop shadow behind this surface
dim_aroundstring or falseDarken the rest of the output behind this surface
corner_radiusnumber or tableThe visual corner radius Sweets assumes
animationbooleanAllow or suppress the layer open and close animations
animation_directionstring"auto", "left", "right", "up", or "down"
animation_distancestring or number"auto", or 0 to 4096 pixels
block_out_fromstring"none", "screencast", or "screen_capture"
popupstableopacity, blur, blur_mode, corner_radius for popups

opacity covers the surface and its subsurfaces, but not its popups. At 0.0 the surface is invisible yet fully functional — layout, exclusive zones, keyboard interactivity, and clicking all still work.

Blur

Blur needs the global sweets.blur switch. blur_mode changes the source of an active effect but never enables one.

sweets.layer_rule({ namespace = "waybar", opacity = 0.9, blur = true })

blur = true blurs the whole surface unless the client requested its own region, which always wins. blur = false vetoes blur for the surface and its subsurfaces.

blur_ignore_alpha shapes forced blur to the visible pixels, which is useful for a launcher that owns a full-screen transparent surface. The threshold reads client alpha before opacity is applied. 0.0 excludes only fully transparent pixels; higher values also drop faint shadows. Omit it to blur the whole surface.

sweets.layer_rule({ namespace = "rofi", blur = true, blur_ignore_alpha = 0.35 })

Shadows

shadow casts a drop shadow behind a layer surface. It needs the global sweets.shadow switch, and it is off unless a rule asks. A layer surface's rectangle is not always its visual shape, so Sweets does not assume one for every surface.

corner_radius is the shape you tell Sweets to assume. It rounds the shadow and any blur Sweets draws for that surface, and never clips the client's own pixels or its input region.

sweets.layer_rule({ namespace = "waybar", shadow = true, corner_radius = 12 })

-- A table names the corners that are round. An omitted corner is square, which
-- suits a bar whose top edge sits against the screen.
sweets.layer_rule({
  namespace = "waybar",
  shadow = true,
  corner_radius = { bottom_right = 12, bottom_left = 12 },
})

Dimming behind a surface

dim_around darkens everything below a layer surface on its monitor, which makes a launcher easier to read over a busy desktop. It takes a #RRGGBB or #RRGGBBAA colour, or false to clear an earlier rule.

sweets.layer_rule({ namespace = "fuzzel", dim_around = "#00000099" })

It follows the same rules as the window-rule field: one dimmer per monitor, fading with the surface's open and close animations.

Animation

animation = false opts one surface out of layer_open and layer_close.

sweets.layer_rule({ namespace = "swaync", animation = false })

animation_direction and animation_distance override the global values for one surface. Use them when "auto" picks something you do not want — a corner-anchored surface entering from the top, or a notification that should nudge rather than fly in:

sweets.layer_rule({ namespace = "sweetwall", animation_direction = "left" })
sweets.layer_rule({ namespace = "mako", animation_distance = 60 })

All three fields have three distinct states. Omitting one defers to the animation settings. "auto" forces per-surface resolution even when the global value is fixed. A named edge or number forces that value even when the global is "auto".

Direction and distance do nothing while the type is "fade", since a fade does not travel. A reload affects surfaces mapped afterwards and never disturbs a running animation.

Hiding a surface from captures

block_out_from replaces a layer surface with a solid black rectangle in screen captures, leaving the screen itself unchanged. Its popups go with it.

-- Notifications off screen shares, still on your own screenshots.
sweets.layer_rule({ namespace = "notifications", block_out_from = "screencast" })

The values and their limits are the same as for window rules: "screencast" blocks streams, "screen_capture" blocks every capture, and "none" releases an earlier rule. It reduces accidental exposure rather than guaranteeing confidentiality.

Blocking a background or bottom surface also removes optimized blur from that capture, so windows appear without their backdrop in it. The screen is unaffected. Optimized blur samples those layers, and keeping it would show the blocked surface through the blur it feeds.

Popups

sweets.layer_rule({
  namespace = "waybar",
  popups = { opacity = 0.85, blur = true, corner_radius = 12 },
})

Popup opacity is absolute — it does not multiply the surface's opacity — and affects client content only, never the backdrop behind it. Placement, focus, grabs, and input are preserved even at 0.0.

corner_radius clips the popup's backdrop effect to a shape you specify, since Wayland does not expose the real radius. It does not clip client pixels or input, and defaults to 0.

blur_ignore_alpha never applies to popups. Input-method popups, ordinary subsurfaces, and menus drawn inside the client are outside this policy.

Rule order

Rules apply in declaration order, and a later rule replaces only what it names:

sweets.layer_rule({ layer = "top", opacity = 0.95 })
sweets.layer_rule({ namespace = "waybar", layer = "top", opacity = 0.85, blur = true })

Every top-layer surface gets 0.95; Waybar then gets 0.85 and blur. Installed rules run first, then yours.

Layer rules have their own budget of 128 rules and 32 KiB of matcher text, separate from window rules.

Inspecting live surfaces

sweets msg layers

This reports each surface's namespace, current layer, map state, output, geometry, keyboard interactivity, opacity, and blur policy. It makes client-side overrides visible — a panel configured as top may commit itself to bottom. Add --json for machine-readable output.

Reload

A valid reload restyles live surfaces at once. It never remaps, rearranges, repositions, refocuses, or dismisses a popup.

On this page