# Theme System

Opaline's theme system is built around a **three-layer resolution pipeline** that transforms raw hex colors into fully composed styles.

## The Resolution Pipeline

```mermaid
graph TD
    TOML["TOML File"] --> Parse["ThemeFile (serde)"]
    Parse --> Resolve["Resolver"]
    Resolve --> Palette["Palette<br/>Raw hex → OpalineColor"]
    Resolve --> Tokens["Tokens<br/>Semantic names → colors"]
    Resolve --> Styles["Styles<br/>fg + bg + modifiers"]
    Resolve --> Gradients["Gradients<br/>Multi-stop interpolation"]
    Palette --> Theme["Theme"]
    Tokens --> Theme
    Styles --> Theme
    Gradients --> Theme

    style TOML fill:#e135ff,color:#fff
    style Theme fill:#80ffea,color:#121218
```

### Layer 1: Palette

Raw named colors. These are the building blocks — hex values with human-readable names:

```toml
[palette]
purple = "#e135ff"
cyan = "#80ffea"
coral = "#ff6ac1"
bg_dark = "#121218"
```

### Layer 2: Tokens

Semantic references that point to palette colors. This is where meaning lives:

```toml
[tokens]
"accent.primary" = "purple"      # resolves to #e135ff
"accent.secondary" = "cyan"      # resolves to #80ffea
"bg.base" = "bg_dark"            # resolves to #121218
```

Tokens can reference other tokens — the resolver handles transitive lookups with cycle detection.

### Layer 3: Styles

Composed styles that combine a foreground color, background color, and text modifiers:

```toml
[styles]
keyword = { fg = "accent.primary", bold = true }
selected = { fg = "accent.secondary", bg = "bg.highlight" }
error_style = { fg = "error" }
```

Style `fg` and `bg` values reference tokens, which reference palette colors. Change the palette and everything downstream updates.

## Theme Structure

A complete Opaline theme TOML file has four sections:

```toml
[meta]
name = "My Theme"
author = "me"
variant = "dark"        # "dark" or "light"
version = "1.0"
description = "A beautiful theme"

[palette]
# Raw hex colors...

[tokens]
# Semantic token mappings...

[styles]
# Composed style definitions...

[gradients]
# Named gradient stop lists...
```

## Loading Themes

```rust
use opaline::Theme;

// From a TOML string
let theme = opaline::load_from_str(toml_string, None)?;

// From a file path
let theme = opaline::load_from_file("path/to/theme.toml")?;

// From builtins
let theme = opaline::load_by_name("dracula").unwrap();

// Default (SilkCircuit Neon)
let theme = Theme::default();
```

## Strict Resolution

The resolver is intentionally strict. If a token references a palette color that doesn't exist, or a style references an undefined token, you get a clear error — not a silent fallback.

```rust
// This will error if "nonexistent" isn't in the palette
// "accent.primary" = "nonexistent"
// → OpalineError::UnresolvedToken { token: "accent.primary", reference: "nonexistent" }
```

Circular references are also detected:

```rust
// "a" = "b", "b" = "a"
// → OpalineError::CircularReference { chain: ["a", "b", "a"] }
```

## Variant Helpers

Every theme declares whether it's dark or light:

```rust
let theme = Theme::default();
assert!(theme.is_dark());

let latte = opaline::load_by_name("catppuccin-latte").unwrap();
assert!(latte.is_light());
```

This is useful for adapting UI elements that need variant-aware rendering (like borders or shadows).
