Theme System

Theme management system providing theme context, CSS variables, and theme switching functionality.

Table of Contents

Overview

hikari-theme provides a complete theme management solution:

All theme components feature:

ThemeProvider

Provides theme context for the entire application.

Basic Usage

rust
1
2
3
4
5
6
7
8
use hikari_theme::ThemeProvider;

rsx! {
    ThemeProvider { initial_palette: "hikari".to_string() }
        // Application content
        App {}
    }
}

Theme Switching

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#[component]
fn App() -> Element {
    let mut theme = use_signal(|| "hikari".to_string());

    rsx! {
        ThemeProvider { initial_palette: theme() }
            div {
                button {
                    onclick: move |_| {
                        theme.set(if theme() == "hikari" {
                            "tairitsu".to_string()
                        } else {
                            "hikari".to_string()
                        });
                    },
                    "Switch Theme"
                }
                // Application content
            }
        }
    }
}

Props

PropertyTypeDefaultDescription
paletteString"hikari"Theme identifier
childrenElement-Child elements

Supported Themes

ThemeContext

Data structure containing theme configuration and color definitions.

Structure Definition

rust
1
2
3
4
pub struct ThemeContext {
    pub palette: String,
    pub colors: Palette,
}

Field Descriptions

Default Values

rust
1
2
3
4
5
6
7
8
impl Default for ThemeContext {
    fn default() -> Self {
        ThemeContext {
            initial_palette: "hikari".to_string(),
            colors: themes::Hikari::palette(),
        }
    }
}

Generated Resources

Auto-generated static resources and CSS variables.

Tailwind CSS

rust
1
2
3
4
use hikari_theme::generated::tailwind;

// Access generated Tailwind CSS classes
let tailwind_classes = tailwind::TAILWIND_CLASSES;

Generated Content

The generated/mod.rs module contains:

File Locations

mermaid

CSS Variables System

The theme system uses CSS variables for dynamic theme switching.

Root Variables

Defined under :root or [data-theme]:

css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[data-theme="hikari"] {
    --hi-color-primary: #FFB3A7;
    --hi-color-secondary: #519A73;
    --hi-color-accent: #FFC773;
    --hi-color-background: #FFFFFF;
    --hi-color-surface: #FFFFFF;
    --hi-color-text-primary: #1A1A2E;
    --hi-color-text-secondary: #666666;
}

[data-theme="tairitsu"] {
    --hi-color-primary: #144A74;
    --hi-color-secondary: #519A73;
    --hi-color-accent: #FFC773;
    --hi-color-background: #161823;
    --hi-color-surface: rgb(32,35,54);
    --hi-color-text-primary: #C9D1D9;
    --hi-color-text-secondary: #8B949E;
}

Using CSS Variables

Use in component styles:

rust
1
2
3
4
5
6
rsx! {
    div {
        style: "color: var(--hi-color-primary); background: var(--hi-color-surface);",
        "Using theme variables"
    }
}

Or in SCSS:

scss
1
2
3
4
5
.my-component {
    color: var(--hi-color-primary);
    background-color: var(--hi-color-surface);
    border: 1px solid var(--hi-color-border);
}

Complete Variable List

Color Variables

css
1
2
3
4
5
6
7
8
9
10
11
--hi-color-primary          /* Primary color */
--hi-color-secondary        /* Secondary color */
--hi-color-accent           /* Accent color */
--hi-color-success          /* Success color */
--hi-color-warning          /* Warning color */
--hi-color-danger           /* Danger color */
--hi-color-background       /* Background color */
--hi-color-surface          /* Surface color */
--hi-color-border           /* Border color */
--hi-color-text-primary     /* Primary text color */
--hi-color-text-secondary   /* Secondary text color */

Typography Variables

css
1
2
3
4
5
6
7
8
9
--hi-font-family-sans       /* Sans-serif font */
--hi-font-family-mono       /* Monospace font */
--hi-font-size-xs           /* 12px */
--hi-font-size-sm           /* 14px */
--hi-font-size-base         /* 16px */
--hi-font-size-lg           /* 18px */
--hi-font-size-xl           /* 20px */
--hi-font-size-2xl          /* 24px */
--hi-font-size-3xl          /* 30px */

Spacing Variables

css
1
2
3
4
5
6
--hi-spacing-xs            /* 4px */
--hi-spacing-sm            /* 8px */
--hi-spacing-md            /* 16px */
--hi-spacing-lg            /* 24px */
--hi-spacing-xl            /* 32px */
--hi-spacing-2xl           /* 48px */

Radius Variables

css
1
2
3
4
5
--hi-radius-sm             /* 4px */
--hi-radius-md             /* 8px */
--hi-radius-lg             /* 12px */
--hi-radius-xl             /* 16px */
--hi-radius-full           /* 9999px */

Shadow Variables

css
1
2
3
4
--hi-shadow-sm             /* Small shadow */
--hi-shadow-md             /* Medium shadow */
--hi-shadow-lg             /* Large shadow */
--hi-shadow-xl             /* Extra large shadow */

Transition Variables

css
1
2
3
--hi-transition-fast       /* 150ms */
--hi-transition-base       /* 200ms */
--hi-transition-slow       /* 300ms */

Theme Switching

