palette-core
TOML-defined theme system with inheritance and multi-target export.
Themes are authored as TOML presets. Variants inherit from a base and override only what changes. The crate parses, merges, and converts them into typed palettes for any rendering target.
Quick start
1. Ship a single preset
Pick a built-in theme and render it for your target.
CSS
use load_preset;
let palette = load_preset?;
let css = format!;
}
Terminal (ratatui)
use load_preset;
use to_terminal_theme;
let palette = load_preset?;
let theme = to_terminal_theme;
// theme.base.background, theme.syntax.keywords, etc.
egui
use load_preset;
use to_egui_visuals;
let palette = load_preset?;
ctx.set_visuals;
JSON
use load_preset;
let palette = load_preset?;
let json = palette.to_json?;
WASM
import from "palette-core";
const palette = ;
console.log; // "TokyoNight (Night)"
console.log; // CSS custom properties
const css = ;
2. Default preset with end-user theme switching
Use a Registry to expose all built-in presets. Load a default at startup. Let users pick from the list.
use Registry;
let reg = new;
// Populate a settings menu
for info in reg.list
// Load the user's choice (or fall back to a default)
let user_choice = "catppuccin";
let palette = reg.load?;
CSS — generate all themes for live switching
use Registry;
let reg = new;
let mut css = Stringnew;
for info in reg.list
Switch themes in the browser by setting a data attribute:
document... = "catppuccin";
Reference the variables in CSS:
}
Terminal — swap themes at runtime
use Registry;
use to_terminal_theme;
let reg = new;
let theme = to_terminal_theme;
WASM
import from "palette-core";
const reg = ;
const themes = reg.; // [{id, name, style}, ...]
const palette = reg.;
3. Developer-defined custom presets
Add your own presets — either full themes or variants that inherit from a built-in.
Variant that inherits from a built-in:
# corporate_dark.toml
[]
= "Corporate Dark"
= "corporate_dark"
= "1"
= "dark"
= "preset-variant"
= "tokyonight"
[]
= "#FF3333"
= "#0099FF"
This theme gets all of tokyonight's colors, overriding only the semantic values.
Full custom preset:
# brand.toml
[]
= "Brand Theme"
= "brand"
= "1"
= "light"
= "preset-base"
[]
= "#FFFFFF"
= "#1A1A1A"
[]
= "#CC0000"
= "#008800"
Register and use:
use Path;
use Registry;
let mut reg = new;
// Add a single file
reg.add_file?;
// Or add an entire directory of .toml files
reg.add_dir?;
// Custom themes appear alongside built-ins
for info in reg.list
// Load like any other theme — inheritance resolves automatically
let palette = reg.load?;
Custom variants can inherit from built-ins or from other custom presets already in the registry.
WASM
import from "palette-core";
const reg = ;
reg.;
const palette = reg.;
4. End-user-defined presets
Let your users load their own theme files at runtime. The same registry handles built-in, developer, and user themes.
use Path;
use Registry;
let mut reg = new;
// Developer themes ship with the app
reg.add_dir?;
// End-user themes loaded from a config directory
let user_themes_dir = config_dir
.map;
if let Some = user_themes_dir.as_deref
// All themes — built-in, developer, and user — are in one list
for info in reg.list
let palette = reg.load?;
A user preset with the same preset_id as an existing theme replaces it, so users can override built-ins or developer themes.
User presets support inheritance too — a user can write a variant that inherits from any theme already in the registry:
# ~/.config/myapp/themes/my_nord.toml
[]
= "My Nord"
= "my_nord"
= "1"
= "dark"
= "preset-variant"
= "nord"
[]
= "#1a1a2e"
WASM — user-supplied TOML string
const reg = ;
reg.;
const palette = reg.;
Utilities
WCAG contrast validation
use ;
use validate_palette;
let palette = load_preset?;
let violations = validate_palette;
for v in &violations
Color manipulation
use Color;
let base = from_hex?;
let hover = base.lighten;
let disabled = base.desaturate;
let accent = base.rotate_hue;
let overlay = base.blend;
let ratio = base.contrast_ratio;
Methods: lighten, darken, saturate, desaturate, rotate_hue, blend, contrast_ratio, meets_level. Manipulation methods take absolute amounts (CSS model). Non-finite inputs return the color unchanged.
Platform overrides
let manifest = from_toml?;
let overrides = from_sections?;
// overrides["terminal"].background, overrides["web"].foreground, etc.
Features
| Feature | Dependency | What it adds |
|---|---|---|
terminal |
ratatui |
Palette → ratatui::style::Color maps |
egui |
egui |
Palette → egui::Visuals |
snapshot |
serde_json |
JSON serialization of Palette |
platform |
— | Parse [platform.terminal] / [platform.web] overrides |
wasm |
wasm-bindgen, js-sys |
JavaScript bindings via wasm-bindgen (includes snapshot) |
full |
all except wasm |
terminal + egui + snapshot + platform |
Core functionality (parsing, merge, CSS export, WCAG contrast, color manipulation) requires no optional dependencies.
Bundled presets
| Family | Presets |
|---|---|
| Ayu | ayu_dark, ayu_light, ayu_mirage |
| Catppuccin | catppuccin, catppuccin_frappe, catppuccin_latte, catppuccin_macchiato |
| Dracula | dracula |
| Everforest | everforest_dark, everforest_light |
| GitHub | github_dark, github_light |
| Gruvbox | gruvbox_dark, gruvbox_light |
| Kanagawa | kanagawa |
| Monokai | monokai |
| Nord | nord |
| One | one_dark, one_light |
| Rosé Pine | rose_pine, rose_pine_dawn, rose_pine_moon |
| Solarized | solarized_dark, solarized_light |
| TokyoNight | tokyonight, tokyonight_storm, tokyonight_day, tokyonight_moon |
All presets are embedded at compile time via include_str!. Use preset_ids() to list them.
Preset format
Base presets define all sections. Variants declare inherits in [meta] and override only differing values.
[]
= "My Theme Storm"
= "my_theme_storm"
= "1"
= "storm"
= "preset-variant"
= "my_theme"
[]
= "#24283b"
Sections: base, semantic, diff, surface, typography, syntax, editor, terminal.
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.