A dependency-free color highlighter and keyboard-driven color picker for Neovim. It understands modern CSS colors and resolves project-specific Tailwind CSS v3 and v4 palettes without executing project code.
color-tools-demo.mp4
- Incremental highlighting of visible colors with background, foreground, or virtual-text display.
- CSS hex, named colors, RGB, HSL, HWB, Lab, LCH, Oklab, Oklch, predefined
color()spaces, gradients,color-mix(), relative colors, and other conservatively resolvable CSS Color 4/5 expressions. - A keyboard-only floating picker for direct hex, RGB, HSL, Oklab, and Oklch literals, with RGB, HSL, HSV, Oklab, and Oklch editing modes.
- Project-aware Tailwind CSS v3/v4 highlighting and palette picking, including custom colors, presets, imports, prefixes, important modifiers, and opacity.
- Optional Tailwind LSP enrichment and clean coexistence with Neovim 0.12's built-in document-color rendering.
- Automatic support for normal buffers and reused preview buffers from fff.nvim and Snacks.nvim.
- No runtime dependencies, no project code execution, and no default global key mappings.
- Linux
- Neovim 0.12+
- A terminal with true-color support (
:set termguicolors)
{
"jake8655/color-tools.nvim",
opts = {
binds = {
pick = "<leader>cp",
insert = "<leader>ci",
toggle = "<leader>ct",
},
},
}Use version = "*" if you prefer stable releases over the latest commit.
vim.pack.add({ "https://github.com/jake8655/color-tools.nvim" })
require("color-tools").setup({
binds = {
pick = "<leader>cp",
insert = "<leader>ci",
toggle = "<leader>ct",
},
})git clone https://github.com/jake8655/color-tools.nvim \
"${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/plugins/start/color-tools.nvim"require("color-tools").setup()Put the cursor on an editable color and run :ColorToolsPick. If no editable
color is under the cursor, the picker inserts a new one. Use
:ColorToolsInsert to always insert a new color.
The picker is keyboard-only. Its default controls are:
| Key | Action |
|---|---|
j / k, arrow down / up |
Select the next / previous channel |
h / l, arrow left / right |
Decrease / increase |
[ / ] |
Fine decrease / increase |
H / L |
Coarse decrease / increase |
e |
Cycle editing space |
o |
Cycle output format |
r |
Reset |
<CR> |
Confirm |
<Esc> / q |
Cancel without changing the source buffer |
Tailwind classes use the same picker surface to select family, shade, and opacity.
The plugin highlights supported buffers by default but creates no ordinary mapping unless you request one:
require("color-tools").setup({
binds = {
pick = "<leader>cp",
insert = "<leader>ci",
toggle = "<leader>ct",
refresh = "<leader>cr",
},
lsp = {
document_color = {
coexist = false, -- avoid a duplicate Neovim document-color overlay
},
tailwind = {
enabled = true, -- enrich exact colors from an attached Tailwind LSP
},
},
highlight = {
enabled = true,
style = "background", -- "background", "foreground", or "virtual"
virtual_text = "■ ",
filetypes = {}, -- empty means every filetype
buftypes = { "" },
},
picker = {
edit_format = "rgb",
output_format = "hex",
default_color = "#ffffff",
binds = {
confirm = { "<CR>" },
cancel = { "<Esc>", "q" },
next_channel = { "j", "<Down>" },
previous_channel = { "k", "<Up>" },
increment = { "l", "<Right>" },
decrement = { "h", "<Left>" },
increment_fine = { "]" },
decrement_fine = { "[" },
increment_coarse = { "L" },
decrement_coarse = { "H" },
cycle_edit_format = { "e" },
cycle_output_format = { "o" },
reset = { "r" },
},
},
})Set a picker action to false to leave it unmapped. Every top-level action also
has a stable <Plug> mapping:
nmap <leader>cp <Plug>(color-tools-pick)
nmap <leader>ci <Plug>(color-tools-insert)
nmap <leader>ct <Plug>(color-tools-toggle)
nmap <leader>cr <Plug>(color-tools-refresh)| Command | Lua API |
|---|---|
:ColorToolsPick |
require("color-tools").pick() |
:ColorToolsInsert |
require("color-tools").insert() |
:ColorToolsToggle |
require("color-tools").toggle() |
:ColorToolsEnable |
require("color-tools").enable() |
:ColorToolsDisable |
require("color-tools").disable() |
:ColorToolsRefresh |
require("color-tools").refresh() |
ColorToolsRefresh invalidates cached Tailwind palettes and refreshes all
active color-tools buffers.
- Only direct hex, RGB, HSL, Oklab, and Oklch literals are editable. Named
colors,
color-mix(), Lab/LCH, variable-derived expressions, and other contextual forms are highlight-only. - Static analysis cannot reproduce the CSS cascade or arbitrary JavaScript and TypeScript execution. Unresolved values fail closed.
- Tailwind preview buffers work without an LSP, but only the statically resolved palette is available there.
- Linux is the currently tested and supported platform.
Bug reports and pull requests are welcome. See CONTRIBUTING.md for the development workflow.