Runtime Switching

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#[component]
fn ThemeSwitcher() -> Element {
    let mut theme = use_signal(|| "hikari".to_string());

    rsx! {
        ThemeProvider { initial_palette: theme() }
            div {
                button {
                    onclick: move |_| theme.set("hikari".to_string()),
                    class: if theme() == "hikari" { "active" } else { "" },
                    "Light"
                }
                button {
                    onclick: move |_| theme.set("tairitsu".to_string()),
                    class: if theme() == "tairitsu" { "active" } else { "" },
                    "Dark"
                }
            }
        }
    }
}

Persistent Theme

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use gloo::storage::LocalStorage;

#[component]
fn PersistentTheme() -> Element {
    // Load theme from LocalStorage
    let mut theme = use_signal(|| {
        LocalStorage::get("theme")
            .unwrap_or_else(|_| Ok("hikari".to_string()))
            .unwrap_or("hikari".to_string())
    });

    // Save theme to LocalStorage when it changes
    use_effect(move || {
        let theme = theme();
        async move {
            if let Err(e) = LocalStorage::set("theme", &theme) {
                eprintln!("Failed to save theme: {}", e);
            }
        }
    });

    rsx! {
        ThemeProvider { initial_palette: theme() }
            // Application content
        }
    }
}

System Theme Detection

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
use web_sys::window;

#[component]
fn SystemTheme() -> Element {
    let mut theme = use_signal(|| "hikari".to_string());

    // Detect system theme preference
    use_effect(|| {
        let win = window()?;
        let media_query = win.match_media("(prefers-color-scheme: dark)")?;

        let listener = Closure::wrap(Box::new(move |e: Event| {
            let matches = e
                .dyn_ref::<MediaQueryListEvent>()
                .unwrap()
                .matches();
            theme.set(if matches {
                "tairitsu".to_string()
            } else {
                "hikari".to_string()
            });
        }) as Box<dyn Fn(_)>);

        media_query
            .add_listener_with_opt_callback(Some(listener.as_ref().unchecked_ref()))
            .unwrap();
        listener.forget();

        async move {}
    });

    rsx! {
        ThemeProvider { initial_palette: theme() }
            // Application content
        }
    }
}

Style Customization

Theme Variable Override

css
1
2
3
4
5
/* Override theme variables in global styles */
[data-theme="hikari"] {
    --hi-color-primary: #0066CC;
    --hi-color-secondary: #FF6600;
}

Component-level Theme

rust
1
2
3
4
5
6
7
8
rsx! {
    // Use different theme for specific component
    div {
        "data-theme": "tairitsu",
        style: "background: var(--hi-color-surface);",
        "This component uses dark theme"
    }
}

Custom Theme Variables

css
1
2
3
4
5
6
[data-theme="custom"] {
    --hi-color-primary: #FF0066;
    --hi-color-secondary: #00FF99;
    --hi-color-accent: #FFFF00;
    /* ... other variables */
}

Best Practices

1. Theme Provider Placement

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Good: Place ThemeProvider at application root
#[component]
fn App() -> Element {
    rsx! {
        ThemeProvider { initial_palette: "hikari".to_string() }
            Router {}
        }
    }
}

// Avoid: Nesting multiple ThemeProviders
#[component]
fn BadExample() -> Element {
    rsx! {
        ThemeProvider { initial_palette: "hikari".to_string() }
            ThemeProvider { initial_palette: "tairitsu".to_string() }
                // Inner theme will override outer
            }
        }
    }
}

2. Theme Switching Animation

css
1
2
3
4
5
6
/* Add smooth theme switching transition */
* {
    transition: background-color 0.3s ease,
                color 0.3s ease,
                border-color 0.3s ease;
}

3. Conditional Styling

rust
1
2
3
4
5
6
7
8
9
10
rsx! {
    div {
        class: if theme() == "hikari" {
            "light-theme"
        } else {
            "dark-theme"
        },
        "Apply different styles based on theme"
    }
}

4. CSS Variable Fallback

css
1
2
3
4
5
/* Provide fallback for browsers that don't support CSS variables */
.my-component {
    background-color: #FFB3A7; /* Fallback value */
    background-color: var(--hi-color-primary, #FFB3A7);
}

API Reference

ThemeProvider

rust
1
2
3
4
5
#[component]
pub fn ThemeProvider(
    palette: String,
    children: Element
) -> Element

ThemeContext

rust
1
2
3
4
5
6
7
8
pub struct ThemeContext {
    pub palette: String,
    pub colors: Palette,
}

impl Default for ThemeContext {
    fn default() -> Self { ... }
}

Integration with Other Systems

Integration with Palette

rust
1
2
3
4
use hikari_palette::{Color, themes};

let hikari_palette = themes::Hikari::palette();
println!("Primary: {}", hikari_palette.primary.hex());

Integration with Animation

rust
1
2
3
4
5
6
7
use hikari_animation::AnimationBuilder;
use hikari_theme::ThemeProvider;

// Theme variables can be used in animations
AnimationBuilder::new(&elements)
    .add_style("button", "background-color", "var(--hi-color-primary)")
    .apply_with_transition("300ms", "ease-in-out");

Integration with Components

All components automatically inherit the theme provided by ThemeProvider:

rust
1
2
3
4
5
6
7
8
rsx! {
    ThemeProvider { initial_palette: "hikari".to_string() }
        // All components automatically use hikari theme
        Button { label: "Button" }
        Card { "Card" }
        Input { placeholder: "Input" }
    }
}

Design Philosophy

Style

Elements

Responsive

Related Systems