Dim, Fade, Tint, and Customize (Neo)vim
Vimade helps you maintain focus on what matters. It dims, fades, and tints inactive windows and buffers. It can also provide "limelight" or "twilight" style highlighting around your cursor, but with a key difference: Vimade preserves syntax highlighting, allowing you to stay in context while focusing on the code that matters most.
Vimade offers a powerful and flexible way to customize your coding environment with:
Create a truly unique and focused coding environment with Vimade.
[!IMPORTANT] Neovim 0.8+ uses a pure Lua implementation. Some features, like focus mode, require Neovim 0.10+. For Vim and older versions of Neovim, Python is required.
::lua::lazy.nvim::
{
"tadaa/vimade",
opts = {
recipe = {"default", {animate = true}},
fadelevel = 0.4,
}
}
::vimscript::vim-plug::
Plug 'TaDaa/vimade'
require('vimade').setup({
recipe = {'default', {animate = true}},
fadelevel = 0.4,
})
Recipes are not available via vimscript configuration (use either python or lua)
let g:vimade = {}
let g:vimade.fadelevel = 0.4
For Vim & Neovim < 0.8
::python::
function! SetupMyVimadeConfig()
python << EOF
from vimade import vimade
vimade.setup(
recipe = ['default', {'animate':True}],
fadelevel = 0.4,
)
EOF
endfunction
# SetupMyVimadeConfig will be called lazily after python becomes available.
# You can call vimade.setup(...) whenever you want.
au! User Vimade#PythonReady call SetupMyVimadeConfig()
Vimade treats the setup() command as an overlay. Each time you call it, it will override any previous settings. Therefore, if you want to combine multiple settings, you must include them all in the same setup() call. If you want to reset the overlay to defaults, just call setup({}) with no options.
Correct:
require('vimade').setup({
recipe = {'minimalist', {animate = true}},
fadelevel = 0.3,
})
Incorrect:
require('vimade').setup({recipe = {'minimalist', {animate = true}}})
require('vimade').setup({fadelevel = 0.3}) -- This will override the recipe setting!
Vimade can fade inactive windows in a few different ways. You can control this behavior with the ncmode option.
'buffers': (Default) Fades all windows that do not share the same buffer as the active window. This is useful if you have the same buffer open in multiple splits and you want them all to remain highlighted.'windows': Fades all inactive windows. This is a good choice if you want a clear distinction between the window you are currently working in and all other windows.'focus': (Neovim 0.10+) Only fades when the :VimadeFocus command is active. This is useful for on-demand highlighting.Most users should try each option to see what they like best.
::lua::
require('vimade').setup{ncmode = 'buffers'} -- or 'windows' or 'focus'
::vimscript::
let g:vimade.ncmode = 'buffers' " or 'windows' or 'focus'
::python::
from vimade import vimade
vimade.setup(ncmode='buffers') # or 'windows' or 'focus'
When using a transparent terminal, your Normal highlight group has a background of NONE. Vimade needs to know the actual background color to properly calculate faded colors. For the best results, you should set the basebg option.
#FFFFFF).basebg to this value in your configuration. You may need to darken it slightly to get the best results.::lua::
require('vimade').setup{basebg = '#2d2d2d'} -- or {45, 45, 45}
::vimscript::
let g:vimade.basebg = '#2d2d2d' " or [45, 45, 45]
::python::
from vimade import vimade
vimade.setup(basebg='#2d2d2d') # or [45, 45, 45]
If you are having issues with Vimade in tmux, here are a few things to try:
t_Co to 8. It is recommended that you set export TERM=xterm-256color before starting Vim. You can also set set termguicolors inside Vim if your terminal supports it for an even more accurate level of fading.let g:vimade.enablefocusfading = 1set -g focus-events on to your tmux.conf.tmux-plugins/vim-tmux-focus-events plugin.You can configure Vimade to only fade windows when a floating window is open. This is useful if you only want to dim the background when you are focused on a floating window, like a popup or a dialog.
This can be achieved with a blocklist function that checks if the current window is a floating window.
Lua:
require('vimade').setup({
blocklist = {
demo_tutorial = function (win, current)
-- current can be nil
if (win.win_config.relative == '') and (current and current.win_config.relative ~= '') then
return false
end
return true
end
}
})
Python:
Similar behaviors can be managed for python as well. Vim doesn't support Focusable floating windows, so the logic required is a bit more complex, but still achievable
from vimade import vimade
from vimade.state import globals
g_tick_id = -1
g_popup_visible = False
g_popup_winids = []
def refresh_popup_visible():
global g_popup_visible
global g_popup_winids
g_popup_winids = vim.eval('popup_list()')
any_visible = False
for winid in g_popup_winids:
if vim.eval(f'popup_getpos({winid})["visible"]') == '1':
any_visible = True
break
if any_visible != g_popup_visible:
g_popup_visible = any_visible
if g_popup_visible:
# A popup is visible, so fade the background including active win
vim.command('VimadeFadeActive')
else:
# No popups are visible, so clear the fade including active win
vim.command('VimadeUnfadeActive')
def only_behind_float_windows (win, current):
global g_tick_id
# This is a performance optimization. We only run the expensive
# refresh_popup_state() function once per "tick" (screen refresh),
# not for every single window being checked.
if g_tick_id != globals.tick_id:
g_tick_id = globals.tick_id
refresh_popup_visible()
return not g_popup_visible or win.winid in g_popup_winids
vimade.setup(blocklist = {
'demo_tutorial': only_behind_float_windows,
})
Now, Vimade will only fade windows when a floating window is active.

