Four variants, one system
Night, dusk, day and high contrast share the same roles and the same checks. Pick one, the whole page follows.
Four variants · open palette tokens · VS Code and terminals
Kvile is a calm, perceptually balanced theme for your editor and terminal. Designed in OKLCH, checked for contrast and colorblind safety, generated from a single set of tokens.
import { createServer } from "node:http"; type Session = { name: string; hours: number }; // Calm by default, bright only where it matters. const MAX_HOURS = 8; const pattern = /^kvile-(\w+)$/i; export function schedule(s: Session): string { if (s.hours > MAX_HOURS) { throw new Error("Take a break: " + s.hours); } return s.name.replace(pattern, "$1"); }
~/kvile on main ❯ node generate.mjs ✓ Kvile Natt contrast ok ✓ Kvile Dag contrast ok ! 1 hue nudged for deuteranopia ❯
Why Kvile
Most themes are picked by eye and checked never. Kvile is generated, measured, and then tuned by eye.
Night, dusk, day and high contrast share the same roles and the same checks. Pick one, the whole page follows.
Text on background in the current variant. Comments stay readable at 4.6:1 instead of fading away.
Every pair of syntax colors stays separable under deuteranopia, protanopia and tritanopia. The build fails if one slips.
Accents are defined by perceptual lightness and chroma instead of hex guesses, so yellow does not shout over purple. Each hue is nudged only as far as it takes to stay distinct.
Edit the tokens once, regenerate everything.
Technical specimen sheets drawn from the real palette data, for desktop and phone.
See wallpapersUse the palette in your own apps as JSON or CSS variables.
tokens.json & kvile.cssPalette
Click any swatch to copy its hex value. The page you are reading uses exactly these colors.
Accessibility
Contrast is measured with WCAG ratios. Colorblind separation uses the Machado simulation and OKLab distance, where about 2 is just noticeable and Kvile enforces 6 or more.
The same eight accents as seen by different eyes. Each row has to stay distinguishable.
Generated from the build for the selected variant.
| Pair | Ratio | Target | Result |
|---|---|---|---|
| text on background | 12.8:1 | 7-13:1 | ✓ pass |
| comment on background | 4.65:1 | >= 4.5:1 | ✓ pass |
| text on selection | 9.78:1 | >= 4.5:1 | ✓ pass |
| keyword on background | 9.55:1 | >= 4.5:1 | ✓ pass |
| string on background | 8.95:1 | >= 4.5:1 | ✓ pass |
| function on background | 7.01:1 | >= 4.5:1 | ✓ pass |
| type on background | 10.98:1 | >= 4.5:1 | ✓ pass |
| number on background | 8.03:1 | >= 4.5:1 | ✓ pass |
| constant on background | 10.57:1 | >= 4.5:1 | ✓ pass |
| special on background | 6.7:1 | >= 4.5:1 | ✓ pass |
| error on background | 6.6:1 | >= 4.5:1 | ✓ pass |
| Pair | Ratio | Target | Result |
|---|---|---|---|
| text on background | 9.13:1 | 5.5-10:1 | ✓ pass |
| comment on background | 3.7:1 | >= 3.5:1 | ✓ pass |
| text on selection | 6.49:1 | >= 4.5:1 | ✓ pass |
| keyword on background | 5.46:1 | >= 4:1 | ✓ pass |
| string on background | 7.14:1 | >= 4:1 | ✓ pass |
| function on background | 5.62:1 | >= 4:1 | ✓ pass |
| type on background | 8.63:1 | >= 4:1 | ✓ pass |
| number on background | 5.51:1 | >= 4:1 | ✓ pass |
| constant on background | 8.21:1 | >= 4:1 | ✓ pass |
| special on background | 7.92:1 | >= 4:1 | ✓ pass |
| error on background | 6.67:1 | >= 4:1 | ✓ pass |
| Pair | Ratio | Target | Result |
|---|---|---|---|
| text on background | 12.63:1 | 7-14:1 | ✓ pass |
| comment on background | 4.7:1 | >= 4.5:1 | ✓ pass |
| text on selection | 9.33:1 | >= 4.5:1 | ✓ pass |
| keyword on background | 6.99:1 | >= 4.5:1 | ✓ pass |
| string on background | 4.58:1 | >= 4.5:1 | ✓ pass |
| function on background | 4.52:1 | >= 4.5:1 | ✓ pass |
| type on background | 6.22:1 | >= 4.5:1 | ✓ pass |
| number on background | 4.7:1 | >= 4.5:1 | ✓ pass |
| constant on background | 7.18:1 | >= 4.5:1 | ✓ pass |
| special on background | 4.84:1 | >= 4.5:1 | ✓ pass |
| error on background | 6.63:1 | >= 4.5:1 | ✓ pass |
| Pair | Ratio | Target | Result |
|---|---|---|---|
| text on background | 17.73:1 | 12-21:1 | ✓ pass |
| comment on background | 7.29:1 | >= 7:1 | ✓ pass |
| text on selection | 14.71:1 | >= 4.5:1 | ✓ pass |
| keyword on background | 9.67:1 | >= 7:1 | ✓ pass |
| string on background | 9.91:1 | >= 7:1 | ✓ pass |
| function on background | 13.18:1 | >= 7:1 | ✓ pass |
| type on background | 14.2:1 | >= 7:1 | ✓ pass |
| number on background | 11.37:1 | >= 7:1 | ✓ pass |
| constant on background | 13.91:1 | >= 7:1 | ✓ pass |
| special on background | 8.92:1 | >= 7:1 | ✓ pass |
| error on background | 8.92:1 | >= 7:1 | ✓ pass |
Install
Everything lives in the repository. Variants are Natt, Skumring, Dag and Kontrast.
git clone https://github.com/asconix/kvile.git
cd kvile/ports/vscode
ln -s "$(pwd)" ~/.vscode/extensions/asconix.kvile-0.1.0Reload VS Code, then run "Preferences: Color Theme" and pick a Kvile variant.
cp ports/terminal/ghostty/kvile-* ~/.config/ghostty/themes/In your Ghostty config: theme = kvile-natt
mkdir -p ~/.config/alacritty/themes
cp ports/terminal/alacritty/*.toml ~/.config/alacritty/themes/In alacritty.toml: general.import = ["~/.config/alacritty/themes/kvile-natt.toml"]
cp ports/terminal/kitty/*.conf ~/.config/kitty/In kitty.conf: include kvile-natt.conf
mkdir -p ~/.config/wezterm/colors
cp ports/terminal/wezterm/*.toml ~/.config/wezterm/colors/In wezterm.lua: config.color_scheme = "Kvile Natt"
open ports/terminal/iterm2/kvile-natt.itermcolorsOr: Settings > Profiles > Colors > Color Presets > Import.
ports/terminal/windows-terminal/kvile-schemes.jsonPaste the entries into the schemes array in settings.json, then set colorScheme on your profile.
Wallpapers
A specimen sheet for each variant: the OKLCH plot of its eight accents, neutral ramp, contrast ladder and colorblind simulation, over a soft aurora. Full resolution PNGs for desktop and phone.