A tiny Neovim helper for making an existing colorscheme slightly harmonize with a
matugen palette.
It does not replace your colorscheme.
It does not generate a full theme.
It does not try to become a universal design system, because that is how small
weekend ideas become architectural paperwork.
It simply takes colors from matugen, mixes them gently into selected
colorscheme colors, and lets Neovim look a little less like it was excluded from
the rest of the desktop.
My desktop theme is generated from the current wallpaper using matugen.
That means the wallpaper influences things like:
- Hyprland
- Waybar
- Rofi
- terminal colors
- possibly other infected parts of the environment
Neovim, however, usually uses a fixed colorscheme. In my case that is mostly
tokyonight-moon.
The problem is not that TokyoNight is ugly. It is not. We are not monsters.
The problem is that a fixed Neovim background can appear dull, flat, or slightly disconnected when it sits next to a wallpaper-derived desktop palette.
This is mostly a perception issue: colors change their apparent character depending on nearby colors. A background that looks good alone can look desaturated or dead when placed against a glowing blue/pink/purple wallpaper.
So the idea is:
keep the original colorscheme identity
+
borrow a very small amount of matugen mood
=
less visual disagreement
In other words:
TokyoNight Moon, but it knows what wallpaper it lives with.
matugen-mix.nvim reads a generated Lua palette file, for example:
~/.config/nvim/lua/generated/matugen-colors.lua
Then it mixes selected colorscheme values with selected matugen colors.
For example:
TokyoNight bg + 5% matugen primary = slightly warmer/lifted bg
This is meant to be subtle.
If it screams, it is wrong.
If it quietly stops annoying you, it is working.
This plugin does not:
- replace your colorscheme
- generate a complete Neovim theme
- manage wallpapers
- run
matugen - configure Hyprland, Waybar, Rofi, or your tea kettle
- promise taste, because computers are already bold enough
It only mixes colors.
My local development path:
~/Development/lua/NeoVim/matugen-mix.nvim
Suggested structure:
matugen-mix.nvim/
├── README.md
└── lua/
└── matugen-mix/
├── init.lua
└── color.lua
First, create a template for Neovim colors.
Example path:
~/.config/matugen/templates/nvim-colors.lua
Template:
-- Generated by matugen.
-- Do not edit by hand unless you enjoy losing small wars against automation.
return {
source = {
image = "{{ image }}",
},
colors = {
background = "{{ colors.background.default.hex }}",
surface = "{{ colors.surface.default.hex }}",
surface_variant = "{{ colors.surface_variant.default.hex }}",
primary = "{{ colors.primary.default.hex }}",
secondary = "{{ colors.secondary.default.hex }}",
tertiary = "{{ colors.tertiary.default.hex }}",
on_background = "{{ colors.on_background.default.hex }}",
on_surface = "{{ colors.on_surface.default.hex }}",
error = "{{ colors.error.default.hex }}",
},
}Then add the template to matugen.
Example:
[templates.nvim]
input_path = "~/.config/matugen/templates/nvim-colors.lua"
output_path = "~/.config/nvim/lua/generated/matugen-colors.lua"After running matugen, Neovim should have a generated module like this:
return {
source = {
image = "~/.config/backgrounds/woman_reading_pinker.png",
},
colors = {
background = "#161217",
surface = "#161217",
surface_variant = "#4c444c",
primary = "#e6b6f1",
secondary = "#d5c0d7",
tertiary = "#f5b7b3",
on_background = "#eae0e7",
on_surface = "#eae0e7",
error = "#ffb4ab",
},
}Test inside Neovim:
:lua print(vim.inspect(require("generated.matugen-colors")))If that prints a table, the infection route is open.
Using lazy.nvim with a local plugin directory:
{
dir = "~/Development/lua/NeoVim/matugen-mix.nvim",
name = "matugen-mix.nvim",
}Or as a dependency of the colorscheme:
{
"folke/tokyonight.nvim",
dependencies = {
{
dir = "~/Development/lua/NeoVim/matugen-mix.nvim",
name = "matugen-mix.nvim",
},
},
}Example TokyoNight configuration:
return {
{
"folke/tokyonight.nvim",
dependencies = {
{
dir = "~/Development/lua/NeoVim/matugen-mix.nvim",
name = "matugen-mix.nvim",
},
},
opts = function(_, opts)
opts = opts or {}
local matugen_mix = require("matugen-mix")
matugen_mix.setup({
enabled = true,
colors_module = "generated.matugen-colors",
palette = {
bg = 0.05,
bg_dark = 0.04,
bg_float = 0.06,
bg_sidebar = 0.06,
},
})
opts.style = "moon"
opts.on_colors = function(colors)
matugen_mix.apply_tokyonight(colors, {
accent = "primary",
})
end
opts.on_highlights = function(h, c)
h.WinSeparator = { fg = c.blue7, bg = "NONE" }
h.VertSplit = { link = "WinSeparator" }
-- Your other highlight tweaks can live here.
-- Syntax styling belongs in on_highlights.
-- Palette/background mutation belongs in on_colors.
end
return opts
end,
},
}The important distinction:
opts.on_colors = function(colors)
-- change base palette colors
endUse this for things like:
bgbg_darkbg_floatbg_sidebar
And:
opts.on_highlights = function(highlights, colors)
-- change final highlight groups
endUse this for things like:
- Treesitter groups
WinSeparatorNormalFloat- custom plugin highlights
Tiny rule:
color ingredients go in on_colors
finished decorations go in on_highlights
The plugin provides:
:MatugenMixEnable
:MatugenMixDisable
:MatugenMixToggleThe toggle is session-local by default.
That means after restarting Neovim, the plugin uses the configured value:
enabled = trueThis is intentional. Persistent state can come later, when this tiny color helper inevitably starts pretending to be infrastructure.
If using snacks.nvim, add a toggle mapping.
Example:
return {
{
"folke/snacks.nvim",
enabled = true,
opts = {
image = {
enabled = true,
doc = {
inline = false,
float = true,
max_width = 40,
max_height = 20,
},
},
explorer = { trash = false },
statuscolumn = {
enabled = true,
},
},
init = function()
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
callback = function()
local Snacks = require("snacks")
Snacks.toggle({
name = "Matugen Mix",
get = function()
return require("matugen-mix").is_enabled()
end,
set = function(state)
local matugen_mix = require("matugen-mix")
if state then
matugen_mix.enable()
else
matugen_mix.disable()
end
end,
}):map("<leader>um")
end,
})
end,
},
}Suggested keymap:
<leader>um
Meaning:
u = UI / toggles
m = Matugen Mix
Or, less officially:
m = mood infection
Default-ish setup:
require("matugen-mix").setup({
enabled = true,
colors_module = "generated.matugen-colors",
fallback_accent = "#7aa2f7",
default_amount = 0.05,
palette = {
bg = 0.05,
bg_dark = 0.04,
bg_float = 0.06,
bg_sidebar = 0.06,
},
})Whether the mix is active.
enabled = trueWhen disabled, apply_tokyonight() does nothing and the colorscheme loads
normally.
The Lua module generated by matugen.
colors_module = "generated.matugen-colors"This expects a file at:
~/.config/nvim/lua/generated/matugen-colors.lua
Which can be required as:
require("generated.matugen-colors")Used if the generated matugen file cannot be loaded.
fallback_accent = "#7aa2f7"Because failing gracefully is better than exploding during startup like a tiny Lua opera.
Default mix amount.
default_amount = 0.05This means 5%.
Recommended range:
0.03 - 0.08
Above that, the theme may stop looking gently adjusted and start looking like it has joined a neon monastery.
Defines which colorscheme fields should be mixed and by how much.
palette = {
bg = 0.05,
bg_dark = 0.04,
bg_float = 0.06,
bg_sidebar = 0.06,
}For TokyoNight, useful keys include:
bg
bg_dark
bg_float
bg_sidebar
Example:
matugen_mix.apply_tokyonight(colors, {
accent = "primary",
})Common options from the generated palette:
primary
secondary
tertiary
background
surface
surface_variant
For the current woman_reading_pinker.png wallpaper, example values were:
primary = "#e6b6f1"
secondary = "#d5c0d7"
tertiary = "#f5b7b3"Suggested behavior:
primary = stronger pink/purple influence
secondary = softer, safer influence
tertiary = warmer pink/salmon influence
When in doubt, start with:
accent = "primary"and small values:
bg = 0.05If it becomes too pink:
accent = "secondary"If it still looks dead:
bg = 0.07If it looks like a nightclub for haunted TypeScript:
bg = 0.03This plugin exists mostly as a small bridge between tools that already do the hard work.
Special thanks to folke for TokyoNight and Snacks.nvim.
TokyoNight provides the actual colorscheme foundation here — this plugin only nudges a few colors so the theme feels more at home inside a wallpaper-driven desktop. Snacks.nvim also makes the optional toggle integration pleasantly easy, which is suspiciously civilized for Neovim.
Thanks also to the people behind
matugen, which does the real color extraction
magic. matugen-mix.nvim does not try to replace it, compete with it, or pretend
to understand color theory better than it does. It simply reads the palette
matugen generates and lets that palette gently influence Neovim.
In short:
matugen finds the mood
TokyoNight provides the room
Snacks gives us a switch
this plugin just opens the window a little
Check that the generated palette can be loaded:
:lua print(vim.inspect(require("generated.matugen-colors")))Check the current primary color:
:lua print(require("matugen-mix").get_color("primary"))Check a manual mix:
:lua print(require("matugen-mix").mix("#222436", "primary", 0.05))Toggle on/off:
:MatugenMixToggleCheck whether it is enabled:
:lua print(require("matugen-mix").is_enabled())- Change wallpaper.
- Run
matugen. - Start or reload Neovim.
- Toggle Matugen Mix on/off.
- Adjust mix amount if needed.
- Stop before inventing a full theme engine.
The last step is important and will probably be ignored.
The plugin should stay boring.
Good boring.
The kind of boring where it does one thing, does not surprise you, and does not turn into a config hydra.
Core idea:
read generated colors
mix carefully
allow toggle
get out of the way
Personal local plugin.
Working target:
Arch Linux + Hyprland + matugen + TokyoNight Moon + Snacks.nvim
Used mainly to make Neovim visually harmonize with a wallpaper-driven desktop without abandoning the original colorscheme.
The matugen virus spreads, but politely.