Bar
Bars are defined as named subtables under [bar.*]. Each bar is spawned on every connected monitor, then per-monitor overrides are applied.
[bar]order = ["main"] # layer-shell creation order
[bar.main]position = "top" # top | bottom | left | rightenabled = trueauto_hide = false # slide out after pointer leaves; reveal from edge trigger stripshow_on_workspace_switch = true # with auto_hide: briefly reveal when the active workspace changesreserve_space = true # reserve compositor exclusive zone / push windows awaylayer = "top" # top | overlay; overlay appears above fullscreen apps
thickness = 34 # bar cross-axis size in pixels (height for horizontal, width for vertical)background_opacity = 1.0 # 0.0 (transparent) to 1.0 (opaque)border = "outline" # color role or #RRGGBB for the bar outlineborder_width = 0.0 # inside outline width in pixels; 0 disables itshadow = true # cast the global [shell.shadow]contact_shadow = false # dark gradient between an attached panel and the bar (depth at the seam)panel_overlap = 1 # logical px an attached panel overlaps the bar edge to hide the seamradius = 12 # global corner radius fallbackradius_top_left = 12radius_top_right = 12radius_bottom_left = 12radius_bottom_right = 12margin_ends = 180 # inset from each end of the bar along its main axismargin_edge = 10 # distance from the nearest screen edge (positive values float the bar)margin_opposite_edge = 0 # extra reserved space on the inward side of the bar (below for top, above for bottom)padding = 14 # main-axis padding from bar edges to start/end widget sectionswidget_spacing = 6 # gap between widgets within a sectionscale = 1.0 # content scale multiplier for icons and textfont_weight = "regular" # "regular" | "bold" — primary label weight for bar widgetsfont_family = "" # typeface for this bar's widgets; empty inherits the global font
# Default capsule style for all widgets on this bar (see Widget Capsule section)capsule = falsecapsule_fill = "surface_variant"capsule_thickness = 0.76 # capsule size across the bar as a fraction of bar thickness (1.0 fills the bar)capsule_radius = 8.0 # omit for automatic pill radiuscapsule_opacity = 1.0# capsule_border = "outline" # omit this key for no border by default
start = ["launcher", "wallpaper", "workspaces"]center = ["clock"]end = ["media", "tray", "notifications", "clipboard", "network", "bluetooth", "volume", "brightness", "battery", "control-center", "session"]Radius precedence: radius is the global fallback; per-corner values override it when provided.
Concave (inverted) corners
Section titled “Concave (inverted) corners”A negative per-corner radius carves a concave corner instead of a convex
round-off: the corner bulges outward into a rounded spike that hugs the screen edge,
so the bar appears to melt into the screen. The spike’s size is the absolute value
(e.g. radius_bottom_left = -16 gives a 16 px spike).
Concave spikes only render on the bar’s two inner corners — the corners facing away from the screen edge — because the bar can’t grow along the edge it’s anchored to:
| Bar position | Inner corners (set these negative) |
|---|---|
top | radius_bottom_left, radius_bottom_right |
bottom | radius_top_left, radius_top_right |
left | radius_top_right, radius_bottom_right |
right | radius_top_left, radius_bottom_left |
The spike depth is capped at half the bar thickness. A negative value on an outer
(screen-edge) corner has no room to grow and is clipped, so leave those at 0 or a
positive radius.
Multiple [bar.*] entries are supported — each is independently configured and rendered on all monitors.
[bar].order controls the order Noctalia creates bar layer-shell surfaces. This matters only for bars with
reserve_space = true, and the compositor still owns the final exclusive-zone arrangement. Names omitted from order
are appended after the listed names.
Dead zone actions
Section titled “Dead zone actions”The dead zone is the bar margin outside the start/center/end widget sections — typically the inset created by margin_ends. Configure shell commands under [bar.<name>.dead_zone]:
[bar.main.dead_zone]command = "noctalia msg launcher toggle" # left clickright_command = "" # empty = open Control Center (default)middle_command = "..." # middle clickscroll_up_command = "..."scroll_down_command = "..."Right-click still opens Control Center when right_command is empty. Set right_command to override that on dead-zone clicks only. While an attached panel is open, right-click anywhere on the bar still toggles Control Center unless a dead-zone right_command is configured and the click lands in the dead zone.
Per-monitor overrides can set any dead-zone command under [bar.<name>.monitor.<match>.dead_zone]; unset fields inherit from the bar defaults.
Per-monitor overrides
Section titled “Per-monitor overrides”Inside a bar, add named monitor subtables under [bar.<name>.monitor.*]. First match wins, in file order.
[bar.main.monitor.dp1]match = "DP-1" # connector name or description substringposition = "bottom" # top | bottom | left | rightenabled = truethickness = 44background_opacity = 0.9border_width = 1.0radius = 0radius_top_left = 12radius_top_right = 12radius_bottom_left = 0radius_bottom_right = 0padding = 20widget_spacing = 6start = []center = ["workspaces"]end = ["volume", "clock"]match resolution — compared against:
- Exact connector name (
eDP-1,DP-1,HDMI-A-1, …) - Any substring of the monitor description string (
"LG","4K","DELL", …)
match defaults to the subtable key name when omitted, so [bar.main.monitor."DP-1"] without a match field works too.
Only the fields you specify are overridden; everything else falls through to the [bar.*] defaults. Supported override fields: all bar fields including position, dead_zone, plus auto_hide, show_on_workspace_switch, reserve_space, layer, scale, background_opacity, color, font_family, and all capsule_* keys.
Shadow blur, offset, and alpha are global under [shell.shadow]. Bars only expose shadow = true|false.
contact_shadow = true adds a dark gradient at the seam between an attached panel and the bar, giving the panel a slight lifted-off-the-bar feel. It is independent of shadow and only takes effect on attached panels; the change applies on the next panel open.
panel_overlap controls how many logical pixels an attached panel is pulled into the bar so the two surfaces share an edge instead of leaving a hairline gap. The ideal value depends on your compositor and the output’s fractional scale (it comes down to physical-pixel rounding), so it is tunable rather than fixed. The default is 1. If you see a faint seam line, try 0; if a gap appears, raise it. Negative values push the panel away from the bar. Because the right value tracks fractional scale, you can set it per monitor with a [bar.<name>.monitor.*] override on a mixed-scale multi-monitor setup. In the Settings GUI it lives under Bar → Layout → Advanced.
capsule_thickness sets the widget capsule size across the bar (its height on a horizontal bar, width on a vertical bar) as a fraction of the bar thickness. The default is 0.76; 1.0 makes capsules fill the full bar thickness and lower values shrink them, leaving more margin on either side. It applies to every capsule on the bar regardless of per-widget content scale. In the Settings GUI it lives under Bar → Capsules.
font_family sets the typeface for this bar’s widget labels. Leave it empty (the default) to inherit the global [shell] font_family. A single widget can override it with [widget.<name>] font_family, and it can be set per monitor with a [bar.<name>.monitor.*] override. The Settings GUI exposes a searchable font picker under Bar → Widgets, with a per-widget picker in each widget’s settings.
border_width draws an inside outline using border. Attached panels stay borderless so they remain visually clean against the bar.
Auto-hide
Section titled “Auto-hide”When auto_hide = true, the bar:
- Slides out once the pointer leaves the bar.
- Slides back in when the pointer reaches the matching screen edge. With
margin_edge > 0, the float gap is included in the layer surface so reveal works from the physical screen edge. Withmargin_edge = 0, the bar stays flush to the edge; pointer hit-testing still uses the full layer surface so reveal does not immediately cancel. - While auto-hide is active, pointer hit-testing uses the full bar layer surface so entering from the edge strip does not immediately leave again.
- With
show_on_workspace_switch = true(default), briefly reveals when the active workspace changes on that monitor, then hides again if the pointer is not over the bar. - IPC
bar-show,bar-hide, andbar-toggleaccept optional[bar-name] [monitor-selector]arguments; omit both to affect every bar instance. They use the same hide/reveal animation instead of tearing down surfaces. Withauto_hide, they retract or reveal the bar like pointer auto-hide; edge reveal still works afterbar-hide. Withauto_hideoff,bar-hidehides the bar and always frees the compositor gap untilbar-show(orbar-togglewhile showing), regardless ofreserve_space.
reserve_space controls whether the bar keeps a compositor exclusive zone. With auto_hide = false, reserve_space = true keeps windows pushed away; reserve_space = false overlays the bar on top of full-screen clients. With auto_hide = true, reserve_space = false retracts the bar as an overlay (no gap); reserve_space = true keeps the gap while the bar slides out of view.
margin_opposite_edge adds extra reserved space on the inward side of the bar (below a top bar, above a bottom bar, and so on for vertical bars). The bar itself does not move; the compositor exclusive zone grows so tiled and maximized windows stop further away. Use it when a compositor ignores or under-reserves struts — for example Labwc with a maximized window that still touches the bar. Requires reserve_space = true. The default is 0.
layer = "overlay" shows the bar above fullscreen apps. Attached panels (for example Control Center when it attaches to the bar) follow the bar’s layer. Centered or floating panel placements keep each panel’s own layer (typically overlay).
The IPC bar-layer-set <top|overlay> [bar-name] [monitor-selector] switches the layer of matching bar instance(s) at runtime; omit bar-name to update every bar. The change is transient and resets to the configured layer on the next config reload. This is handy for peeking the bar above a fullscreen window on a keybind, for example in niri:
binds { Mod+B { spawn-sh "noctalia msg bar-layer-set overlay"; } Mod+Shift+B { spawn-sh "noctalia msg bar-layer-set top"; }}Widget Capsule
Section titled “Widget Capsule”Each widget can have a capsule (pill-shaped background + optional border). Settings cascade from bar defaults down to per-widget overrides.
Bar-level defaults
Section titled “Bar-level defaults”Set under [bar.<name>] or [bar.<name>.monitor.*]:
| Setting | Type | Default | Description |
|---|---|---|---|
capsule | bool | false | true gives every widget a capsule unless [widget.*] sets capsule = false. |
color | string | (unset) | Default icon + label color role for every widget on this bar; fixed hex colors are also supported. |
icon_color | string | (unset) | Overrides color for icons only on this bar; fixed hex colors are also supported. When unset, icons inherit color. |
capsule_radius | number | (auto pill) | Default capsule corner radius in logical pixels before scale is applied (clamped 0–80). Also used by workspace pills and taskbar workspace groups. Omit to use automatic pill radius. |
capsule_fill | string | surface_variant | Default capsule background color role; fixed hex colors are also supported. |
capsule_foreground | string | (unset) | Default icon + label color role for capped widgets; fixed hex colors are also supported. |
capsule_padding | number | 6 | Inner padding in logical pixels before scale is applied (clamped 0–48). |
capsule_opacity | number | 1.0 | Capsule background opacity (0.0–1.0). |
capsule_border | string | (omitted) | Color role for the capsule border; fixed hex colors are also supported. If omitted, no border by default. If present as "", no border. |
Per-widget overrides
Section titled “Per-widget overrides”Set under [widget.<name>]:
| Setting | Type | Default | Description |
|---|---|---|---|
scale | number | 1.0 | Multiplies the owning bar’s scale for this widget only (clamped 0.2–2.5). |
capsule | bool | (from bar) | Omit to inherit bar flag; false disables; true forces on. |
capsule_radius | number | (from bar) | Per-widget corner radius (0–80). Omit to inherit; omit at every level for automatic pill radius. |
capsule_fill | string | (from bar) | Capsule background color role; fixed hex colors are also supported. |
capsule_foreground | string | (from bar) | Icon + label color role when capsule is visible; fixed hex colors are also supported. color takes priority over this. |
capsule_padding | number | (from bar) | Per-widget inner padding (0–48). |
capsule_opacity | number | (from bar) | Per-widget capsule background opacity. |
capsule_border | string | (from bar) | Color role for the capsule border; fixed hex colors are also supported. Omit to inherit bar policy. Present but empty/whitespace-only = no border. |
color | string | (unset) | Icon + label color role with or without capsule; fixed hex colors are also supported. Resolution order: color → capsule_foreground → built-in defaults. |
icon_color | string | (unset) | Overrides color for icons only. Resolution order for icons: icon_color → color → capsule_foreground → built-in defaults. Labels always use color. |
Prefer color role names so widget styling follows the active palette. Role names use snake_case (e.g. on_surface, surface_variant, secondary). Use fixed hex colors only when you deliberately want a non-palette color; hex may use #RGB, #RGBA, #RRGGBB, or #RRGGBBAA. Invalid role names or malformed hex colors are treated as config errors.
The capsule is hidden automatically when a widget reports no visible ink (empty tray, absent battery, invisible root). Subclasses may override Widget::shouldShowBarCapsule().
The workspaces widget reuses its resolved capsule_radius for workspace pills. The taskbar also reuses its resolved
capsule_radius for workspace group capsules when group_by_workspace = true, even if the taskbar’s outer widget
capsule is disabled.
[bar.main]capsule = truecapsule_fill = "surface_variant"capsule_opacity = 0.9capsule_border = "outline"
# Accent bar: primary fill + matching text[bar.accent]capsule = truecapsule_fill = "primary"capsule_foreground = "on_primary"capsule_padding = 10capsule_radius = 8.0
[widget.volume]capsule_fill = "secondary"capsule_radius = 0.0 # square this widget's capsulecapsule_border = "" # no border on this widget
[widget.spacer]type = "spacer"capsule = falseCapsule groups
Section titled “Capsule groups”A capsule group is a container that holds several widgets and renders them inside one shared capsule with a single common style. A group is a real item in the lane — its members live inside it, so inserting another widget elsewhere can never split it. Grouping is managed entirely from the Settings GUI:
- Open Settings → Bar → Bar Widgets.
- Drag one widget onto the middle of another (using its drag handle) to instantly create a group from the two. Or tick the checkbox on two or more adjacent widgets and click Group. Either way they move into one group container and a style editor opens.
- Edit the group’s background, border, foreground, padding, radius, and opacity — every member updates together.
- Drag a widget onto the group to add it; drag a member out, or use its eject button, to remove it. Reorder members by dragging them within the group. Drag the group’s own handle to move the whole group along the lane or into another lane.
- To dissolve a group, open its style editor and click Ungroup (its members return to the lane in place).
When dragging a widget, hovering the middle of another widget combines them into a group, while hovering the top/bottom edge drops it between widgets as a normal reorder.
Under the hood each group is one [[bar.<name>.capsule_group]] table holding an ordered members list plus the
shared style, and the lane references the group with a single group:<id> token. You normally do not write these
by hand:
[bar.main]end = ["clock", "group:g1", "session"] # the group occupies one lane slot
[[bar.main.capsule_group]]id = "g1"members = ["network", "bluetooth", "volume"]fill = "surface_variant"padding = 6.0opacity = 0.9# border, foreground, radius are optional; omit border for no outlineA group’s style can be overridden per monitor by declaring [[bar.<name>.monitor.<match>.capsule_group]] tables
with matching id values. A reused widget name (e.g. several spacer instances) can appear in different groups
independently — membership is positional, not a per-widget flag.