---
title: Hyprland Waybar Configuration and Theme
url: https://doc.liz6.com/en/tools/hyprland/04-waybar-theming
locale: en
area: tools
tags:
- tools
- hyprland
date: null
modified: 2026-08-11
description: Hyprland Waybar Configuration and Theme This Waybar serves as the fallback status bar for the Hyprland desktop. The current primary desktop uses Quickshell, and…
---

# 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

```text
~/.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`:

```jsonc
{
  "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:

```jsonc
"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:

```jsonc
"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:

```jsonc
{
  "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:

```jsonc
"group/pulseaudio": {
  "orientation": "horizontal",
  "modules": [
    "pulseaudio#output",
    "pulseaudio#input"
  ],
  "drawer": {
    "transition-left-to-right": false
  }
}
```

The output module separates three types of operations:

```jsonc
"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:

```jsonc
{
  "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:

1. Clicking the same button again closes the panel.
2. Before opening another panel, close the current one to avoid overlapping control centers.
3. `panel_watch.sh` monitors 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:

```css
@import "theme.css";

* {
  all: initial;
  color: @main-fg;
}

@import "styles/fonts.css";
@import "styles/modules-center.css";
@import "styles/modules-left.css";
@import "styles/modules-right.css";
@import "styles/states.css";
```

The current `theme.css` is a static Catppuccin Mocha theme. It defines base colors first, then maps them to semantic colors:

```css
@define-color lavender #b4befe;
@define-color text     #cdd6f4;
@define-color crust    #11111b;

@define-color accent   @lavender;
@define-color main-bg  @crust;
@define-color main-fg  @text;
@define-color warning  @yellow;
@define-color critical @red;
```

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:

```toml
[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:

```css
@define-color accent  #{{colors.primary.default.hex}};
@define-color main-bg #{{colors.surface.default.hex}};
@define-color main-fg #{{colors.on_surface.default.hex}};
@define-color main-br #{{colors.outline.default.hex}};
@define-color warning #{{colors.tertiary.default.hex}};
@define-color critical #{{colors.error.default.hex}};
```

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:

```ini
[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:

```bash
waybar -l info
```

Switch to default status bar:

```bash
systemctl --user enable --now waybar.service
```

Restore to fallback status:

```bash
systemctl --user disable --now waybar.service
```

Before enabling Waybar, stop other bars occupying the same screen edge to avoid exclusive zone, click area, or layer conflicts.

## 9. Troubleshooting Order

1. `waybar -l trace`: Check for JSONC, module, and CSS parsing errors first.
2. `hyprctl clients -j`: Confirm the window class relied upon by click scripts.
3. `fc-match`: Check Nerd Font and icon fonts.
4. Run commands under `scripts/` individually to distinguish between Waybar issues and script issues.
5. Temporarily remove `@import` statements to locate CSS parsing failures layer by layer.
6. Use `systemctl --user status waybar.service` and 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.
