Color that means something. Everything else fades away.

Most colorschemes are hex tables with opinions. Aalto is a perceptual color engine that happens to produce a colorscheme.
The difference in practice: when you change a color in Aalto, you change a hue identity. Contrast, saturation, and the visual hierarchy between roles are recomputed automatically in OKLCH space — a perceptually uniform color model where equal numeric steps look equal to the eye. You get a coherent result without manually tweaking six related values.
Aalto maps all of code down to four semantic roles and colors those. Everything else — keywords, operators, punctuation, variables — renders in neutral foreground.
| Role | Meaning | Examples |
|---|---|---|
definition |
Structure | functions, types, classes, modules |
constant |
Values | numbers, booleans, enum members |
string |
Data | string literals |
comment |
Context | comments, documentation |
The hierarchy definition > constant > string > comment is enforced
perceptually, not just numerically — each role is placed at a deliberate
distance from the background in OKLCH lightness, so the prominence ordering
holds across both dark and light variants regardless of hue.
The result is an editor that quietly shows you the shape of your code. The best colorscheme is the one you stop noticing.
Read the full reasoning in docs/philosophy.md.
:checkhealth aalto reports contrast, gamut, and
light/dark balancelazy.nvim
{
"micdzu/aalto.nvim",
priority = 1000,
config = function()
require("aalto").setup({})
vim.cmd("colorscheme aalto")
end,
}
packer.nvim
use {
"micdzu/aalto.nvim",
config = function()
require("aalto").setup({})
vim.cmd("colorscheme aalto")
end,
}
vim-plug
Plug 'micdzu/aalto.nvim'
require("aalto").setup({})
vim.cmd("colorscheme aalto")
-- Dark variant, all defaults
require("aalto").setup({})
vim.cmd("colorscheme aalto")
-- Light variant
require("aalto").setup({ variant = "light" })
vim.cmd("colorscheme aalto")
require("aalto").setup({
-- "dark" or "light"
variant = "dark",
-- Override raw palette hues. Contrast and hierarchy are re-applied on top.
palette = {
definition = "#7C8CFA",
string = "#8FC77C",
constant = "#B87EDC",
comment = "#746FA3",
},
-- Override semantic role colors directly, bypassing palette mapping.
semantic = {
definition = "#82AAFF",
},
-- Bold/italic for comments and keywords only.
styles = {
comments = { italic = true },
keywords = {}, -- e.g. { bold = true }
},
-- Transparent backgrounds.
transparent = false, -- main windows
float_transparent = false, -- floating windows
-- Set terminal_color_0 … terminal_color_15.
terminal_colors = true,
-- Applied last, wins over everything.
overrides = {
-- ["@keyword"] = { italic = true },
},
-- Enable built-in statusline.
statusline = false,
-- Print resolved palette after setup.
debug = false,
})
Full customization reference: docs/customization.md
| Command | Description |
|---|---|
:AaltoVariant [dark|light] |
Switch variant, or toggle if no argument |
:AaltoStatus |
Show current configuration |
:AaltoReload |
Re-apply highlights with the last used config |
:AaltoPreview dark|light |
Preview a variant temporarily without saving |
require("lualine").setup({
options = {
theme = require("aalto").lualine_theme(),
},
})
Pass { lualine_style = "full" } for a filled mode indicator instead of the
default minimal one.
require("aalto").register_plugin_specs({
{
definition = { "MyPluginTitle", "MyPluginHeader" },
fg_dark = { "MyPluginBorder", "MyPluginSeparator" },
error = { "MyPluginErrorSign" },
string = { "MyPluginAddedLine" },
},
})
Available roles: definition, constant, string, comment, fg, fg_dark,
error, warn, info, hint, bg, bg_light, selection,
inv_definition, inv_constant, inv_string.
Full reference: docs/plugins.md
:checkhealth aalto
Reports contrast ratios, gamut warnings, and a light/dark comparison table.
base → variants → semantic → link() → groups → highlights
Full internals: docs/design.md
MIT