diff --git a/gallery/src/components/demo-black-white-row.ts b/gallery/src/components/demo-black-white-row.ts index a3b1233ffb..92a4beeffe 100644 --- a/gallery/src/components/demo-black-white-row.ts +++ b/gallery/src/components/demo-black-white-row.ts @@ -52,17 +52,13 @@ class DemoBlackWhiteRow extends LitElement { firstUpdated(changedProps) { super.firstUpdated(changedProps); - applyThemesOnElement( - this.shadowRoot!.querySelector(".dark"), - { - default_theme: "default", - default_dark_theme: "default", - themes: {}, - darkMode: false, - }, - "default", - { dark: true } - ); + applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), { + default_theme: "default", + default_dark_theme: "default", + themes: {}, + darkMode: true, + theme: "default", + }); } handleSubmit(ev) { diff --git a/gallery/src/demos/demo-ha-alert.ts b/gallery/src/demos/demo-ha-alert.ts index 91286a1045..3a62c4a1d2 100644 --- a/gallery/src/demos/demo-ha-alert.ts +++ b/gallery/src/demos/demo-ha-alert.ts @@ -159,17 +159,13 @@ export class DemoHaAlert extends LitElement { firstUpdated(changedProps) { super.firstUpdated(changedProps); - applyThemesOnElement( - this.shadowRoot!.querySelector(".dark"), - { - default_theme: "default", - default_dark_theme: "default", - themes: {}, - darkMode: false, - }, - "default", - { dark: true } - ); + applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), { + default_theme: "default", + default_dark_theme: "default", + themes: {}, + darkMode: true, + theme: "default", + }); } static get styles() { diff --git a/src/auth/ha-authorize.ts b/src/auth/ha-authorize.ts index acd98952c9..8f91bfe8bf 100644 --- a/src/auth/ha-authorize.ts +++ b/src/auth/ha-authorize.ts @@ -101,17 +101,13 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) { this._fetchAuthProviders(); if (matchMedia("(prefers-color-scheme: dark)").matches) { - applyThemesOnElement( - document.documentElement, - { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: false, - }, - "default", - { dark: true } - ); + applyThemesOnElement(document.documentElement, { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }); } if (!this.redirectUri) { diff --git a/src/common/dom/apply_themes_on_element.ts b/src/common/dom/apply_themes_on_element.ts index fca6e2262a..dd5aebc423 100644 --- a/src/common/dom/apply_themes_on_element.ts +++ b/src/common/dom/apply_themes_on_element.ts @@ -23,9 +23,9 @@ let PROCESSED_THEMES: Record = {}; * Apply a theme to an element by setting the CSS variables on it. * * element: Element to apply theme on. - * themes: HASS theme information. - * selectedTheme: Selected theme. - * themeSettings: Settings such as selected dark mode and colors. + * themes: HASS theme information (e.g. active dark mode and globally active theme name). + * selectedTheme: Selected theme (used to override the globally active theme for this element). + * themeSettings: Additional settings such as selected colors. */ export const applyThemesOnElement = ( element, @@ -33,31 +33,33 @@ export const applyThemesOnElement = ( selectedTheme?: string, themeSettings?: Partial ) => { - let cacheKey = selectedTheme; - let themeRules: Partial = {}; + // If there is no explicitly desired theme provided, we automatically + // use the active one from `themes`. + const themeToApply = selectedTheme || themes.theme; // If there is no explicitly desired dark mode provided, we automatically - // use the active one from hass.themes. - if (!themeSettings || themeSettings?.dark === undefined) { - themeSettings = { - ...themeSettings, - dark: themes.darkMode, - }; - } + // use the active one from `themes`. + const darkMode = + themeSettings && themeSettings?.dark !== undefined + ? themeSettings?.dark + : themes.darkMode; - if (themeSettings.dark) { + let cacheKey = themeToApply; + let themeRules: Partial = {}; + + if (darkMode) { cacheKey = `${cacheKey}__dark`; themeRules = { ...darkStyles }; } - if (selectedTheme === "default") { + if (themeToApply === "default") { // Determine the primary and accent colors from the current settings. // Fallbacks are implicitly the HA default blue and orange or the // derived "darkStyles" values, depending on the light vs dark mode. - const primaryColor = themeSettings.primaryColor; - const accentColor = themeSettings.accentColor; + const primaryColor = themeSettings?.primaryColor; + const accentColor = themeSettings?.accentColor; - if (themeSettings.dark && primaryColor) { + if (darkMode && primaryColor) { themeRules["app-header-background-color"] = hexBlend( primaryColor, "#121212", @@ -98,17 +100,17 @@ export const applyThemesOnElement = ( // Custom theme logic (not relevant for default theme, since it would override // the derived calculations from above) if ( - selectedTheme && - selectedTheme !== "default" && - themes.themes[selectedTheme] + themeToApply && + themeToApply !== "default" && + themes.themes[themeToApply] ) { // Apply theme vars that are relevant for all modes (but extract the "modes" section first) - const { modes, ...baseThemeRules } = themes.themes[selectedTheme]; + const { modes, ...baseThemeRules } = themes.themes[themeToApply]; themeRules = { ...themeRules, ...baseThemeRules }; // Apply theme vars for the specific mode if available if (modes) { - if (themeSettings?.dark) { + if (darkMode) { themeRules = { ...themeRules, ...modes.dark }; } else { themeRules = { ...themeRules, ...modes.light }; diff --git a/src/data/ws-themes.ts b/src/data/ws-themes.ts index 99a44064f9..619066e8a9 100644 --- a/src/data/ws-themes.ts +++ b/src/data/ws-themes.ts @@ -23,6 +23,8 @@ export interface Themes { // in theme picker, this property will still contain either true or false based on // what has been determined via system preferences and support from the selected theme. darkMode: boolean; + // Currently globally active theme name + theme: string; } const fetchThemes = (conn) => diff --git a/src/fake_data/provide_hass.ts b/src/fake_data/provide_hass.ts index 1be324de95..0f764be982 100644 --- a/src/fake_data/provide_hass.ts +++ b/src/fake_data/provide_hass.ts @@ -201,6 +201,7 @@ export const provideHass = ( default_dark_theme: null, themes: {}, darkMode: false, + theme: "default", }, panels: demoPanels, services: demoServices, diff --git a/src/onboarding/ha-onboarding.ts b/src/onboarding/ha-onboarding.ts index 7095e8a887..eb01f05877 100644 --- a/src/onboarding/ha-onboarding.ts +++ b/src/onboarding/ha-onboarding.ts @@ -133,17 +133,13 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) { import("./particles"); } if (matchMedia("(prefers-color-scheme: dark)").matches) { - applyThemesOnElement( - document.documentElement, - { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: false, - }, - "default", - { dark: true } - ); + applyThemesOnElement(document.documentElement, { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }); } } diff --git a/src/state/themes-mixin.ts b/src/state/themes-mixin.ts index 9aa5a0ac2c..bb5c443a62 100644 --- a/src/state/themes-mixin.ts +++ b/src/state/themes-mixin.ts @@ -38,17 +38,13 @@ export default >(superClass: T) => }); mql.addListener((ev) => this._applyTheme(ev.matches)); if (!this._themeApplied && mql.matches) { - applyThemesOnElement( - document.documentElement, - { - default_theme: "default", - default_dark_theme: null, - themes: {}, - darkMode: false, - }, - "default", - { dark: true } - ); + applyThemesOnElement(document.documentElement, { + default_theme: "default", + default_dark_theme: null, + themes: {}, + darkMode: true, + theme: "default", + }); } } @@ -89,6 +85,9 @@ export default >(superClass: T) => } themeSettings = { ...this.hass.selectedTheme, dark: darkMode }; + this._updateHass({ + themes: { ...this.hass.themes!, theme: themeName }, + }); applyThemesOnElement( document.documentElement, diff --git a/src/types.ts b/src/types.ts index 539dcb610d..b6bab9a982 100644 --- a/src/types.ts +++ b/src/types.ts @@ -84,9 +84,12 @@ export interface CurrentUser { } // Currently selected theme and its settings. These are the values stored in local storage. +// Note: These values are not meant to be used at runtime to check whether dark mode is active +// or which theme name to use, as this interface represents the config data for the theme picker. +// The actually active dark mode and theme name can be read from hass.themes. export interface ThemeSettings { theme: string; - // Radio box selection for theme picker. Do not use in cards as + // Radio box selection for theme picker. Do not use in Lovelace rendering as // it can be undefined == auto. // Property hass.themes.darkMode carries effective current mode. dark?: boolean;