Sweets
Configuration

Layer Rules

Match layer-shell roots by namespace and current layer, then apply live root or popup appearance.

sweets.layer_rule({ ... }) selects live compositor appearance for layer-shell roots such as panels, launchers, notification surfaces, and wallpapers. Layer rules are separate from window rules because layer surfaces have a different identity and do not use window opening policy.

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

Rules are optional. An unmatched layer root uses compositor opacity 1.0, does not receive compositor-forced blur, and keeps normal client and protocol behavior.

Matchers

Every rule requires at least one matcher and at least one appearance property. When both matchers are present, both must match the same layer root.

FieldTypeMatches
namespacestringThe namespace supplied by the layer-shell client
layerstringThe current committed "background", "bottom", "top", or "overlay" layer

Matching is exact and case-sensitive. Sweets does not interpret substrings, globs, regular expressions, or Lua callbacks. namespace cannot be empty.

Namespace is fixed for the layer surface's lifetime. A client may commit a new layer; Sweets then updates the typed identity and re-resolves the current rules. For example, a namespace-only rule keeps matching if the client moves between bottom and top, while a rule containing layer = "top" follows its current layer.

Appearance properties

FieldTypeEffect when specified
opacitynumberWhole-root opacity from 0.0 through 1.0
blurbooleanForce or suppress blur for the matching root tree
blur_modestringOverride the global mode with "optimized" or "scene"
blur_ignore_alphanumberSuppress forced blur where composed client alpha is at or below 0.0 through 1.0
popupstableDescendant XDG popup opacity, blur, and blur_mode

Opacity multiplies the root and all its ordinary subsurfaces once. Independent XDG popups are not part of that multiplier. A value of 0.0 makes the root tree invisible without unmapping it: layout, exclusive zones, keyboard interactivity, pointer hit testing, and client protocol state remain unchanged.

Blur is independent from transparency and still requires the global sweets.blur master switch. blur_mode changes the source used by an active effect but does not enable an effect by itself.

blur_ignore_alpha shapes compositor-forced blur from the visible client pixels. It is useful for launchers that own a full-output transparent surface. The threshold uses client alpha before the rule's opacity multiplier:

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

0.0 excludes only fully transparent pixels. Higher values can also remove faint shadows. Omit the field to blur the complete arranged root geometry.

sweets.blur({
  enabled = true,
  mode = "optimized",
  strength = 8,
  noise = 0.02,
})

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

blur = true supplies a whole-root effect when the root and ordinary subsurface tree has no non-empty client-requested blur region. A non-empty client region remains authoritative and retains its exact shape. blur = false vetoes client-requested blur in that root tree. The resolved blur_mode applies to either form. blur_ignore_alpha affects only the compositor-forced form.

Role popups remain independent from root opacity, force, veto, mode, and alpha masking. A nested popups block styles every descendant XDG popup:

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

The popup opacity does not multiply root opacity. It preserves popup placement, focus, grabs, input, and mapping, including at 0.0. Input-method popups, ordinary subsurfaces, and client-emulated menus are outside this policy.

Popup blur requires the global master. blur = false vetoes client popup blur. true supplies whole-popup blur only without a non-empty client region.

An exact non-empty client region wins. blur_mode selects "optimized" or "scene" for either form. blur_ignore_alpha never affects popup blur.

Popup opacity affects client content only. It does not attenuate the backdrop effect below the popup.

corner_radius clips either backdrop-effect form without clipping popup client pixels or input. It accepts the window radius's uniform integer or four-corner table and defaults to 0, because Wayland does not expose the client's visual radius.

Rule order and merging

Every matching rule contributes properties in declaration order. A later rule replaces only the properties it specifies; omitted properties remain from an earlier match.

sweets.layer_rule({
  layer = "top",
  opacity = 0.95,
})

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

Every top-layer root receives opacity 0.95. A matching Waybar root then receives opacity 0.85 and forced blur. Installed rules run first; personal rules append afterward, including declarations loaded with sweets.include.

Layer rules have their own limit of 128 entries and 32 KiB of retained matcher text. They do not consume the window-rule budget. Unknown fields, invalid types, non-finite or out-of-range opacity, missing matchers, missing appearance properties, and invalid layer or blur-mode names reject the complete candidate.

Inspecting live identity

Use the typed IPC query to see the identity and appearance Sweets actually resolved for every live layer-shell root:

sweets msg layers

The snapshot reports the exact namespace and current protocol layer, stable root ID, map state, requested and resolved output, arranged global geometry, keyboard interactivity, opacity, blur inherit/force/veto policy, resolved mode, global blur gate, retained client blur demand, and current effect demand. This makes client-side overrides visible—for example, a panel configured as top may commit itself to bottom. Independent XDG popups are not roots and are omitted. Add --json when a script needs the complete response envelope.

Live reload and rendering

Every valid configuration reload resolves all current layer roots against one complete new rule snapshot and installs their effective appearances atomically. The update does not remap, rearrange, configure, reposition, dismiss, focus, or disturb popup grabs. A visible effective change requests one redraw and invalidates output capture; static root or popup opacity and blur do not pace frames.

Forced blur is a visual element immediately below its owning root and uses the root's arranged geometry. Output capture includes the same composition shown on screen. Opacity below 1.0 or active blur requires compositor composition and blocks direct primary-plane scanout, while the hardware cursor plane remains independently eligible.

In optimized mode, background and bottom layer roots normally contribute to the shared output source. A root currently consuming forced or client-requested blur is excluded from that source so it cannot sample itself. Scene mode uses the same owner-bounded capture and renderer-failure isolation as window blur.

On this page