v5-bar.md
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 = 12concave_edge_corners = true # carve the screen-edge corners inward; requires margin_edge = 0margin_ends = 100 # inset from each end of the bar along its main axismargin_edge = 0 # 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 sectionhover_highlight = true # softly tint the widget under the mouse pointer with its foreground colorscale = 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.
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).
In Settings → Bar → Shape you don’t type a negative number: each per-corner slider shows the positive radius, and an invert toggle beside it carries the sign, turning it on stores the negative value that produces the concave spike. The toggle is enabled only for the two inner corners (below); on the outer, screen-edge corners it appears disabled, since a concave spike there has no room to grow.
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 (invertible) |
|---|---|
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 (in the config file), 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.
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.
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:
eDP-1, DP-1, HDMI-A-1, …)"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.
When auto_hide = true, the bar:
margin_edge > 0, the float gap is included in the layer surface so reveal works from the physical screen edge. With margin_edge = 0, the bar stays flush to the edge; pointer hit-testing still uses the full layer surface so reveal does not immediately cancel.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.bar-show, bar-hide, bar-toggle, and bar-reserve-toggle accept optional [bar-name] [monitor-selector] arguments; omit both to affect every bar instance. bar-show/hide/toggle use the same hide/reveal animation instead of tearing down surfaces. With auto_hide, they retract or reveal the bar like pointer auto-hide; edge reveal still works after bar-hide. With auto_hide off, bar-hide hides the bar and always frees the compositor gap until bar-show (or bar-toggle while showing), regardless of reserve_space. bar-reserve-toggle toggles the exclusive zone reservation on or off for the matched instances.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"; }}
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.
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. |
hover_highlight | bool | true | Softly tint the widget under the mouse pointer with its own foreground color. Works with and without capsules; inside a capsule group only the hovered member lights up. The highlight never changes widget layout or spacing. Set false to disable the effect on this bar. |
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. |
enabled | bool | true | Show or hide this widget on the bar. Disabled widgets are not created and consume no resources. |
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 = false
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:
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.9enabled = true# border, foreground, radius, enabled are optional
| Setting | Type | Default | Description |
|---|---|---|---|
id | string | (required) | Group identifier. Lane entries reference the group with group:<id>. |
members | list | (required) | Ordered list of widget names belonging to this group. |
enabled | bool | true | Show or hide the entire group on the bar. Disabled groups are not populated and their widgets consume no resources. |
fill | string | surface_variant | Background color role for the shared capsule; fixed hex colors are also supported. |
border | string | (omitted) | Color role for the capsule border; fixed hex colors are also supported. Omit for no outline; present but empty ("") also means no outline. |
foreground | string | (unset) | Icon + label color role for all widgets inside this group; fixed hex colors are also supported. |
padding | number | 6 | Inner padding between the capsule edge and its widgets, in logical pixels (clamped 0–48). |
radius | number | (auto pill) | Capsule corner radius in logical pixels (0–80). Omit for automatic pill radius. |
opacity | number | 1.0 | Capsule background opacity (0.0–1.0). |
A 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.