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.
| Field | Type | Matches |
|---|---|---|
namespace | string or pattern | The namespace the client declares |
layer | string | The 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
| Field | Type | Effect |
|---|---|---|
opacity | number | Whole-surface opacity, 0.0 to 1.0 |
blur | boolean | Force or suppress blur |
blur_mode | string | "optimized" or "scene" |
blur_ignore_alpha | number | Skip forced blur where client alpha is at or below this |
shadow | boolean | Cast a drop shadow behind this surface |
dim_around | string or false | Darken the rest of the output behind this surface |
corner_radius | number or table | The visual corner radius Sweets assumes |
animation | boolean | Allow or suppress the layer open and close animations |
animation_direction | string | "auto", "left", "right", "up", or "down" |
animation_distance | string or number | "auto", or 0 to 4096 pixels |
block_out_from | string | "none", "screencast", or "screen_capture" |
popups | table | opacity, 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 layersThis 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.