dioxus-compose guide

Theming

Colours as CSS variables

A theme on an HTML screen is what it is on the web: custom properties, a prefers-color-scheme media query, and a class when the user overrides the system. Nothing in this crate replaces that, so a stylesheet written for a browser keeps its themes.

Custom properties

samples/theme writes every colour as a var(). A light set is the default, a dark set applies under prefers-color-scheme: dark, and a class on the app's root wins over both.

samples/theme/src/lib.rs, trimmed
:root, .app.light {
    --bg: #ffffff;
    --surface: #f3f4f6;
    --text: #111827;
    --accent: #2563eb;
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg: #111827;
        --surface: #1f2937;
        --text: #f9fafb;
        --accent: #60a5fa;
    }
}

.app.dark {
    --bg: #111827;
    --surface: #1f2937;
    --text: #f9fafb;
    --accent: #60a5fa;
}

.app { height: 100%; padding: 24px; background-color: var(--bg); color: var(--text); }

Light and dark

HtmlConfig::color_scheme answers prefers-color-scheme. It is ColorScheme::Light unless you say otherwise.

samples/theme/tests/theme.rs, trimmed
let dom = HtmlDom::with_config(
    theme::app,
    HtmlConfig {
        stylesheets: vec![theme::STYLE.to_string()],
        color_scheme: ColorScheme::Dark,
        ..HtmlConfig::default()
    },
);

With no class on the app, Dark picks the dark set. The test checks every colour on the page against both sets.

Overriding the system

The toggle button puts light or dark on the root element. That is a class change and nothing else, so only colours change.

samples/theme/src/lib.rs, trimmed
// None follows the system through the media query.
let mut theme = use_signal(|| None::<Theme>);
let class = match theme.cloned() {
    None => "app",
    Some(Theme::Light) => "app light",
    Some(Theme::Dark) => "app dark",
};

rsx! {
    div { id: "app", class: "{class}",
        button {
            id: "theme-toggle",
            class: "toggle",
            onclick: move |_| {
                let next = match theme.cloned() {
                    Some(Theme::Dark) => Theme::Light,
                    _ => Theme::Dark,
                };
                theme.set(Some(next));
            },
            "Toggle theme"
        }
    }
}

The test clicks the toggle and checks the layout diff: entries changed, none were added or removed, the order is the same, and every changed entry differs from before in its colours only.

Not yet