Vimade comes with several pre-built recipes to get you started. You can enable them with the recipe option.
The standard Vimade experience.
require("vimade").setup({recipe = {"default", {animate = true}}})
Hides low-value inactive highlights like line numbers and the end-of-buffer marker.
require("vimade").setup({recipe = {"minimalist", {animate = true}}})
A balanced approach between window and buffer fading.
require("vimade").setup({recipe = {"duo", {animate = true}}})
Inverts the colors of the active window for a high-contrast look. (Neovim only)
require("vimade").setup({recipe = {"paradox", {animate = true}}})
Gradually fades windows based on their distance from the active window.
require("vimade").setup({recipe = {"ripple", {animate = true}}})
| Option | Description |
|---|---|
recipetableDefault: {'default', {}} |
Specifies a recipe to use for styling. A recipe is a pre-configured set of styles and options. Any other configuration will overlay the recipe config. See the Recipes section for available recipes.Example (lua):require('vimade').setup({recipe = {'minimalist', {animate = true}}})Example (python):vimade.setup(recipe = ['minimalist', {'animate': True}]) |
ncmodestringDefault: 'buffers' |
- 'buffers': Fades all windows that do not share the same buffer as the active window. This is useful if you have the same buffer open in multiple splits and you want them all to remain highlighted.- 'windows': Fades all inactive windows. This is a good choice if you want a clear distinction between the window you are currently working in and all other windows.- 'focus' (Neovim 0.10+): Only fades when the :VimadeFocus command is active. This is useful for on-demand highlighting. |
fadelevelfloat or functionDefault: 0.4 |
The amount of fade to apply to inactive windows. A value between 0.0 (completely faded) and 1.0 (not faded at all). |
tinttable or functionDefault: {} |
Apply a color tint to inactive windows. The tint option is a table that can contain fg, bg, and sp keys. Each of these keys is a table that can contain rgb and intensity keys. The rgb key is a table of 3 values (red, green, blue) from 0-255. The intensity is a value from 0.0-1.0 that determines how much of the color to apply.Example (lua):require('vimade').setup({ tint = { fg = { rgb = {255, 0, 0}, intensity = 0.5}, bg = { rgb = {0, 0, 0}, intensity = 0.2}}})Example (vimscript):let g:vimade.tint = { \ 'fg': {'rgb': [255, 0, 0], 'intensity': 0.5}, \ 'bg': {'rgb': [0, 0, 0], 'intensity': 0.2}}Example (python):vimade.setup( tint = { 'fg':{'rgb':[255,0,0], 'intensity':0.5}, 'bg':{'rgb':[0,0,0], 'intensity':0.2}}) |
basebgstring or table (list in python)Default: nil |
The base background color for transparent terminals. When using a transparent terminal, your Normal highlight group has a background of NONE. Vimade needs to know the actual background color to properly calculate faded colors. You can provide a hex string (e.g., '#2d2d2d') or a table/list of RGB values (e.g., {20, 20, 20}).For best results, follow these steps:1. Place your transparent terminal over a pure white background.2. Take a screenshot of your terminal.3. Use a color picker to get the hex code of the background color.4. Set basebg to that value.5. Darken basebg until you find the highlights acceptable. |
blocklisttable or functionDefault: See below |
A list of windows or buffers to exclude from fading. This can be a table of rules or a function that returns true to block a window. Each rule is a table of conditions that must be met for the window to be blocked. The available conditions are:- buf_name: Matches against the buffer name.- win_type: Matches against the window type.- buf_opts: Matches against the buffer options.- buf_vars: Matches against the buffer variables.- win_opts: Matches against the window options.- win_vars: Matches against the window variables.- win_config: Matches against the window configuration.- highlights: (Neovim only) A list of highlight groups to exclude from fading. This can be a list of strings or a function that returns a list of strings. You can also use a pattern by surrounding the string with /.Default (lua):blocklist = { default = { highlights = { laststatus_3 = function(win, active) if vim.go.laststatus == 3 then return 'StatusLine' end end, 'TabLineSel', 'Pmenu', 'PmenuSel', 'PmenuKind', 'PmenuKindSel', 'PmenuExtra', 'PmenuExtraSel', 'PmenuSbar', 'PmenuThumb', }, buf_opts = {buftype = {'prompt'}}, }, default_block_floats = function (win, active) return win.win_config.relative ~= '' and (win ~= active or win.buf_opts.buftype =='terminal') and true or false end, }Default (python):'blocklist': { 'default': { 'buf_opts': { 'buftype': ['popup', 'prompt'] }, 'win_config': { 'relative': True #block all floating windows }, }},Example (lua):require('vimade').setup({ blocklist = { my_rule = { buf_opts = { ft = 'oil' } } } })Example (vimscript):let g:vimade.blocklist = { 'my_rule': { 'buf_opts': { 'ft' : 'nerdtree'} } }Example (python):vimade.setup(blocklist={'my_rule': {'buf_opts': {'ft': 'nerdtree'}}}) |
linktable or functionDefault: {} |
A list of windows to style together. When windows are linked, they are styled together, meaning they will both be considered "active" or "inactive" based on the rules. This can be a table of rules or a function that returns true to link a window. Each rule is a table of conditions that must be met for the window to be linked. The available conditions are:- buf_name: Matches against the buffer name.- win_type: Matches against the window type.- buf_opts: Matches against the buffer options.- buf_vars: Matches against the buffer variables.- win_opts: Matches against the window options.- win_vars: Matches against the window variables.- win_config: Matches against the window configuration.By default, Vimade links diff windows (if groupdiff is true) and scroll-bound windows (if groupscrollbind is true).Tutorial Example: Linking Windows with linked_windowLet's say you want to link two windows so they always have the same fade state. You can achieve this by setting a window-local variable, for example, linked_window, to 1 in both windows. Then, configure Vimade's link option to recognize this variable.Step 1: Set a window-local variable in both windows:let w:linked_window = 1Step 2: Configure Vimade to link windows based on this variableExample (lua):require('vimade').setup({ link = { my_linked_windows = function(win, active) return win.win_vars.linked_window == 1 and active.win_vars.linked_window == 1 end, },})Example (vimscript):let g:vimade.link = { \ 'my_linked_windows': { \ 'win_vars': {'linked_window': 1}, \ }, \ }Example (python):from vimade import vimadevimade.setup(link={ 'my_linked_windows': { 'win_vars': {'linked_window': 1} }})Now, when you navigate between these two windows, they will be styled together. This is an extremely useful concept, and Vimade uses it behind the scenes to ensure that diffs are visible in unison. |
groupdiffbooleanDefault: true |
When true, all windows in diff mode are treated as a single entity. This means they will all be styled together, remaining active or inactive in unison. This is particularly useful for keeping visual consistency when reviewing changes across multiple diff splits. |
groupscrollbindbooleanDefault: false |
When true, all windows with the 'scrollbind' option enabled are treated as a single entity. This ensures that they are styled in unison, maintaining a consistent appearance as you scroll through them together. |
enablefocusfadingbooleanDefault: false |
When true, Vimade will fade the entire editor when it loses focus and unfade it upon gaining focus. This is useful for visually distinguishing between active and inactive editor instances when switching between different applications. Note that this feature may require additional configuration for some terminal multiplexers like tmux.Tmux Configuration:To get focus events working in tmux, you need to do the following:1. Add set -g focus-events on to your tmux.conf file.2. For Vim users, you may also need to install the tmux-plugins/vim-tmux-focus-events plugin. |
checkintervalintegerDefault: 1000 |
The interval in milliseconds for checking window states. This option is ignored if usecursorhold is enabled. |
usecursorholdbooleanDefault: false* |
When true, Vimade will use the CursorHold event to trigger updates instead of a timer. This disables the checkinterval option and can provide a more responsive feel in some scenarios. See :help updatetime for more information on configuring the CursorHold event. |
lazybooleanDefault: false |
When set to true, Vimade is disabled at startup. You will need to manually call :VimadeEnable or vimade#Load() to enable it.Neovim (lazy.nvim):For lazy loading with lazy.nvim, you can use the event key to specify when to load the plugin. For example, to load Vimade on the VeryLazy event:require('lazy').setup({spec = {{'tadaa/vimade', event = 'VeryLazy'}}})Vimscript/Python:To lazy load with vimscript, you can set g:vimade.lazy to 1 and then call vimade#Load() on an autocommand:let g:vimade = {}let g:vimade.lazy = 1au WinEnter * ++once call vimade#Load() |
rendererstringDefault: 'auto' |
Specifies the rendering engine for Vimade. In most cases, you should not need to change this setting, as Vimade will automatically select the best available renderer for your environment.- 'auto': (Default) Automatically selects the best available renderer. It will prioritize the Lua renderer on Neovim 0.8+ and fall back to the Python renderer on older Neovim versions and Vim.- 'lua': Forces the use of the Lua renderer. This is only available on Neovim 0.8+.- 'python': Forces the use of the Python renderer. This is required for Vim and older versions of Neovim. |
nohlcheckbooleanDefault: true |
(Lua only) This option controls how often Vimade checks for updates to highlight namespaces. By default, this is true, which means that Vimade will only recompute namespaces when you switch windows or buffers. While this is efficient, some plugins that dynamically change highlight groups may not be reflected immediately. In general, you should not need to change this setting. However, if you are experiencing issues with highlights not updating correctly, you can set this to false for debugging purposes. When set to false, Vimade will recompute highlight namespaces on every frame, which may have a minor performance impact. |
enablesignsbooleanDefault: True |
(Python only) Whether or not to fade signs. |
signsidintegerDefault: 13100 |
(Python only) The starting ID for placing Vimade signs. If you use other plugins that place signs, you may need to change this value to avoid conflicts. |
signsretentionperiodintegerDefault: 4000 |
(Python only) The amount of time after a window becomes inactive to check for sign updates. Many plugins update signs asynchronously, so having a long enough retention period helps ensure that newly added signs are faded. |
fademinimapbooleanDefault: true |
(Python only) Enables a special fade effect for severin-lemaignan/vim-minimap. |
matchpriorityintegerDefault: 10 |
(Python only) Controls the highlighting priority. You may want to tweak this value to make Vimade play nicely with other highlighting plugins and behaviors. For example, if you want hlsearch to show results on all buffers, you may want to lower this value to 0. |
linkwincolortableDefault: {} |
(Python only) Vim only option when wincolor is supported. List of highlights that will be linked to Normal. |
disablebatchbooleanDefault: false |
(Python only) Disables IPC batching. Users should not need to toggle this feature unless they are debugging issues with Vimade, as it will greatly reduce performance. |
enablebasegroupsbooleanDefault: true |
(Python only) Only old Neovim. Allows winlocal winhl for the basegroups listed below. |
basegroupstableDefault: every built-in highlight |
(Python only) Only old Neovim. Fades the listed highlights in addition to the buffer text. |
enabletreesitterbooleanDefault: false |
(Python only) Only old Neovim. Uses treesitter to directly query highlight groups instead of relying on synID. |
| command | description |
|---|---|
VimadeEnable |
Enables Vimade. Not necessary to run unless you have explicitly disabled Vimade. |
VimadeDisable |
Disable and remove all Vimade highlights. |
VimadeToggle |
Toggle between enabled/disabled states. |
VimadeRedraw |
Force vimade to recalculate and redraw every highlight. |
VimadeInfo |
Provides debug information for Vimade. Please include this info in bug reports. |
VimadeFocus |
Neovim-only. Highlights around the cursor using your configured providers. When used with no subcommand, it toggles the focus highlight. This command has the following subcommands:- toggle: Toggles the focus highlight. (Default)- toggle-on: Enables the focus highlight.- toggle-off: Disables the focus highlight. |
VimadeMark |
Neovim-only. Mark an area that should not be highlighted in inactive windows. When used with no subcommand, it toggles the mark for the current selection. This command has the following subcommands:- set: Marks the current selection.- remove: Removes marks within the current selection.- remove-win: Removes all marks in the current window.- remove-buf: Removes all marks in the current buffer.- remove-tab: Removes all marks in the current tab.- remove-all: Removes all marks. |
VimadeWinDisable |
Disables fading for the current window. |
VimadeWinEnable |
Enables fading for the current window. |
VimadeBufDisable |
Disables fading for the current buffer. |
VimadeBufEnable |
Enables fading for the current buffer. |
VimadeFadeActive |
Fades the current active window. |
VimadeUnfadeActive |
Unfades the current active window. |
VimadeFadeLevel [0.0-1.0] |
Sets the FadeLevel config and forces an immediate redraw. |
VimadeFadePriority [0+] |
Sets the FadePriority config and forces an immediate redraw. |
VimadeOverrideFolded |
(Recommended for Vim users) Overrides the Folded highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include Folded highlights that are distracting in faded windows. |
VimadeOverrideSignColumn |
(Recommended for Vim users) Overrides the SignColumn highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include SignColumn highlights that are distracting in faded windows. |
VimadeOverrideLineNr |
(Recommended for Vim users) Overrides the LineNr highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include LineNr highlights that are distracting in faded windows. |
VimadeOverrideSplits |
(Recommended for Vim users) Overrides the VertSplit highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include VertSplit highlights that are distracting in faded windows. |
VimadeOverrideNonText |
(Recommended for Vim users) Overrides the NonText highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include NonText highlights that are distracting in faded windows. |
VimadeOverrideEndOfBuffer |
(Recommended for Vim users) Overrides the EndOfBuffer highlight by creating a link to the Vimade base fade. This should produce acceptable results for colorschemes that include EndOfBuffer highlights that are distracting in faded windows. |
VimadeOverrideAll |
(Recommended for Vim users) Combines all VimadeOverride commands. |
Recipes are the heart of Vimade's customization. A recipe is simply a collection of styles that are applied to inactive windows. You can mix and match styles to create your own unique look and feel. All of Vimade's built-in recipes are just a collection of the styles listed below, with varying degrees of complexity.
To create a recipe, you define a list of styles in your configuration. Each style is a Lua table with a specific set of options.
Here is an example of a simple recipe:
Lua:
require('vimade').setup({
style = {
-- Fade inactive windows by 60%
require('vimade.style.fade').Fade({value = 0.4}),
-- Tint the foreground of inactive windows with a blue color
require('vimade.style.tint').Tint({
value = {
fg = {rgb = {100, 100, 255}, intensity = 0.2},
}
}),
}
})
Python:
from vimade import vimade
from vimade.style.fade import Fade
from vimade.style.tint import Tint
vimade.setup(
style = [
# Fade inactive windows by 60%
Fade(value = 0.4),
# Tint the foreground of inactive windows with a blue color
Tint(
value = {
'fg': {'rgb': [100, 100, 255], 'intensity': 0.2},
}
),
]
)
If you create a style combination that you find particularly useful, you can abstract it into your own recipe. Recipes are located in the lua/vimade/recipe/ directory. If you believe your recipe would be beneficial to other users, please feel free to open a pull request to add it to Vimade!
Here is a list of the available styles and their options:
Fades the highlights of inactive windows.
| Option | Description |
|---|---|
valuenumber or functionDefault: nil |
The target fade level. A value between 0.0 (completely faded) and 1.0 (not faded). |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the style should be applied. |
Tints the highlights of inactive windows.
| Option | Description |
|---|---|
valuetable or functionDefault: nil |
A table with fg, bg, and sp keys, each with rgb and intensity values. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the style should be applied. |
Inverts the colors of inactive windows.
| Option | Description |
|---|---|
valuenumber or table or functionDefault: nil |
The inversion level from 0.0 to 1.0. Can be a single number or a table with fg, bg, and sp keys. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the style should be applied. |
Applies nested styles to a specific list of highlight groups.
| Option | Description |
|---|---|
valuetableDefault: nil |
A list of highlight group names. |
styletableDefault: nil |
A list of styles to apply to the included highlight groups. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the style should be applied. |
Applies nested styles to all highlight groups except for a specific list.
| Option | Description |
|---|---|
valuetableDefault: nil |
A list of highlight group names to exclude. |
styletableDefault: nil |
A list of styles to apply to the not-excluded highlight groups. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the style should be applied. |
A container style that groups other styles under a name and a condition. This is useful for creating logical groups of styles that can be enabled or disabled together.
| Option | Description |
|---|---|
namestringDefault: nil |
The name of the component. |
styletableDefault: nil |
A list of styles to be included in the component. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the component's styles should be applied. |
Links one highlight group to another.
| Option | Description |
|---|---|
valuetableDefault: nil |
A list of tables, each with a from and to key specifying the highlight group to link from and to. |
conditionfunctionDefault: CONDITION.INACTIVE |
A function that determines if the linking should be applied. |
Vimade is the original syntax-aware dimming and fading plugin for Vim and Neovim. While other window fading plugins have emerged, Vimade is a powerful and flexible option with a rich feature set and the only option that correctly handles plugin namespaces.
This table highlights some of the features that make only Vimade stand out:
| Feature | Vimade |
|---|---|
| Fade buffers and windows | Yes |
| Dim around cursor (Neovim) | Yes |
| Mark areas to stay visible (Neovim) | Yes |
| Group diffs and scroll-bound windows | Yes |
| Advanced block-listing and linking | Yes |
| Smooth animations | Yes |
| High performance | Lua: ~0.5ms-1.4ms per frame Python: varies, but expect ~0.5-6ms per frame |
| Composable recipes | Yes |
Support for 256 colors and termguicolors |
Yes |
| Per-window configuration | Yes |
| Cleared highlights | Yes |
| Compatibility with other namespaces | Yes |
| Support for Vim + All versions of Neovim | Yes |
If you are looking for a mature, feature-rich, and highly customizable plugin, Vimade is the right choice for you.
:VimadeFocus and :VimadeMarkContributions are always welcome! Whether you have a feature request, a bug report, or a question, please feel free to open an issue or a pull request.
If you've created a recipe that you think others would enjoy, please consider sharing it! Recipes are a great way to contribute to the Vimade community. You can find the existing recipes in the lua/vimade/recipe/ directory.
If you encounter a bug or have an idea for a new feature, please open an issue on the GitHub repository. When opening an issue, please provide as much detail as possible, including:
:VimadeInfoThanks for your interest in contributing to Vimade!