//! Tri-state color theme: auto (follow the OS), light, dark. //! //! The choice is persisted in localStorage and applied as a class on //! ``; `app.css` resolves the palette variables accordingly. //! "Auto" means no class at all, so the stylesheet falls back to //! `prefers-color-scheme`. use crate::icons::IconName; const STORAGE_KEY: &str = "dovenest.theme"; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Theme { /// Follow the OS `prefers-color-scheme` (the default). Auto, Light, Dark, } impl Theme { /// The i18n key of this state's display name. pub const fn name_key(self) -> &'static str { match self { Theme::Auto => crate::i18n::k::THEME_AUTO, Theme::Light => crate::i18n::k::THEME_LIGHT, Theme::Dark => crate::i18n::k::THEME_DARK, } } /// The next state in the toggle cycle: auto → light → dark → auto. pub const fn next(self) -> Theme { match self { Theme::Auto => Theme::Light, Theme::Light => Theme::Dark, Theme::Dark => Theme::Auto, } } /// Lowercase name, used in tooltips and as the localStorage value. pub const fn label(self) -> &'static str { match self { Theme::Auto => "auto", Theme::Light => "light", Theme::Dark => "dark", } } /// The glyph the toggle button shows for this state. pub const fn icon(self) -> IconName { match self { Theme::Auto => IconName::ThemeAuto, Theme::Light => IconName::ThemeLight, Theme::Dark => IconName::ThemeDark, } } /// Persist the choice. pub fn store(self) { crate::util::storage_set(STORAGE_KEY, self.label()); } /// The persisted choice; anything missing or invalid is /// [`Theme::Auto`]. pub fn load() -> Theme { match crate::util::storage_get(STORAGE_KEY).as_deref() { Some("light") => Theme::Light, Some("dark") => Theme::Dark, _ => Theme::Auto, } } /// Apply to ``: auto clears the class (the stylesheet falls back /// to `prefers-color-scheme`), light/dark force a palette. pub fn apply(self) { let Some(html) = web_sys::window() .and_then(|w| w.document()) .and_then(|d| d.document_element()) else { return; }; html.set_class_name(match self { Theme::Auto => "", t => t.label(), }); } }