colorful-winsep.nvim: colouring the border of the active window
Make your nvim window separators colorful
At a glance
- What is it?
- colorful-winsep.nvim is a small Neovim plugin that makes the active window distinguishable through its separator, the way tmux colours the border of the pane in focus. It offers four border styles, two animation modes, an indicator for the two window case, a multi-colour marquee effect, and a low level hook that lets the plugin's own border characters be replaced.
- Who is it for?
- colorful-winsep.nvim fits a Neovim user on version 0.11.3 or later who works in split windows and wants the focused one to be obvious at a glance, and who already uses a patched font. It does not fit a user on an older Neovim, and it does not fit a terminal without the Nerd Font glyphs the border characters require.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 180 days ago.
- What is it written in?
- Mainly Lua, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem with a split window
The motivation section states the problem in one sentence: in Neovim you cannot make the active window distinguishable via the window separator. The plugin's answer is to colour the border of the active window, the way tmux does for its different panes.
That comparison is the whole pitch. A tmux user already knows what it feels like to have the pane in focus lit up around the edge, and anyone moving from tmux to a Neovim split workflow loses that cue. The plugin puts it back, in the editor rather than in the terminal multiplexer.
The scope is deliberately narrow. It is not a statusline plugin, and it does not add a status line or a window list. It changes the characters that draw the window separator and the highlight groups that colour them, and that is all it does.
The requirements are short. Neovim 0.11.3 or newer, which is a recent floor and tells you the plugin is written against the current window and extmark APIs rather than the older ones. And a Nerd Font, because the border characters it draws come from that patched font set rather than from plain ASCII.
The project is MIT licensed, the last push was on 2026-04-26, and the repository has no tagged releases. The layout is a Lua directory with the plugin code, a documentation directory, a stylua configuration for formatting, and the usual project files.
Installing with three plugin managers
The installation section covers the three plugin managers that a Neovim configuration is likely to use, and the difference between them is more interesting than it looks.
With vim-plug it is a single line.
Plug 'nvim-zh/colorful-winsep.nvim'With Packer.nvim the plugin is declared with a configuration function.
use {
"nvim-zh/colorful-winsep.nvim",
config = function ()
require('colorful-winsep').setup()
end
}And with lazy.nvim the declaration includes an event, which is the interesting part.
{
"nvim-zh/colorful-winsep.nvim",
config = true,
event = { "WinLeave" },
}The event is the whole reason to read the three examples side by side. The plugin colours the border of the window that has focus, and the focus changes when you leave a window, so the plugin needs to run when that happens rather than only at startup. Loading it on the WinLeave event means the first window switch triggers the load, and the alternative is that the plugin is loaded eagerly and always pays its cost.
That is a small plugin making a small but correct architectural choice, and it is the kind of detail that decides whether a plugin is comfortable to have in a configuration that loads a hundred of them.
The other observation from these three examples is that the setup function is the configuration entry point in every case, which is the convention the plugin follows throughout.
The configuration, and what the defaults do
The README prints the default configuration in full, with the instruction to read the comments carefully if you intend to change it, and the defaults are worth reading as a design statement.
require("colorful-winsep").setup({
-- choose between "single", "rounded", "bold" and "double".
border = "bold",
excluded_ft = { "packer", "TelescopePrompt", "mason" },
highlight = nil, -- nil|string|function. See the docs's Highlights sectionThe border option takes four values, single, rounded, bold, and double, and the default is bold. That is a choice about how the separator is drawn, and because the characters come from a patched font there are four distinct glyph families available.
The excluded filetypes list is the more interesting default, because it names three specific interfaces. A plugin manager prompt, a fuzzy finder prompt, and a tool that opens its own window are all cases where the plugin should get out of the way, since those interfaces take over the screen and their own borders would be overwritten. Any full screen plugin a user adds would want to be in that list, which is worth knowing because the list is where the extension point is.
The highlight option defaults to nil, and the highlights section explains what that means in practice. A nil value means the plugin does nothing if the highlight group has already been set before the plugin loads. Otherwise the highlight is set to a specific purple, and the reason given is that this is useful for anyone who controls highlights through a colour scheme plugin, because the plugin's colour then fits the rest of the scheme.
The config ends with an empty colours array, with a comment explaining that a single colour applies statically while multiple colours create a marquee effect.
Two ways to animate the border
The animation section is where the plugin does something that most separator plugins do not.
By default the animation mode is called shift, and the option accepts false to turn it off or one of the two mode names, with the configuration block then holding the settings for the chosen one. The shift mode has three settings: a delay of 16 milliseconds, which the comment describes as about 60 frames per second, a frame count of 15, which is how many frames are needed to complete the animation, and an easing algorithm, with the available list given as linear, ease_out_cubic, ease_in_out_sine, ease_out_quad, and ease_out_expo.
The second mode is called progressive, and it has the same frame delay plus two interpolation factors, one vertical and one horizontal, each documented as being between 0 and 1. The factor is the proportion of the remaining distance covered on each frame, which is why a smaller number produces a slower settle rather than a different shape.
The two modes are shown with animated recordings in the README, one at the top for shift and one in the progressive section, and the difference in approach is visible: the shift mode moves the coloured band around the border, while the progressive mode interpolates the colour itself.
The easing list is a detail worth noting, because it tells you the animation is not a fixed curve hard coded in the plugin but a selectable algorithm, and the five named are the common ones from the easing literature. The default of ease_out_cubic means the movement decelerates as it settles, which is the behaviour that reads as intentional rather than mechanical.
The frame count and the delay together determine the duration of the animation, so at the default settings the whole thing takes about a quarter of a second, which is fast enough to read as a pulse and slow enough to notice.
The two window case, and the Winsep command
There is a problem that only exists when there are exactly two windows, and the plugin addresses it with its own option.
The reasoning is given directly: when using the plugin with two windows only, it becomes difficult to discern which window is currently active. With more than two windows the coloured border is enough, because the position of the coloured window tells you something. With two, the border says which one is focused but not where it is on the screen.
The fix is an indicator, and the option takes a position, which can be false to disable it or one of center, start, end, and both. The symbols are configured in a table with four entries for each of the four edges, so the indicator can show a different glyph depending on whether the separator is on the left, the right, the top, or the bottom of the focused window.
The command interface is a single user command, Winsep, with three subcommands, and the README puts the command, the Lua call, and the effect in a table.
The enable subcommand calls the enable function, the disable subcommand calls disable, and the toggle subcommand calls toggle. The functions are all reachable from Lua, which matters for the configuration style where the plugin is loaded lazily and a user may want to drive it from a keybinding.
The highlight name is ColorfulWinSep, and the README notes it can be changed with Neovim's built in function or through the plugin's configuration. That dual route is the one to know about, because it means the colour can be set by a colour scheme that runs after the plugin, or by the plugin's own setup, and the two can disagree.
The marquee effect and the frame render hook
The last two sections of the README are marked as local additions, and they are the most interesting part of the plugin.
The marquee effect comes from passing an array of hex colours to the colours option in the setup function, or by calling the set colours function at run time. A single colour applies statically, and multiple colours create the marquee effect.
require("colorful-winsep").setup({
colors = { "#a6d189", "#e5c890", "#e78284", "#ca9ee6", "#8caaee" }
})The example list is a pastiche palette, five colours that read as a spectrum, and the effect is a band of colour travelling around the border. The mechanism follows from the earlier description of the border as a loop, since a loop is what makes a travelling band possible.
The advanced hook is the frame render callback, and it comes with the model the plugin uses internally. The README says the border is represented as a circular linked list, going left to top to right to bottom, and that each node, which is a character point, can be intercepted before it is rendered to apply custom styling or custom characters.
The node parameter carries five things: the index, which is the global zero based index tracing the entire active border loop; the type, which is an enumeration of eight positions, the vertical left, the top left corner, the horizontal top, the top right corner, the vertical right, the bottom right corner, the horizontal bottom, and the bottom left corner; the character intended for that spot; the window direction the node belongs to, one of left, up, right, or down; and the buffer index, which is the physical position of the extmark on the underlying local buffer.
That last field is the most technical detail in the plugin, and it tells you how the border is actually drawn. The border is not a set of buffer characters that get rewritten, it is a set of extmarks positioned on a local buffer, and the plugin reads and writes the character at each position to produce the coloured effect. That is a much better implementation than rewriting window separator text, because it leaves the underlying buffer untouched.
The example interceptor uses the type field to detect a corner and replaces both the character and the colour for those nodes, while leaving the rest alone, and the README pairs it with a joke about a playable snake game on the window border, which is the sort of demonstration that tells you the hook is genuinely powerful.
Editorial conclusion
colorful-winsep.nvim fits a Neovim user on version 0.11.3 or later who works in split windows and wants the focused one to be obvious at a glance, and who already uses a patched font. It does not fit a user on an older Neovim, and it does not fit a terminal without the Nerd Font glyphs the border characters require. Verify first that the border style you pick renders in the terminal you use, that the filetypes you exclude are the ones that open their own full screen interfaces, and that the animation is worth the redraws it causes on a large window, since the default is an animated shift rather than a static colour.
Frequently asked questions
What does colorful-winsep.nvim do?
It colours the border of the active Neovim window so that the focused split is distinguishable from the others, in the way tmux colours the pane in focus. The border style, the highlight, the animation, and the colours are all configurable.
What does colorful-winsep.nvim require?
Neovim 0.11.3 or newer, and a Nerd Font, because the separator characters it draws come from a patched font rather than from plain ASCII. The plugin is written in Lua and is MIT licensed.
How do I install colorful-winsep.nvim with lazy.nvim?
Add the plugin with its configuration set to true and the WinLeave event, so that it loads when you first leave a window rather than at startup. The README also shows the vim-plug and Packer.nvim equivalents, and in the Packer case the configuration calls the setup function directly.
What animation modes does colorful-winsep.nvim support?
Two, named shift and progressive, plus false to disable the animation. The shift mode has a delay of 16 milliseconds by default, a frame count of 15, and a selectable easing algorithm from a list that includes linear and ease_out_cubic. The progressive mode adds vertical and horizontal interpolation factors.
How does the colorful-winsep.nvim marquee effect work?
By passing an array of hex colours to the colours option in the setup function, or by calling the set colours function at run time. A single colour is applied statically, while several colours create a travelling marquee, which works because the border is modelled internally as a circular linked list of character points.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nvim-zh-colorful-winsep-nvim)Was this useful?