NAME
opaline — ✦ A token-based theme engine for Rust TUI applications
SYNOPSIS
INFO
DESCRIPTION
✦ A token-based theme engine for Rust TUI applications
README
✦ opaline
A token-based theme engine for Rust applications
39 builtin themes · semantic tokens · multi-stop gradients · ratatui · egui · iced · crossterm · syntect · CSS
Docs • llms.txt • Quick Start • Features • Themes • Usage • Custom Themes • Contributing
SilkCircuit Neon (dark) and SilkCircuit Dawn (light) — two of 39 builtin themes
💜 What is Opaline?
Opaline is a theme engine that brings consistent, beautiful color to Rust 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.
Opaline ships adapters for ratatui, egui, iced, crossterm, owo-colors, syntect, and CSS — one theme definition, every rendering target.
TOML file → ThemeFile (serde) → Resolver (palette → tokens → styles → gradients) → Theme
Opaline ships with 39 professionally crafted themes spanning 17 colorscheme families, all enforced by a strict contract test suite that validates 26 core semantic tokens, 13 required styles, and 5 gradients per theme.
✦ Features
| Feature | Description |
|---|---|
| 🎨 39 Builtin Themes | SilkCircuit, Catppuccin, GitHub, Monokai Pro, Ayu, Night Owl, Flexoki, Palenight, Dracula, Nord, Rose Pine, Gruvbox, Solarized, Tokyo Night, Kanagawa, Everforest, One Dark/Light |
| 🔗 Semantic Tokens | 26 core tokens across generic text.*, bg.*, accent.*, border.*, and 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 |
| 🎮 egui Integration | Color32 conversion, full Visuals generation from theme tokens |
| 🪄 iced Integration | Color conversion plus Palette/Custom theme generation from theme tokens |
| ⌨️ Crossterm Adapter | Direct Color/ContentStyle conversion with gradient rendering |
| 🌈 owo-colors Adapter | Zero-allocation terminal coloring with Style conversion |
| 🖌️ Syntax Highlighting | Generate syntect themes — powers bat, delta, and more |
| 🌐 CSS Generation | Custom properties + classes for web frameworks (Leptos, Yew, Dioxus, Tauri) |
| 🎛️ 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 |
| ⚡ Zero Cost Builtins | Themes embedded via include_str! at compile time — no file I/O at runtime |
⚡ Quick Start
Add opaline to your Cargo.toml:
[dependencies]
opaline = "0.2"
Load a theme and start styling:
use opaline::load_by_name;// Load any builtin theme 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("muted", "src/main.rs"); // styled Span
Run the interactive demo
cargo run --example theme-showcase
Browse all 39 themes, see every style and gradient rendered in real-time.
🎨 Builtin Themes
| Family | Variants | Character |
|---|---|---|
| SilkCircuit | Neon, Soft, Glow, Vibrant, Dawn | Electric meets elegant — the signature design language |
| Catppuccin | Mocha, Macchiato, Frappé, Latte | Soothing pastels across four flavors |
| GitHub | Dark Dimmed, Light | Clean, familiar, institutional |
| Monokai Pro | Classic | The iconic warm vivid syntax palette |
| Ayu | Dark, Mirage, Light | Calm, modern, bright across three modes |
| Night Owl | Dark, Light | Accessibility-designed deep blue |
| Flexoki | Dark, Light | Ink-on-paper aesthetic, Oklab-designed |
| Palenight | — | Soft purple-blue pastel sci-fi |
| Rose Pine | Base, Moon, Dawn | Botanical elegance across three variants |
| Everforest | Dark, Light | Warm green forest tones |
| Tokyo Night | Default, Storm, Moon | Neo-Tokyo neon aesthetic |
| Kanagawa | Wave, Dragon, Lotus | The great wave — dark, darker, light |
| Dracula | — | The classic dark syntax theme |
| Nord | — | Arctic, north-bluish clean |
| Gruvbox | Dark, Light | Retro groove with warm contrast |
| Solarized | Dark, Light | Precision colors for machines and people |
| One | Dark, Light | Atom's iconic syntax palette |
Every theme is contract-tested: 26 core semantic tokens, 13 required styles, 5 required gradients.
🔮 Usage
Colors and Styles
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("muted", "src/lib.rs"); let keyword_span = theme.span("keyword", "fn");
Gradients
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");
Theme Switching
use opaline::{list_available_themes, load_by_name};// Enumerate all themes for a picker UI let themes = list_available_themes(); for info in &themes { let author = if info.author.is_empty() { "—" } else { &info.author }; println!("{} ({:?}) by {}", info.display_name, info.variant, author); }
// Hot-swap themes at runtime let dracula = load_by_name("dracula").unwrap(); let nord = load_by_name("nord").unwrap();
ThemeBuilder (Programmatic)
use opaline::{OpalineColor, OpalineStyle, Theme};
let theme = Theme::builder("My Theme") .palette("bg", OpalineColor::new(26, 27, 38)) .palette("fg", OpalineColor::new(192, 202, 245)) .palette("blue", OpalineColor::new(122, 162, 247)) .token("text.primary", OpalineColor::new(192, 202, 245)) .token("bg.base", OpalineColor::new(26, 27, 38)) .token("accent.primary", OpalineColor::new(122, 162, 247)) .style("keyword", OpalineStyle::fg(OpalineColor::new(122, 162, 247)).bold()) .build();
🪄 Custom Themes
Add a builtin theme by dropping a .toml file in src/builtins/. For user themes, load from any path at runtime or place the file in your app's theme directory.
[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" "bg.selection" = "bg" "accent.primary" = "blue"
... 26 required core tokens across text., bg., accent., border., code.*, etc.
[styles] keyword = { fg = "accent.primary", bold = true }
... 13 required core styles
[gradients] primary = ["blue", "purple"]
... 5 required gradients
The resolver validates everything at load time — circular references, missing tokens, and invalid colors all produce clear error messages via OpalineError.
⚙️ Feature Flags
| Feature | Default | Description |
|---|---|---|
builtin-themes | ✓ | 39 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 |
crossterm | — | Direct crossterm Color/ContentStyle adapter |
owo-colors | — | Zero-allocation terminal coloring |
css | — | CSS custom properties + classes generation |
syntect | — | Syntax highlighting theme generation |
egui | — | egui Visuals/Color32 adapter |
iced | — | iced Custom/Palette/Color adapter |
global-state | — | Process-wide current()/set_theme() |
discovery | — | Load user themes from ~/.config/ |
widgets | — | Theme selector widget with live preview |
🏗️ Architecture
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 |
🧪 Development
cargo check # Fast type check
cargo clippy --all-targets --all-features # Pedantic lint gate
cargo test --all-features # Full test suite (210 tests)
cargo doc --all-features --open # Generate docs
cargo run --example theme-showcase # Interactive TUI demo
Requires Rust 1.85+ (Edition 2024). unsafe_code = "forbid", clippy::pedantic deny.
🚀 Used By
| Project | Description |
|---|---|
| git-iris | AI-powered Git workflow assistant with Iris Studio TUI |
| unifly | CLI + TUI for UniFi network management |
🤝 Contributing
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.
⚖️ License
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! ⚡️