On this page
Hyprland Waybar Configuration and Theme
This Waybar serves as the fallback status bar for the Hyprland desktop. The current primary desktop uses Quickshell, and the Waybar systemd service remains disabled/inactive. However, the configuration can still be started independently, making it suitable for debugging Quickshell, restoring the desktop, or when a lightweight status bar is needed.
This article is based on the actual configuration on this machine. The focus is not on dumping a massive JSON file, but on explaining how to split Waybar into maintainable modules, styles, and interaction scripts.
1. File Structure
~/.config/waybar/
├── config # Main layout, determines module order and global behavior only
├── style.css # CSS entry point
├── theme.css # Current static color scheme (Catppuccin Mocha)
├── styles/
│ ├── fonts.css
│ ├── modules-left.css
│ ├── modules-center.css
│ ├── modules-right.css
│ └── states.css
├── modules/
│ ├── hyprland/*.jsonc # Workspaces, windows, input methods
│ ├── custom/*.jsonc # User, separators, power
│ └── *.jsonc # CPU, memory, network, Bluetooth, volume, etc.
└── scripts/ # Click, scroll, and popup control scripts
The main configuration imports module fragments via include:
{
"include": [
"~/.config/waybar/modules/*.jsonc",
"~/.config/waybar/modules/custom/*.jsonc",
"~/.config/waybar/modules/hyprland/*.jsonc"
]
}
This way, when adding new modules, you only need to add a JSONC file; the main configuration will not gradually bloat into an unmanageable single file.
2. Three-Part Layout
The actual layout is divided into three groups: left, center, and right:
"modules-left": [
"custom/user",
"hyprland/workspaces",
"hyprland/window"
],
"modules-center": [
"hyprland/language",
"temperature",
"memory",
"cpu",
"idle_inhibitor",
"clock#time",
"clock#date",
"network",
"bluetooth"
],
"modules-right": [
"mpris",
"group/pulseaudio",
"battery",
"custom/power"
]
The real configuration also includes left and right angled separators between modules. They are merely presentation modules and do not handle state logic; business modules continue to work normally even if the separators are removed.
Among the global options, the following are worth keeping:
"layer": "top",
"mode": "dock",
"spacing": 0,
"reload_style_on_change": true
reload_style_on_change reloads the style when the CSS file changes, so there is no need to repeatedly restart Waybar when tweaking themes.
3. Hyprland Workspaces
The workspace module keeps a fixed 5 workspaces and allows switching via scroll wheel:
{
"hyprland/workspaces": {
"format": "{icon}",
"format-icons": {
"active": "",
"default": ""
},
"persistent-workspaces": {
"*": 5
},
"on-scroll-up": "hyprctl dispatch workspace +1",
"on-scroll-down": "hyprctl dispatch workspace -1",
"cursor": true
}
}
This relies on Nerd Font icons. If Waybar displays boxes instead of icons, check the font first, rather than modifying the JSON.
4. Volume: Drawer + Click/Scroll Scripts
Output and input volumes are placed in a horizontal drawer:
"group/pulseaudio": {
"orientation": "horizontal",
"modules": [
"pulseaudio#output",
"pulseaudio#input"
],
"drawer": {
"transition-left-to-right": false
}
}
The output module separates three types of operations:
"on-click": "~/.config/waybar/scripts/volume output mute",
"on-scroll-up": "~/.config/waybar/scripts/volume output raise",
"on-scroll-down": "~/.config/waybar/scripts/volume output lower"
The input module reuses the same script, only changing the object to input. This interface is easier to test than copying wpctl/pactl commands in JSON, and it unifies handling of step size, default devices, and error messages.
5. Network and Control Panel
The network module keeps icons compact and puts detailed information in the tooltip:
{
"network": {
"interval": 10,
"format-ethernet": "",
"format-wifi": "{icon}",
"format-disconnected": "",
"on-click": "ghostty -e ~/.config/waybar/scripts/network",
"on-click-right": "~/.config/waybar/scripts/network off",
"tooltip-format-wifi": "<b>Network</b>: {essid}\n<b>IP</b>: {ipaddr}/{cidr}\n<b>Strength</b>: {signalStrength}%"
}
}
Volume and Bluetooth buttons open the GTK panel via a unified panel_toggle.sh. The script follows three rules:
- Clicking the same button again closes the panel.
- Before opening another panel, close the current one to avoid overlapping control centers.
panel_watch.shmonitors the Hyprland active window and automatically closes the panel when focus leaves it.
When determining if a panel exists, query the window class from hyprctl clients -j, rather than relying solely on pgrep. Programs like Blueman may be launched by Python, so the process name does not reliably represent the window the user actually sees.
6. CSS Layering
style.css is responsible only for composition:
}
The current theme.css is a static Catppuccin Mocha theme. It defines base colors first, then maps them to semantic colors:
@@@@@@@@@@@@@
Business styles only reference semantic colors like @main-bg, @accent, and @critical, avoiding direct use of dozens of hexadecimal values. When changing themes, only theme.css needs to be replaced.
7. Optional: Let Matugen Generate Waybar Theme
Currently, Matugen on this machine manages Quickshell, Hyprland, Hyprlock, Fuzzel, Fcitx5, GTK, Ghostty, KDE, and Zellij, but has not yet generated Waybar's theme.css. If you want Waybar to follow the wallpaper, you can add a new template:
[templates.waybar]
input_path = '~/.config/matugen/templates/waybar/theme.css'
output_path = '~/.config/waybar/theme.css'
post_hook = 'pkill -SIGUSR2 waybar 2>/dev/null || true'
The template outputs only semantic colors, keeping existing module CSS unchanged:
@}};
@}};
@}};
@}};
@}};
@}};
Matugen's template variable syntax may change with versions. Before integrating, render to a temporary file using the currently installed version to confirm there are no empty variables, then overwrite the official theme.css.
8. systemd Startup and Fallback
Waybar user service:
[Unit]
Description=Highly customizable Wayland bar for Hyprland
PartOf=graphical-session.target
After=graphical-session.target
ConditionEnvironment=WAYLAND_DISPLAY
[Service]
ExecStart=/usr/bin/waybar
ExecReload=kill -SIGUSR2 $MAINPID
Restart=on-failure
[Install]
WantedBy=hyprland-session.target
Manual trial run:
Switch to default status bar:
Restore to fallback status:
Before enabling Waybar, stop other bars occupying the same screen edge to avoid exclusive zone, click area, or layer conflicts.
9. Troubleshooting Order
waybar -l trace: Check for JSONC, module, and CSS parsing errors first.hyprctl clients -j: Confirm the window class relied upon by click scripts.fc-match: Check Nerd Font and icon fonts.- Run commands under
scripts/individually to distinguish between Waybar issues and script issues. - Temporarily remove
@importstatements to locate CSS parsing failures layer by layer. - Use
systemctl --user status waybar.serviceand journal to check the session environment.
The core of this structure is to separate layout, module behavior, interaction scripts, and color themes. Waybar can remain a lightweight fallback or evolve independently; whether to integrate Matugen is merely a choice of theme source and should not affect the modules themselves.