Skip to content

Repository files navigation

vanadis

CI status crates.io version MIT OR Apache-2.0

Installation · Configuration · Theme format · Agent skill

Colour lives in every tool's own config file. Changing one shade means editing ten of them, and they drift apart; switching between light and dark means editing them all again.

vanadis renders every one of those files from a single palette, and reloads the tools that can reload. You write the templates and you choose the token names. There is no spec to conform to and no template repository to maintain.

One vanadis cycle, and herdr, Neovim, btop and the shell prompt all change together

One vanadis cycle, and the multiplexer, the editor, the process viewer and the prompt all follow. Four config files, four different ways of picking a change up, one palette.

  • Yours: your templates, your vocabulary. role.accent or colors.mauve — vanadis does not have a list of names to squeeze into.
  • Checked: vanadis check re-renders every target and names the ones that no longer match.
  • All or nothing: every target renders before anything is written. A failure at any one of them writes no files at all.
  • Adoptive: vanadis init turns a config file you already have into a template and a theme, so nothing has to be rewritten by hand.
  • Stocked: vanadis import converts base16, base24 and tinted8 schemes out of the tinted-theming collection, 538 of them, offline after one fetch.
  • Scriptable: vanadis get role.bg prints one colour and nothing else, for a tool that would rather ask than read a file.

🚀 Installation

Step 1. Install vanadis

Repository Instructions
crates.io cargo install vanadis --locked

Or from this repository, without waiting for a release:

cargo install --git https://github.com/torabit/vanadis --locked

Pre-built binaries and a Homebrew tap are not published yet.

Step 2. Get a theme

vanadis remote update           # cache the tinted-theming collection, once
vanadis search nord             # then search it offline
vanadis import nord             # convert one into ~/.config/vanadis/themes/nord.toml

Or write your own: docs/examples/papercolor-light.toml is a complete theme, and docs/theme-format.md is the format.

Step 3. Adopt a config you already have

vanadis init ~/.config/ghostty/config

init reads the file, finds the colours in it, asks which token each one is, and writes three things: a template under templates/, the colours into a theme, and a target entry into config.toml. The file it read is never modified. It becomes the target's output.

vanadis init reading a ghostty config and matching each colour to a token the palette already holds

A colour the theme already carries is offered by name, so adopting a file into a palette that is already there is mostly the return key.

Step 4. Apply

vanadis apply nord --diff       # what would change
vanadis apply nord              # write it, and run each target's reload

🎨 What it looks like

~/.config/vanadis/config.toml — one entry per file vanadis writes. docs/examples/config.toml is the whole of one, with eleven targets in it; these are three of them:

[auto]
light = "papercolor-light"
dark = "papercolor-dark"

[[targets]]
name = "hunk"
template = "templates/hunk/config.toml.in"
output = "~/.config/hunk/config.toml"

# bat matches a theme by the name inside the file, so the output is named for vanadis rather
# than for the theme it currently holds.
[[targets]]
name = "bat"
template = "templates/bat/theme.tmTheme.in"
output = "~/.config/bat/themes/vanadis.tmTheme"
reload = ["bat", "cache", "--build"]

# An editor plugin colours far more than one palette carries, so nvim stays on gruvbox while
# everything else follows the theme being applied. Light and dark still flip together.
[[targets]]
name = "nvim"
template = "templates/neovim/palette.lua.in"
output = "~/.config/nvim/lua/palette.lua"
themes = { light = "gruvbox-light", dark = "gruvbox-dark" }

A template is the tool's own config with the colours replaced. Nothing else:

[themes.vanadis]
background   = "{{role.bg}}"
panel        = "{{role.hover-bg}}"
border       = "{{role.border}}"
accent       = "{{role.accent}}"
text         = "{{role.fg}}"
muted        = "{{role.comment}}"
selectedHunk = "{{role.selection-bg}}"

vanadis apply papercolor-light writes:

[themes.vanadis]
background   = "#eeeeee"
panel        = "#e4e4e4"
border       = "#bcbcbc"
accent       = "#d70087"
text         = "#444444"
muted        = "#878787"
selectedHunk = "#d7d7af"

{{token}} substitution is the whole template language. No conditionals, no loops, no filters, no includes. A template that needs logic is a template that has moved a decision out of the theme, where it can be read.

📋 Commands

command what it does
vanadis apply <theme> renders every target, writes them, runs each reload
vanadis apply --variant dark the theme [auto] names for dark, so a shell hook needs no theme names
vanadis apply <theme> --diff the change line by line, writing nothing
vanadis cycle the theme after the one in use, from [cycle]
vanadis check do the generated files still match their templates?
vanadis list / vanadis current the themes in themes/, and the one applied last
vanadis get role.bg one resolved colour, for a prompt or a script
vanadis render <template> one template against one named theme, to stdout — how a theme is exported
vanadis render --target <name> what an apply would write for one target, to stdout
vanadis init <file> turn a config you already have into a template, a theme and a target
vanadis remote update cache the tinted-theming scheme collection
vanadis search <query> find a scheme in the cache, offline
vanadis import <scheme> convert one into a theme

init and list show each colour as a block beside its hex, on a terminal that says COLORTERM=truecolor or COLORTERM=24bit. A 256-colour approximation would show a colour the theme does not hold, so nothing is painted where the exact colour cannot be. Piped output, NO_COLOR, and TERM=dumb each turn it off; get never paints, so $(vanadis get role.bg) stays a colour and nothing else.

🤖 The agent skill

init removes the mechanical half of adoption. The half it cannot remove is judgement: where a tool keeps its config, which lines carry colour, whether a colour is role.accent or role.accent-alt, what belongs in reload, and whether a failing check means the template is wrong or the theme is.

skills/vanadis/SKILL.md carries that judgement. An agent reads it and does the work; a person reads the same file and does the work by hand. It ships as a plugin under both the Agent Plugins manifest and Claude Code's, over the one skills/ tree.

🧭 What this is not

A theme distribution system. If you want to browse hundreds of ready-made themes for tools other people already support, use tinty. That is what it is for and it does it well.

vanadis is for the case tinty does not cover: you have written your own configs, for tools that may have no template repository at all, in a palette that may not fit base16's sixteen slots.

renders templates vocabulary needs
tinty no, copies pre-built files base16 / base24 / tinted8 template repos
flavours yes base16's 16 slots —
Stylix yes base16 Nix
vanadis yes arbitrary —

The niche is the intersection: arbitrary templates and an arbitrary token vocabulary. It is a narrow one, and the three above are the better choice whenever they fit.

One thing follows from it that is worth saying out loud. Template repository ecosystems are structurally behind: every new tool — ghostty, zellij, yazi, atuin, helix — has no template repository for months or years after it ships. With vanadis you write ten lines of template and it works the day the tool does.

📚 Documentation

Every decision in vanadis is written down with the alternatives it rejected.

📝 Licence

MIT OR Apache-2.0.

About

Manage colorschemes for all your CLI tools from one palette. Renders configs and reloads apps.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages