Releases: hyperb1iss/opaline
Release v0.4.0
Release Notes — Opaline v0.4.0
Released: 2026-03-22
Opaline v0.4.0 transforms the library from a Ratatui-focused theme engine into a multi-framework theme engine for all of Rust. Five new rendering adapters (crossterm, owo-colors, CSS, syntect, egui) join the existing ratatui and CLI adapters, the builtin theme collection nearly doubles from 20 to 39 themes across 17 colorscheme families, and the core theme contract is narrowed to 26 universal semantic tokens — removing all app-specific leaks.
🌟 Highlights
Multi-Framework Adapter Ecosystem
Five new adapters in src/adapters/ make Opaline themes portable across rendering targets. crossterm and owo-colors cover direct terminal styling, CSS generates custom properties and classes for web frameworks (Leptos, Yew, Dioxus, Tauri), syntect bridges into syntax highlighters like bat and delta, and egui maps themes to full Visuals for immediate-mode GUIs. Each adapter is independently feature-gated — pull in only what you need.
39 Builtin Themes Across 17 Families
19 new themes bring the collection to 39, adding complete families for Ayu (Dark, Mirage, Light), Night Owl (Dark, Light), Flexoki (Dark, Light), Palenight, GitHub (Dark Dimmed, Light), and Monokai Pro, plus missing variants for Catppuccin, Solarized, Gruvbox, One, Tokyo Night, and Kanagawa. Every theme passes the full contract test suite.
Focused Core Contract (26 Tokens, 13 Styles)
App-specific tokens (git.*, diff.*, mode.*, chat.*, code.hash, code.path) and their matching styles are removed from the core contract. Consuming apps now derive domain-specific semantics via register_default_token(). This makes Opaline a clean, universal base layer.
Theme TOML Export
New to_toml(), to_theme_file(), and save_to_file() methods on Theme enable round-trip serialization — load a theme, modify it, export valid TOML. Foundation for theme editor workflows.
File-Backed Theme Override Semantics
Theme discovery now searches file-backed themes first, then builtins. User themes in ~/.config/opaline/themes/ override builtin themes with the same id. New load_theme_by_name_in_dirs() and list_available_themes_in_dirs() functions give apps full control over search paths.
CI Migration to Shared Workflows
All three GitHub Actions workflows (cicd.yml, docs.yml, release.yml) are replaced with thin callers to hyperb1iss/shared-workflows. Total workflow code drops from ~354 lines to ~90 lines. Crates.io publishing now uses OIDC trusted publishing instead of long-lived tokens.
✨ New Adapters
crossterm—From<OpalineColor>→Color,From<OpalineStyle>→ContentStylewith all 9 modifier attributes,Theme::crossterm_styled()helper, and per-character gradient rendering viagradient_styled()andgradient_bar()owo-colors— Zero-allocation terminal coloring withOwoThemeExttrait providingowo_style(),owo_fg(),owo_bg()onTheme, plusgradient_string()for gradient text outputcss—generate_css_vars()emits:root { --opaline-*: #hex; }custom properties,generate_css_classes()produces.opaline-*rule sets with mapped CSS properties (font-weight, opacity, text-decoration), andgenerate_stylesheet()combines bothsyntect—to_syntect_theme()generates a fullsyntect::highlighting::Themewith TextMate scope mappings fromcode.*tokens,ThemeSettingsfrom bg/fg/accent tokens, andFontStyleflag conversionegui—to_egui_visuals()produces completeegui::Visualsfrom theme tokens, starting from dark or light base and overriding all panel, window, selection, widget, and stroke colors
🎨 Theme Collection
- New families: GitHub (Dark Dimmed, Light), Monokai Pro, Ayu (Dark, Mirage, Light), Night Owl (Dark, Light), Palenight, Flexoki (Dark, Light)
- Completed families: Catppuccin Frappé + Macchiato, Solarized Dark, Gruvbox Light, One Light, Tokyo Night Moon, Kanagawa Dragon + Lotus
- Existing theme updates: All 20 existing themes trimmed to the new 26-token core contract — removed
git.*,diff.*,mode.*,code.hash,code.pathtokens and their associated styles - Light theme contrast fixes: Everforest Light accent/grey darkened to ~4:1 contrast ratios (up from ~2.4:1); Rose Pine Dawn and Solarized Light
text.dim/text.mutedoverlap corrected
♻️ Core Engine
- Strict TOML parsing:
#[serde(deny_unknown_fields)]added toThemeFile,ThemeMeta, andStyleDef— unknown keys in theme files now produce parse errors instead of being silently ignored - Resolver hardening: Hex literal color refs (
#rrggbb) in style fg/bg and gradient stops are now parsed viaOpalineColor::from_hex()with properInvalidColorerrors, separate from named token/palette resolution - Gradient deserialization: Custom
Deserializeimpl onGradientroutes throughtry_new(), catching empty-stop errors at parse time rather than panicking - Empty gradient guard: The resolver now returns
OpalineError::EmptyGradientfor zero-stop gradient definitions ThemeInfo::load()— New method loads the theme represented by metadata, preferring file-backed path over builtin fallbacklist_available_themes_for_app()/list_available_themes_in_dirs()— New discovery functions for app-specific and custom theme search paths with deduplication viapush_or_replace_theme()load_theme_by_name_in_dirs()— Directory-driven variant ofload_theme_by_namefor apps managing their own search paths
⚡ Unicode Correctness
- All gradient rendering functions across ratatui, CLI, crossterm, and owo-colors adapters switched from
chars()tographemes(true)viaunicode_segmentation— correct handling of combining marks, emoji with modifiers, and complex scripts ThemeSelectorwidget text wrapping now usesUnicodeWidthStrfor accurate visual width calculation instead of byte length
📝 Documentation
- Five new VitePress guide pages:
crossterm.md,css.md,egui.md,owo-colors.md,syntect.md - Updated all existing guide and reference pages for the new inherent API (no trait import) and 26-token contract
- README broadened from "Ratatui TUI applications" to "Rust applications" throughout
- Added "Used By" section to README listing downstream consumers (git-iris, unifly)
- Cargo.toml description and keywords updated — added
egui,gui; broadened categories
🔧 Infrastructure
- CI/CD, docs, and release workflows migrated to
hyperb1iss/shared-workflowsreusable workflows (~75% line reduction) - Crates.io publishing switched to OIDC trusted publishing via
rust-lang/crates-io-auth-action— tokens auto-revoked on job completion ThemeSelectorvim-style keybindings (j/k) removed — only standardUp/Downarrow keys remain
💥 Breaking Changes
- Token contract narrowed from 36 to 26 tokens — Removed:
git.staged,git.modified,git.untracked,git.deleted,diff.added,diff.removed,diff.hunk,diff.context,mode.active,mode.inactive,mode.hover,code.hash,code.path. Apps using these tokens must now derive them viaregister_default_token(). - Style contract narrowed from 18 to 13 styles — Removed:
file_path,commit_hash,git_staged,git_modified,git_untracked,git_deleted,diff_added,diff_removed,diff_hunk,diff_context,timestamp,author,mode_inactive. Apps must derive these via custom styles. #[serde(deny_unknown_fields)]on schema types — Theme TOML files with unrecognized keys now fail to parse. Remove any non-standard fields from custom themes.- Theme loading priority reversed —
load_theme_by_name()now checks file-backed discovery paths before builtins (was builtins-first). User themes with the same id as a builtin now take precedence. discovery::theme_dirs()scope narrowed — Returns only the Opaline-specific config directory. Useapp_theme_dirs()for app-specific paths.
📋 Upgrade Notes
- Remove app-specific tokens from custom themes — If your
.tomlfiles definegit.*,diff.*,mode.*,code.hash, orcode.pathtokens, delete them from the[tokens]section. Useregister_default_token()in your app code instead. - Clean up unknown TOML fields — Any non-standard keys in
[meta],[styles], or[styles.*]sections will now cause parse errors. Audit custom theme files. - Update code relying on load order — If you depend on builtins taking precedence over user themes, switch to
load_by_name()(builtins-only) instead ofload_theme_by_name()(discovery-first). - Enable new adapters as needed — Add feature flags to
Cargo.toml:crossterm,owo-colors,css,syntect,egui. ThemeSelectorkeybindings — If your app documentation referencesj/kfor navigation, update to arrow keys only.
Release v0.2.0
Release Notes v0.2.0
Released: February 25, 2026
This release marks the initial public release of Opaline, a token-based theme engine for Ratatui TUI applications. Opaline provides a complete TOML-driven theming system with 20 builtin themes, multi-stop gradients, semantic tokens, and deep Ratatui integration.
🌟 Highlights
Complete Theme Engine for Ratatui
A production-ready theming system with a three-layer resolution pipeline: palette → tokens → styles → gradients. Load themes from TOML files, switch at runtime, and apply consistent color to any Ratatui widget without scattering hex codes across your codebase.
20 Professionally Crafted Themes
Ships with 20 builtin themes spanning 11 colorscheme families: SilkCircuit (5 variants), Catppuccin, Dracula, Nord, Rose Pine, Gruvbox, Tokyo Night, Kanagawa, Everforest, One Dark, and Solarized. Every theme enforced by contract tests validating 40+ semantic tokens, 18 required styles, and 5 gradients.
Zero-Import Ratatui API
Inherent methods on Theme eliminate trait imports. Call theme.span("keyword", "text"), theme.line(), theme.text(), and theme.gradient_text() directly. Styles implement Into<ratatui::style::Style> for seamless widget integration.
ThemeSelector Widget with Live Preview
A production-ready, stateful widget providing searchable theme selection with instant live preview via global state. Two-pane layout shows theme list and color swatches. Keyboard navigation with Enter/Esc and automatic rollback on cancel.
Color Manipulation and App-Level Derivation
New darken(), lighten(), and desaturate() methods enable deriving colors from theme tokens. Apps can register custom tokens with register_default_token() that respect TOML overrides, perfect for domain-specific tokens.
🎨 Theme System
Resolution Pipeline
- Strict Resolver - Unresolvable tokens, circular references, and invalid colors error immediately with clear diagnostics
- TOML Schema - Complete serde-based schema with
ThemeFile,ThemeMeta,StyleDefsupporting all 9 Ratatui modifiers - Palette Layer - Raw hex colors (
#rrggbb) as the foundation - Token Layer - 40+ semantic tokens across
text.*,bg.*,accent.*,git.*,diff.*,code.*,border.*,mode.*namespaces - Style Layer - Composed styles with foreground, background, and modifiers (bold, italic, dim, underline, blink, reversed, hidden, crossed-out)
- Gradient Layer - Multi-stop color interpolation with smooth transitions
Builtin Themes (20 Total)
- SilkCircuit - Neon, Soft, Glow, Vibrant, Dawn (electric meets elegant)
- Catppuccin - Mocha (dark), Latte (light)
- Rose Pine - Base, Moon, Dawn (botanical elegance)
- Everforest - Dark, Light (warm green forest tones)
- Tokyo Night - Default, Storm (neo-Tokyo neon)
- Dracula - Classic dark syntax theme
- Nord - Arctic, north-bluish clean
- Gruvbox - Dark (retro groove)
- One Dark - Atom's iconic palette
- Solarized - Light (precision colors)
- Kanagawa - Wave (the great wave)
Build-Time Auto-Discovery
Themes in src/builtins/*.toml are auto-discovered at compile time via build.rs. Generated code includes include_str! constants, name registry, and lookup match statement. Add a theme by dropping a TOML file—no manual registration needed.
Theme Contract Testing
Every builtin theme validated by comprehensive contract tests ensuring all 40+ tokens, 18 styles, and 5 gradients exist. Light themes tested for proper contrast ratios on cream/white backgrounds.
🔧 Core API
Color System (src/color.rs)
- OpalineColor - RGB color primitive with hex parsing (
#rrggbb), lerp interpolation, and conversions - Color Manipulation -
darken(amount),lighten(amount),desaturate(amount)for deriving new colors - Multiple Constructors - From hex strings, RGB tuples, arrays, and u32
- Display/FromStr - Automatic hex formatting and parsing
Style System (src/style.rs)
- OpalineStyle - Composed style with foreground, background, and 9 modifiers
- Builder Pattern - Fluent API with
fg(),bg(),bold(),italic(), etc. - Merge Semantics - Combine styles with explicit override rules
- Ratatui Integration - Implements
Styledtrait, unlocks fullStylizeAPI - Non-Exhaustive - Marked
#[non_exhaustive]for semver safety
Gradient System (src/gradient.rs)
- Multi-Stop Interpolation - Define gradients with 2+ color stops
- Smooth Sampling -
at(t)for single color at position,generate(n)for discrete palette - Fallible Construction -
try_new()returnsResult,new()panics for compile-time guarantees - Gradient Helpers - Free functions
gradient_bar(),gradient_spans(),gradient_line()for common patterns
Theme API (src/theme.rs)
- Color Access -
color(token)with fallback,try_color(token)strict lookup - Style Access -
style(name)with default,try_style(name)strict lookup - Gradient Access -
gradient(name, t)samples at position,get_gradient(name)returns reference - Introspection -
token_names(),palette_names(),style_names(),gradient_names() - Variant Helpers -
is_dark(),is_light()for theme-aware UX - Ratatui Builders -
span(style, text),line(style, text),text(style, text),gradient_text(gradient, text) - App-Level Derivation -
register_default_token(name, value)for domain tokens with TOML override support
Resolution Engine (src/resolver.rs)
- Four-Pass Pipeline - Palette → tokens → styles → gradients with dependency ordering
- Recursive Token Resolution - Tokens reference palette or other tokens, chains allowed
- Cycle Detection - Vec-based traversal tracking for readable circular reference errors
- Strict Validation - Unresolvable tokens return
OpalineError::UnresolvedTokenimmediately
Error Handling (src/error.rs)
- OpalineError - thiserror-based enum covering IO, parse, color, resolution, and validation failures
- Contextual Messages - Path information, token names, and chain traces in all errors
🔌 Integrations
Ratatui Adapter (src/adapters/ratatui.rs)
- From Impls -
OpalineColor → ratatui::style::Color,OpalineStyle → ratatui::style::Style - Inherent Methods -
theme.span(),theme.line(),theme.text(),theme.gradient_text()require no trait import - Gradient Helpers -
gradient_bar(width, char, gradient),gradient_spans(),gradient_line() - All 9 Modifiers - Bold, dim, italic, underline, slow_blink, rapid_blink, reversed, hidden, crossed_out
- Styled Trait - Unlocks Ratatui's fluent
StylizeAPI onOpalineStyle
CLI Adapter (src/adapters/cli.rs)
- colored Crate Integration - Behind
clifeature flag for ANSI terminal output - ColoredExt Trait - Extension methods on
OpalineColorfor colored strings - ThemeCliExt Trait - Extension methods on
Themefor styled CLI output - gradient_string() - Render gradient text for terminal emulators
ThemeSelector Widget (src/widgets/theme_selector.rs)
- Stateful Widget - Complete theme picker with 753 lines of production-ready code
- Pre-Loading - All themes cached in memory for instant preview on navigation
- Live Preview - Integrates with global state to apply themes on cursor movement
- Search Filter - Filter by theme name and author with real-time list updates
- Two-Pane Layout - List (55%) + preview pane (45%) with color swatches and gradients
- Keyboard Navigation - ↑↓/jk for movement, Tab/BackTab for focus, Enter to confirm, Esc to cancel
- Rollback Support - Original theme restored automatically on Esc
- Section Headers - Groups dark and light themes visually
- App Derivation - Optional callback for app-specific token injection
🛠️ Developer Experience
Names Module (src/names.rs)
- Type-Safe Constants - Three modules (
tokens,styles,gradients) with const strings - IDE Autocomplete - Use
tokens::TEXT_PRIMARYinstead of"text.primary" - Contract Documentation - All 40+ tokens, 25 styles, 5 gradients in one place
Loader API (src/loader.rs)
- load_from_str() - Parse TOML from string with optional path context
- load_from_file() - Direct filesystem loading with automatic error context
- Clean Pipeline - TOML → ThemeFile (serde) → resolver → Theme
Discovery System (src/discovery.rs)
- XDG Compliance - Uses
dirscrate for cross-platform config directories - theme_dirs() - Global opaline themes in
~/.config/opaline/themes/ - app_theme_dirs(name) - App-specific themes in
~/.config/<app>/themes/ - Composable - Returns
Vec<PathBuf>for scanning multiple locations
ThemeBuilder API (src/theme.rs)
- Programmatic Construction - Build themes in code without TOML
- Fluent API -
palette(),token(),style(),gradient()methods - Validation - Same resolution pipeline as TOML themes
- Metadata Support -
name(),author(),description(),variant(),version()
Builtin Registry (src/builtins/mod.rs)
- list_available_themes() - Returns
Vec<ThemeInfo>with metadata for picker UIs - load_by_name(id) - Load by kebab-case ID (e.g.,
"catppuccin-mocha") - ThemeInfo - Struct with
display_name,id,variant,author,description
🎯 Feature Flags
| Feature | Default | Description |
|---|---|---|
builtin-themes |
✓ | 20 embedded TOML themes via include_str! |
gradients |
✓ | Multi-stop gradient interpolation |
ratatui |
✓ | From impls, inherent methods, Styled trait |
cli |
— | colored crate adapter for ANSI output |
global-state |
— | Process-wide current()/set_theme() singleton |
discovery |
— | XDG-compliant theme directory scanning |
widgets |
— | ThemeSelector widget with live preview |