Skip to content

Releases: hyperb1iss/opaline

Release v0.4.0

22 Mar 05:31

Choose a tag to compare

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

  • crosstermFrom<OpalineColor>Color, From<OpalineStyle>ContentStyle with all 9 modifier attributes, Theme::crossterm_styled() helper, and per-character gradient rendering via gradient_styled() and gradient_bar()
  • owo-colors — Zero-allocation terminal coloring with OwoThemeExt trait providing owo_style(), owo_fg(), owo_bg() on Theme, plus gradient_string() for gradient text output
  • cssgenerate_css_vars() emits :root { --opaline-*: #hex; } custom properties, generate_css_classes() produces .opaline-* rule sets with mapped CSS properties (font-weight, opacity, text-decoration), and generate_stylesheet() combines both
  • syntectto_syntect_theme() generates a full syntect::highlighting::Theme with TextMate scope mappings from code.* tokens, ThemeSettings from bg/fg/accent tokens, and FontStyle flag conversion
  • eguito_egui_visuals() produces complete egui::Visuals from 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.path tokens 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.muted overlap corrected

♻️ Core Engine

  • Strict TOML parsing: #[serde(deny_unknown_fields)] added to ThemeFile, ThemeMeta, and StyleDef — 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 via OpalineColor::from_hex() with proper InvalidColor errors, separate from named token/palette resolution
  • Gradient deserialization: Custom Deserialize impl on Gradient routes through try_new(), catching empty-stop errors at parse time rather than panicking
  • Empty gradient guard: The resolver now returns OpalineError::EmptyGradient for zero-stop gradient definitions
  • ThemeInfo::load() — New method loads the theme represented by metadata, preferring file-backed path over builtin fallback
  • list_available_themes_for_app() / list_available_themes_in_dirs() — New discovery functions for app-specific and custom theme search paths with deduplication via push_or_replace_theme()
  • load_theme_by_name_in_dirs() — Directory-driven variant of load_theme_by_name for apps managing their own search paths

⚡ Unicode Correctness

  • All gradient rendering functions across ratatui, CLI, crossterm, and owo-colors adapters switched from chars() to graphemes(true) via unicode_segmentation — correct handling of combining marks, emoji with modifiers, and complex scripts
  • ThemeSelector widget text wrapping now uses UnicodeWidthStr for 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-workflows reusable 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
  • ThemeSelector vim-style keybindings (j/k) removed — only standard Up/Down arrow 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 via register_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 reversedload_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. Use app_theme_dirs() for app-specific paths.

📋 Upgrade Notes

  1. Remove app-specific tokens from custom themes — If your .toml files define git.*, diff.*, mode.*, code.hash, or code.path tokens, delete them from the [tokens] section. Use register_default_token() in your app code instead.
  2. Clean up unknown TOML fields — Any non-standard keys in [meta], [styles], or [styles.*] sections will now cause parse errors. Audit custom theme files.
  3. Update code relying on load order — If you depend on builtins taking precedence over user themes, switch to load_by_name() (builtins-only) instead of load_theme_by_name() (discovery-first).
  4. Enable new adapters as needed — Add feature flags to Cargo.toml: crossterm, owo-colors, css, syntect, egui.
  5. ThemeSelector keybindings — If your app documentation references j/k for navigation, update to arrow keys only.

Release v0.2.0

25 Feb 07:43

Choose a tag to compare

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, StyleDef supporting 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 Styled trait, unlocks full Stylize API
  • 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() returns Result, 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::UnresolvedToken immediately

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 Stylize API on OpalineStyle

CLI Adapter (src/adapters/cli.rs)

  • colored Crate Integration - Behind cli feature flag for ANSI terminal output
  • ColoredExt Trait - Extension methods on OpalineColor for colored strings
  • ThemeCliExt Trait - Extension methods on Theme for 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_PRIMARY instead 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 dirs crate 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

📦...

Read more