Skip to main content

Command Palette

Search for a command to run...

Hyprland: Dynamically Theme Wayle With Matugen

Published
5 min read
Hyprland: Dynamically Theme Wayle With Matugen
F
I’m FlareXes. I work around Linux, networking, security, and internet infrastructure as part of my job. University was mostly trash, honestly. The people were good. The content wasn’t. Staying didn’t make sense, so I left.

For those who don't know, Wayle is a project from the same creator as Hyprpanel, which is apparently going to be deprecated soon. Probably because of that, I couldn't find a proper theming guide anywhere either. So here we are.

This guide themes Wayle using Matugen instead of Wayle's built-in integration because:

  • it's simpler

  • gives you actual control

  • works like every other sane Matugen setup

  • doesn't force Wayle to become your wallpaper manager too for absolutely no reason.

No overengineered “ecosystem” nonsense. Just generate colors and move on with your life.

Install Dependencies

You need both matugen and wayle installed.

yay -S matugen wayle-bin

Once installed, you can control Wayle using:

wayle panel start
wayle panel stop
wayle panel restart

Wayle usually hot reloads configs automatically, but if it decides to act like a KDE app from 2014:

wayle panel restart

Now Wayle should be running.

Setup Matugen Theming

Step 1: Add Wayle Template to Matugen

Edit:

~/.config/matugen/config.toml

Add:

[templates.wayle]
input_path = "~/.config/matugen/templates/wayle.toml"
output_path = "~/.config/wayle/matugen/colors.toml"

This tells Matugen:

  • where the template lives

  • where to dump generated colors

Nothing magical. Just file generation.

Step 2: Create the Wayle Matugen Template

Create:

~/.config/matugen/templates/wayle.toml

Add:

[styling.palette]
primary = "{{ colors.primary.default.hex }}"
fg = "{{ colors.primary.default.hex }}"
fg-muted = "{{ colors.secondary.default.hex }}"
elevated = "{{ colors.background.default.hex }}"

[modules.dashboard]
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.hyprland-workspaces]
container-bg-color = "{{ colors.background.default.hex }}"
active-color = "{{ colors.primary.default.hex }}"

[modules.systray]
button-bg-color = "{{ colors.background.default.hex }}"

[modules.clock]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.battery]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.bluetooth]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.network]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.volume]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.microphone]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.media]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.notification]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

[modules.window-title]
label-color = "{{ colors.primary.default.hex }}"
button-bg-color = "{{ colors.background.default.hex }}"
icon-color = "{{ colors.background.default.hex }}"
icon-bg-color = "{{ colors.primary.default.hex }}"

This is where the actual wallpaper-based colors get mapped into Wayle. You can experiment with different Material colors if you want.

Step 3: Import Generated Colors Into Wayle

Edit:

~/.config/wayle/config.toml

Add:

imports = ["./matugen/colors.toml"]

[bar]
bg = "transparent"

button-variant = "icon-square"
padding-ends = 0.8
button-icon-size = 0.8
button-label-size = 0.8
button-rounding = "lg"
button-icon-position = "end"

[[bar.layout]]
monitor = "*"
show = true
left = [
    "dashboard",
    "hyprland-workspaces",
]
center = [
    "systray",
    "clock",
]
right = [
    "battery",
    "volume",
    { name = "group", modules = [
    "bluetooth",
    "network",
] },
    "notifications",
]

# modules
[modules.hyprland-workspaces]
app-icons-show = true
icon-size = 0.70
label-size = 0.80

[modules.network]
label-show = false

[modules.bluetooth]
label-show = false

[modules.notification]
label-show = false

This imports the generated Matugen colors dynamically.

Also removes the giant background bar because personally I think it looks cleaner without turning the top of your screen into a solid rectangle block.

Step 4: Generate Theme From Wallpaper

Run:

matugen image ~/Pictures/Wallpapers/OGs/cyan-moon.png

That's it.

Your Wayle theme should now follow your wallpaper colors automatically.

Extend Theme to Support More Modules

This setup only themes the modules you explicitly define.

And yes, that means you need to add modules manually one by one because currently Wayle's theming structure is very module-centric. Slightly annoying, but manageable.

To see all available modules and their default values:

wayle config default --stdout

This dumps the full default config.

For example, you'll see something like:

[modules.hyprland-workspaces]
workspace-padding = 0.5
icon-size = 1.0
label-size = 1.0
workspace-ignore = []
active-indicator = "background"
active-color = "accent"
occupied-color = "fg-muted"
empty-color = "fg-subtle"
container-bg-color = "bg-surface-elevated"
border-show = false
border-color = "border-default"

You only care about theme-related values here.

So take the color fields and map them inside your Matugen template:

[modules.hyprland-workspaces]
container-bg-color = "{{ colors.background.default.hex }}"
active-color = "{{ colors.primary.default.hex }}"

That's basically the workflow:

  1. Find module defaults

  2. Copy color-related params

  3. Replace them with Matugen variables

  4. Repeat until your rice reaches enlightenment

Or until you forget what sunlight looks like.

You can keep adding all the modules you use this way.

Hopefully Wayle eventually gets a more global theming structure instead of handling everything per-module, but for now this works perfectly fine.

Why Not Use Wayle's Built-In Matugen Integration?

Because honestly:

  1. It's more complicated than it needs to be

  2. Theme generation should be handled by Matugen, not the bar itself

  3. It reduces flexibility

  4. You basically end up coupling wallpaper management to Wayle too

  5. This approach already matches how most people theme the rest of their setup

If you already use Matugen across your system, this keeps everything consistent instead of introducing another weird abstraction layer nobody asked for.

Dotfiles

You can find my full config here along with other rice-related stuff:

FlareXes Dotfiles

If you improve the config or find a cleaner approach, let me know. There's probably still a better way to structure some of this before Wayle's config evolves further.