A token-based theme engine for Ratatui TUI applications
20 builtin themes · semantic tokens · multi-stop gradients · zero unsafe
Docs • llms.txt • Quick Start • Features • Themes • Usage • Custom Themes • Contributing
SilkCircuit Neon (dark) and SilkCircuit Dawn (light) — two of 20 builtin themes
Opaline is a theme engine that brings consistent, beautiful color to Ratatui terminal applications. Instead of scattering hex codes across your codebase, you define themes as TOML files with a palette → token → style → gradient resolution pipeline. Switch themes at runtime with a single call — every widget updates instantly.
TOML file → ThemeFile (serde) → Resolver (palette → tokens → styles → gradients) → Theme
Opaline ships with 20 professionally crafted themes spanning 8 colorscheme families, all enforced by a strict contract test suite that validates 40+ semantic tokens, 18 styles, and 5 gradients per theme.
| Feature | Description |
|---|---|
| 🎨 20 Builtin Themes | SilkCircuit, Catppuccin, Dracula, Nord, Rose Pine, Gruvbox, Solarized, Tokyo Night, Kanagawa, Everforest, One Dark |
| 🔗 Semantic Tokens | 40+ tokens across text.*, bg.*, accent.*, git.*, diff.*, code.* namespaces |
| 🌊 Multi-Stop Gradients | Smooth color interpolation with gradient_bar(), gradient_text_line(), and gradient_spans() |
| 🖥️ Deep Ratatui Integration | From impls, Styled trait, inherent span(), line(), text(), gradient_text() on Theme |
| 🎛️ ThemeSelector Widget | Drop-in theme picker with live preview, search filtering, and cancel/restore |
| 🔬 Color Manipulation | darken(), lighten(), desaturate() for deriving colors from theme palettes |
| 🏗️ ThemeBuilder | Programmatic theme construction without TOML — perfect for runtime customization |
| 🧩 App-Level Derivation | Register app-specific tokens/styles with register_default_token() — TOML overrides respected |
| 🔍 Theme Discovery | Scan ~/.config/ for user themes, list metadata for picker UIs |
| 🌐 Global State | Optional process-wide current()/set_theme() behind a feature flag |
| 🛡️ Strict Resolution | Cycle detection, unresolvable token errors, compile-time theme validation |
| 🖨️ CLI Adapter | colored crate integration for ANSI terminal output outside of Ratatui |
| ⚡ Zero Cost Builtins | Themes embedded via include_str! at compile time — no file I/O at runtime |
Add opaline to your Cargo.toml:
[dependencies]
opaline = "0.1"Load a theme and start styling:
use opaline::load_by_name;
// Load any of 20 builtin themes
let theme = load_by_name("catppuccin-mocha").expect("theme exists");
// Use semantic colors and styles in your Ratatui widgets
let style = theme.style("keyword"); // bold accent color
let color = theme.color("accent.primary"); // OpalineColor
let span = theme.span("file_path", "src/main.rs"); // styled Spancargo run --example theme-showcaseBrowse all 20 themes, see every style and gradient rendered in real-time.
| Family | Variants | Character |
|---|---|---|
| SilkCircuit | Neon, Soft, Glow, Vibrant, Dawn | Electric meets elegant — the signature design language |
| Catppuccin | Mocha, Latte | Soothing pastels for dark and light |
| Rose Pine | Base, Moon, Dawn | Botanical elegance across three variants |
| Everforest | Dark, Light | Warm green forest tones |
| Tokyo Night | Default, Storm | Neo-Tokyo neon aesthetic |
| Dracula | — | The classic dark syntax theme |
| Nord | — | Arctic, north-bluish clean |
| Gruvbox | Dark | Retro groove with warm contrast |
| One Dark | — | Atom's iconic syntax palette |
| Solarized | Light | Precision colors for machines and people |
| Kanagawa | Wave | The great wave off Kanagawa |
Every theme is contract-tested: 40+ semantic tokens, 18 required styles, 5 required gradients.
use opaline::Theme;
let theme = Theme::default(); // SilkCircuit Neon
// Semantic color access
let primary = theme.color("accent.primary");
let bg = theme.color("bg.base");
// Composed styles (fg + bg + modifiers)
let keyword = theme.style("keyword"); // bold accent
let error = theme.style("error_style"); // red foreground
let selected = theme.style("active_selected"); // accent on highlight bg
// Styled spans for inline text — no trait import needed
let path = theme.span("file_path", "src/lib.rs");
let hash = theme.span("commit_hash", "a1b2c3d");use opaline::{Theme, gradient_bar};
let theme = Theme::default();
// Render a gradient progress bar
if let Some(gradient) = theme.get_gradient("aurora") {
let bar = gradient_bar(40, '█', gradient); // Line with per-char colors
}
// Gradient-styled text (each character gets interpolated color)
let title = theme.gradient_text("primary", "Opaline Theme Engine");use opaline::{list_available_themes, load_by_name};
// Enumerate all themes for a picker UI
let themes = list_available_themes();
for info in &themes {
println!("{} ({:?}) by {}", info.display_name, info.variant,
info.author.as_deref().unwrap_or("—"));
}
// Hot-swap themes at runtime
let dracula = load_by_name("dracula").unwrap();
let nord = load_by_name("nord").unwrap();use opaline::ThemeBuilder;
let theme = ThemeBuilder::new("My Theme")
.palette("bg", "#1a1b26")
.palette("fg", "#c0caf5")
.palette("blue", "#7aa2f7")
.token("text.primary", "fg")
.token("bg.base", "bg")
.token("accent.primary", "blue")
.style("keyword", "accent.primary", None, true, false, false)
.build()
.expect("valid theme");Drop a .toml file in src/builtins/ — it's auto-discovered at compile time. Or load from any path at runtime.
[meta]
name = "My Theme"
author = "your name"
variant = "dark" # or "light"
description = "A custom theme"
[palette]
bg = "#1a1b26"
fg = "#c0caf5"
blue = "#7aa2f7"
purple = "#bb9af7"
[tokens]
"text.primary" = "fg"
"bg.base" = "bg"
"accent.primary" = "blue"
# ... 40+ tokens across text.*, bg.*, accent.*, git.*, diff.*, code.*, etc.
[styles]
keyword = { fg = "accent.primary", bold = true }
# ... 18 required styles
[gradients]
primary = ["blue", "purple"]
# ... 5 required gradientsThe resolver validates everything at load time — circular references, missing tokens, and invalid colors all produce clear error messages via OpalineError.
| Feature | Default | Description |
|---|---|---|
builtin-themes |
✓ | 20 embedded TOML themes via include_str! |
gradients |
✓ | Multi-stop gradient interpolation |
ratatui |
✓ | From impls, inherent span()/line()/text()/gradient_text() |
cli |
— | colored crate adapter for ANSI output |
global-state |
— | Process-wide current()/set_theme() |
discovery |
— | Load user themes from ~/.config/ |
widgets |
— | Theme selector widget with live preview |
TOML → ThemeFile (serde) → Resolver → Theme
│ │ │
│ palette │ ├── color("token.name") → OpalineColor
│ tokens │ ├── style("style_name") → OpalineStyle
│ styles │ ├── gradient("name") → Gradient
│ gradients │ └── meta (name, author, variant)
│ │
│ ├── palette → token resolution
│ ├── token → style resolution
│ ├── cycle detection
│ └── gradient stop resolution
| Component | Purpose |
|---|---|
OpalineColor |
RGB color with hex/tuple/array/u32 conversions + lerp + darken/lighten/desaturate |
OpalineStyle |
Composed style (fg, bg, 9 modifiers) with builder pattern |
Gradient |
Multi-stop color interpolation with at(t) and generate(n) |
Theme |
Fully resolved theme with color(), style(), gradient() accessors |
ThemeBuilder |
Programmatic theme construction without TOML |
ThemeInfo |
Metadata for theme discovery and picker UIs |
OpalineError |
Typed errors for IO, parsing, resolution, and validation failures |
cargo check # Fast type check
cargo clippy --all-targets --all-features # Pedantic lint gate
cargo test --all-features # Full test suite (135 tests)
cargo doc --all-features --open # Generate docs
cargo run --example theme-showcase # Interactive TUI demoRequires Rust 1.85+ (Edition 2024). unsafe_code = "forbid", clippy::pedantic deny.
Contributions welcome! Adding a new builtin theme is as easy as dropping a .toml file in src/builtins/ — it's auto-discovered at compile time. Run cargo test --all-features to validate against the contract test suite.
Distributed under the MIT License. See LICENSE for details.
📖 Documentation · 📦 API Reference · 🐛 Report Bug · 💡 Request Feature
Created by Stefanie Jane 🌠
If you find this useful, buy me a Monster Ultra Violet! ⚡️





