# SwBr config — ~/.config/swbr/config
# One key=value per line. '#' starts a comment. Blank lines are ignored.
# Every key here also works on the command line (key=value or --key=value);
# command-line values win over the file. See `swbr --help` for the list.
#
# Inline comments after a value are stripped, but a '#' inside quotes is not,
# so foreground="#19cb00" inside a command is safe.
#
# All sizes are LOGICAL pixels: swbr multiplies them by the output scale,
# so the bar keeps its size on a HiDPI screen. ui_scale scales the text on
# top of that, and height=0 derives the height from the font.
# --- placement ---
layer=top # background | bottom | top | overlay
# top floats above tiled windows but below fullscreen ones;
# overlay stays above fullscreen too
position=top # top | bottom
height=0 # 0 = derive from the font size
exclusive=0 # 0 = float above the windows, they keep the whole screen
# 1 = reserve the space, windows tile below the bar
margin=0 # gap to the screen edge
side_margin=0 # gap left and right
min_width=800 # 0 = span the whole output. Otherwise the bar floats and
# sizes itself to its content: never narrower than this,
# never wider than the screen. Cells appearing (charging,
# docker) grow it instead of being cut off
shrink_px=24 # it grows at once but ignores getting narrower by less than
# this, so 99% -> 100% or a temperature losing a digit does
# not move the whole (centred) bar
shrink_s=30 # a bigger narrowing has to hold this long first
align_x=center # where the floating bar sits: center | left | right
radius=10 # corner radius. The two corners that touch the screen edge
# stay square, so the bar sits flush against it
outputs= # comma separated output names, empty = one bar per output
# --- workspaces ---
ws_names=0 # 0 = number only ("3" out of "3:web"), 1 = the full name
ws_pad=11
ws_gap=4
ws_min_w=0
ws_inset=0 # gap above and below the buttons. 0 = the button spans
# the full bar height, top to bottom
ws_radius=0 # 0 = square corners. -1 = follow radius, or a full pill
# when ws_inset > 0
ws_border=0 # outline thickness in px around every button. 0 = none,
# which is the plain look: the gap between buttons is the
# only division
ws_slots=10 # always show the numbers 1..10, open or not. A free number
# is the same button in the same place, just faint, and
# clicking one opens that workspace. It also means the bar
# is the same width folded and open. 0 = only what is open
ws_empty_alpha=35 # how visible a free number is, percent
ws_click=1
scroll_workspace=0
mode_show=1 # show the binding mode (resize, ..)
# --- text ---
# By default swbr uses the desktop's monospace font (via fontconfig).
# font=DejaVu Sans Mono
# font_alt=Noto Sans Symbols 2 # for glyphs the main font lacks
ui_scale=1.0 # master text size. 1.3 = bigger, 0.85 = smaller
text_px=19 # status text
ws_px=19 # workspace labels
text_y=-2 # nudge every baseline, logical px, negative = up
pad_x=12 # padding at the left end
pad_right=20 # padding at the right end, so the last cell can breathe
scroll_speed=45 # text cells: logical px per second while hovered
scroll_pause=900 # ms held at each end before scrolling
# ---------------------------------------------------------------------------
# CELLS — the right hand side
#
# cell=NAME declares a cell. The order of these lines is the order
# the cells appear in, left to right.
# NAME.cmd=... shell command; whatever it prints is the cell text.
# Print nothing and the cell disappears, separator too.
# NAME.interval=2 seconds between runs. 0 = run once at start,
# -1 = keep the command running and take every line.
# NAME.fmt=%s C %s is replaced by the output. No printf escaping:
# a % in here is just a %.
# NAME.color= text colour NAME.bg= cell background
# NAME.warn=>70:cecb00 NAME.crit=<12:ff2222
# recolour when the first number in the output is at or
# above (>) / at or below (<) the threshold
# NAME.min_w=280 keep this width even when the text is shorter
# NAME.max_w=0 clip beyond this width
# NAME.align=right left | center | right, inside min_w
# NAME.sep=0 do not draw a separator after this cell — that is how
# two cells are combined into one group
# NAME.empty=TEXT drawn instead of hiding when the command prints
# nothing — keeps the cell's area clickable
# NAME.gap= space after this cell, overrides cell_gap. 0 glues it
# to the next one: equal bg colours merge into one box
# NAME.pad=0 padding inside the cell, left and right
# NAME.slim=auto folded strip: auto | tick | bar | clock | presence |
# off. presence = a plain block in the running colour,
# present only while the program is
# NAME.slim_color= override this cell's colour on the strip. Without it
# the strip reuses the colour the cell's own text has,
# so a cyan clock stays cyan when folded
# NAME.slim_color2= second colour: the minutes of a clock cell
# NAME.slim_min=0 the value range a gauge maps, so a temperature or a
# NAME.slim_max=100 load average can be a gauge as well as a percent
# NAME.markup=1 the output may use ..
# NAME.button2=CMD click the cell (1 left, 2 middle, 3 right, 4/5 wheel)
# ---------------------------------------------------------------------------
cell_gap=14
cell_inset=3 # gap above and below a cell background
cell_radius=5 # corner radius of a cell background
separator=│
separator_color=3a4654ff
# --- messages ---------------------------------------------------------------
# Anything can shout at the bar:
# swbr --msg "warn: backup running"
# swbr --msg "error: disk full"
# swbr --msg clear
# echo "info: hello" > $XDG_RUNTIME_DIR/swbr.fifo
# While a message is up it takes over the cmus cell, so the title turns into
# the warning and turns back into the title afterwards. Click it to dismiss.
msg_target=cmus
redraw_ms=250 # or refresh=0.25, the same thing in seconds. Try 1000 if
# you would rather the bar redrew once a second.
# Cell updates are held this long and merged into one
# frame. Every frame is a round trip through the
# compositor, and ten cells on their own timers otherwise
# wake the bar three times a second. Anything from sway --
# a workspace switch, urgency -- skips the hold and is
# drawn on the next frame
msg_timeout=8 # seconds; 0 = stays until cleared
msg_flash=1 # folded bar turns fully into the message colour
msg_info=89afc4
msg_warn=cb9b00
msg_error=e0533c
# --- cmus: two cells glued into one box -------------------------------------
# The title is a text cell: cut with ".." and scrolls while hovered. The
# controls are their OWN cell so a long title can never truncate them away,
# and they keep a fixed clickable area when nothing plays (empty=).
# Both share one background: same bg + gap=0 merges them into a single box.
# Note: the U+23Fx media glyphs (pause, stop) are missing from most mono
# fonts, so the icon is derived here from glyphs that actually exist.
cell=cmus
cmus.source=cmus # swbr runs cmus-remote itself: no script, and playing vs
# paused is read from cmus rather than guessed
cmus.cmus_fmt=%n # just Artist — Title; %i is the icon, %a %t %s also work
cmus.interval=2
cmus.scroll=1 # text cell: cut with ".." and scroll it while hovered
cmus.min_w=210 # fixed slot, so nothing else on the bar ever shifts
cmus.align=left
cmus.pad=10 # breathing room inside the background box
cmus.bg=ffffff14 # a faint lighter panel, not a solid block
cmus.gap=0 # glued to the controls: one continuous background
cmus.sep=0
cmus.slim=off # the title says nothing at five pixels
cmus.button1=pgrep -x cmus >/dev/null && cmus_control toggle || foot -e cmus
cmus.button2=cmus_control toggle
cmus.button4=cmus_control prev
cmus.button5=cmus_control next
cell=cmusui
cmusui.source=cmus # the same source, laid out as just the icon
cmusui.cmus_fmt=%i # ▶ while playing, ⏸ while paused, following the real state
cmusui.interval=2
cmusui.empty=♪ # cmus not running: still a button, click it to start
cmusui.min_w=38 # never shrinks, never truncates
cmusui.align=center
cmusui.pad=4
cmusui.bg=ffffff14
cmusui.color=e8b81a
cmusui.slim=media # folded: a full block while playing, a short dim one when
# paused, nothing at all when cmus is stopped.
# source=cmus knows which, so slim_on is not needed.
cmusui.slim_w=26 # wider than the other ticks, so it is easy to find and hit
cmusui.button1=pgrep -x cmus >/dev/null && cmus_control toggle || foot -e cmus
cmusui.button2=cmus_control toggle
cmusui.button4=cmus_control prev
cmusui.button5=cmus_control next
# --- volume ----------------------------------------------------------------
cell=volume
volume.cmd=alsa_control volume get
volume.interval=2
volume.button2=alsa_control mute toggle
volume.slim=bar
volume.slim_w=56 # folded: a gauge needs room for its ten blocks, and this
# cell is only two characters wide. Wins over slim_align,
# growing about the middle so it stays where the text was
volume.slim_color=a9d8f5 # light baby blue, its own thing on the strip
# --- docker and mpv share one group: docker.sep=0 joins them ---------------
cell=docker
docker.cmd=pgrep -x dockerd >/dev/null || exit 0; docker ps --format '{{.Image}}' | sed 's/:.*//' | xargs
docker.interval=10
docker.color=19cb00
docker.sep=0
docker.slim=presence # folded: a block in the running colour, there only
# while dockerd is, so a glance says it is up
cell=mpv
mpv.source=window # asks sway which workspaces have an mpv window, no jq and
# no swaymsg. %n the name, %w the workspaces, %c how many
mpv.program=mpv # matched against app_id, the X11 class, then the title
mpv.src_fmt=%n [%w]
mpv.interval=3
mpv.slim=presence # same for mpv: started something in the background?
mpv.slim_color=3ea6ff # its own colour, so you can tell the two apart
# --- clock -----------------------------------------------------------------
cell=clock
clock.cmd=date '+%H:%M %a %d.%m'
clock.interval=5
clock.hover_click=1 # the calendar opens on a click and stays until you
# click again, rather than following the pointer
clock.hover_cal=1 # the panel: this month, with today in the hl colour.
# Built in, so the columns line up whatever the font and
# nothing is spawned. hover_cmd=cal would work too
# clock.hover_cmd=cal # the panel: this month, from cal(1). Run once when
# the hover starts, not on a timer
clock.slim=clock # twelve dots for the hour, in the same cyan the clock
# uses in the full bar (taken from its own markup)
clock.slim_color2=2f7f9f # the minutes bar, and the morning hours. Every dot
# up to the hour is lit, so you count bars: 2 at 02:00 and
# still 2 at 02:55. Main colour after noon, this one
# before, so 13:00 and 1:00 differ
# --- cpu load and temperature, joined --------------------------------------
cell=cpu
cpu.cmd=cpu_in_percent
cpu.fmt=%s%
cpu.interval=2
cpu.color=e1eeff
cpu.warn=>70:cecb00
cpu.crit=>80:cc0403
cpu.min_w=52
cpu.slim=bar # a gauge in the folded strip, 0..100%
cpu.sep=0
cell=temp
temp.cmd=t=$(cpu_temp); [ "${t%%.*}" -ge 70 ] 2>/dev/null && printf '%s°C' "$t"
temp.interval=5
temp.warn=>70:cecb00
temp.crit=>85:cc0403
temp.slim=bar # a gauge too, but degrees are not percent:
temp.slim_min=40 # so map 40..100 C onto the gauge
temp.slim_max=100
# --- battery ---------------------------------------------------------------
# The draw and the time left live in the hover panel now, so they are there
# when you want them and not taking width when you do not. Uncomment this if
# you would rather have them in the bar all the time.
# cell=power
# power.source=battery
# power.src_fmt=%w %h
# power.color=b3c0cd
# power.slim=off
# power.sep=0
# The marker at the end tells the folded strip which way it is going:
# + charging, - discharging, = full. In the full bar it reads as ⚡ or nothing.
cell=battery
battery.source=battery # reads /sys itself: 83+ filling, 83- draining, no
# sign when full
# battery.bat_path=/sys/class/power_supply/BAT1 # if yours is not BAT0, or
# you have two. Left out, swbr takes the first thing with a
# capacity file, preferring one called BAT*
# battery.src_fmt=%c%i %w %h # %c capacity, %i sign, %w watts now, %h hours
# left, %s the word. This one reads "83- 21.5W 1.7h"
# battery.cmd=... # setting a command switches the cell back to your own
# script; source= and cmd= are the two ways, never both
battery.hover=%w %h %e # the panel: 21.5W 1.7h left, or "0.3h to full" on
# the cable. %e says which way the clock runs; with
# nothing to report it falls back to the word Full
# or Discharging
battery.hover_click=1 # opens on a click and stays until you click again,
# rather than following the pointer
battery.hover_slim=0 # 1 = folded, resting on it opens the bar so the
# panel has somewhere to go. 0 = no hover when folded
battery.interval=10
battery.color=e1eeff
battery.warn=<30:cecb00
battery.crit=<12:ff2222
battery.slim=bar
battery.slim_w=56 # same again: wide enough to count the blocks
# --- shouting when it is going flat -----------------------------------------
battery.alert=30,20,10,5,1 # percentages. Each fires once on the way down and
# rearms when the charge comes back above it, or when the
# cable goes in — no counter file, no repeats at 19%
battery.alert_msg=BATTERY %c%% # taken over the bar like any other message;
# the last level is sent as an error, the rest as warnings
battery.alert_tone=250 # Hz. A sine with soft edges, written straight to
battery.alert_ms=1050 # the player — no click, unlike a test tone.
battery.alert_beeps=2 # 0 Hz for silence
battery.alert_gain=35 # how loud the tone itself is, percent of full
# scale. Costs nothing and touches no mixer, so try this
# before alert_vol_min below. 80 is loud but still clean
battery.alert_play=aplay -q -f cd -
# on pipewire/pulse instead:
# paplay --raw --rate=44100 --format=s16le --channels=2
# An alert nobody hears is not an alert. If the mixer is below this, swbr
# raises it for the tone and puts it straight back — even if the alert is
# killed half way through. ALSA has no per-stream level, so this is the master
# control: music gets louder too, for the second or two the tone lasts.
# 0 = never touch the mixer.
battery.alert_vol_min=50
battery.alert_vol_get=amixer -M get Master | grep -o '[0-9]*%' | head -1
battery.alert_vol_set=amixer -M -q set Master %v%
# any pair of commands works: the first prints a number,
# the second takes %v. Your own alsa_control, pactl,
# wpctl -- whatever you already use
# A muted output stays muted: swbr cannot tell deliberate
# silence from an accident. Append `unmute` to the set
# command if you would rather it spoke anyway
battery.alert_cmd=[ %c -le 1 ] && poweroff
# run at every level, and ONLY at a level: the guard can
# only ever be true if 1 is in the list above, which is why
# it is. %c is the percentage. Check it works without
# waiting for a flat battery: swbr --alert-test
# poweroff needs root — give your user a sudo rule for it,
# or the command silently does nothing
# --- folding ---------------------------------------------------------------
# Put the pointer over the bar and press the key: the bar rolls up into a thin
# strip. Press it again to roll it back out.
hide_key=space # matched by physical key, not by layout
hover_keys=1 # grab the keyboard while the pointer is over the bar.
# That is what makes hide_key work without clicking first.
collapsed_px=5
anim_ms=120
slim_align=1 # folded: each mark takes its cell's whole place, same
# left edge and same width, so toggling changes the height
# of the bar and nothing else.
# 0 = pack them against the right edge instead
signals=1 # the folded strip still says something: every workspace
# keeps a fixed slot on the left (slot 3 is always
# workspace 3), the clock becomes twelve dots, and cells
# with a percentage become little gauges
slim_ws_slots=10 # only for slim_align=0, the packed strip: this many
# slots. Lined up, the strip is exactly the open bar's own
# buttons — same set, same place, same width — so set
# ws_slots above to choose how many
slim_bar_segs=10 # folded gauges are split into this many blocks, so four
# lit blocks reads as forty percent without measuring it.
# 0 = one solid bar. Halves itself in a cell too narrow to
# show the dividers — give that cell a slim_w instead
slim_bar_gap=2 # px between those blocks
slim_bar_pad=3 # px either side of a folded gauge. A gauge takes its
# whole cell, so without this it runs edge to edge
slim_ws_w=0 # folded slot width in px. 0 = each slot copies its own
# button in the open bar, so nothing moves or resizes
start_collapsed=0 # 1 = start folded. Command line: swbr --slim
# Without the mouse. Bind either one to a key in your sway config:
# bindsym $mod+b exec swbr --toggle (also --fold / --unfold)
# bindsym $mod+b exec pkill -USR1 swbr
# --- mouse: anywhere on the bar, folded or not ------------------------------
# Cell bindings win where a cell is; these catch everywhere else, which is the
# reliable way to control music while the bar is folded down to a few pixels.
# Same buttons your old sway bar block used.
button2=cmus_control toggle
button6=cmus_control prev
button7=cmus_control next
# ---------------------------------------------------------------------------
# LAYOUT — one line that places everything. Keep it last: it assigns group,
# order and separators, overriding pos= and sep= above.
#
# || splits groups. One part = left only, two = left and right,
# three = left, center, right.
# , separates items.
# (..) glues items together with no separator between them.
# names are cell names plus the built-ins "workspaces" and "mode".
# ---------------------------------------------------------------------------
bar={workspaces,mode||(cmus,cmusui),volume,(docker,mpv),clock,(cpu,temp),battery}
# --- colors: #rrggbb or #rrggbbaa ------------------------------------------
bg=26313fe6 # the bar itself
text=e1eeffff # default cell colour alias: statusline
dim=5a6b7aff
accent=89afc4ff # visible-but-not-focused workspace tick
hl=cb9b00ff # focused workspace tick in the folded strip
urgent=e0533cff
ws_sort=1 # order them by number. sway lists them grouped by output,
# so two monitors otherwise give 1 2 5 8 3 10
ws_other=1 # 1 = also show the workspaces of your other monitors, as
# short pills on the baseline so they are clearly not this
# screen's
ws_cpu=1 # a dot on each workspace button showing how much
# processor time the windows on it are using. Folded, the
# workspace slot fills from the bottom instead
ws_cpu_interval=3 # seconds between samples
# tree_min_s=5 # the load dots and source=window cells need sway's tree.
# It is fetched only when sway reports a change, and never
# more often than this. no_tree=1 stops asking entirely
ws_cpu_idle=0.01 # in cores. Under this a workspace counts as doing
# nothing and stays blank. Above it, but under ws_cpu_min,
# one faint dot: a player, a polling script, something
# small but alive
ws_cpu_min=0.25 # where the real scale starts, in cores: 1.0 is one core
# kept busy
ws_cpu_full=4 # four cores fills the column. A share of the whole
# machine was the wrong unit: on sixteen threads two busy
# cores are twelve percent, which rounds to nothing
running=3ddc84ff # folded strip: this program is up, and where a charging
# gauge's pips ramp to
slim_warm=e8963cff # where the direction pips start: orange into red while
# draining, orange into green while charging
ws_bg=00000000 # inactive button: nothing, the bar shows through
ws_fg=b3c0cdff
ws_focused_bg=cb9b00ff # the appwheel orange
ws_focused_fg=141414ff
ws_visible_bg=3c4a5cff
ws_visible_fg=e1eeffff
ws_urgent_bg=e0533cff
ws_urgent_fg=141414ff
mode_bg=cb9b00ff
mode_fg=141414ff