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.