diff --git a/webapp/src/app/core/theme/theme-switcher.service.ts b/webapp/src/app/core/theme/theme-switcher.service.ts index 089f5d50..e07a1abe 100644 --- a/webapp/src/app/core/theme/theme-switcher.service.ts +++ b/webapp/src/app/core/theme/theme-switcher.service.ts @@ -5,71 +5,78 @@ export enum AppTheme { DARK = 'dark' } -const IS_CLIENT_RENDER = typeof localStorage !== 'undefined'; const LOCAL_STORAGE_THEME_KEY = 'theme'; -let selectedTheme: AppTheme | undefined = undefined; - -if (IS_CLIENT_RENDER) { - selectedTheme = (localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as AppTheme) || undefined; -} - @Injectable({ providedIn: 'root' }) export class ThemeSwitcherService { - currentTheme = signal(selectedTheme); + private htmlElement = document.documentElement; + private metaThemeColor = document.querySelector('meta[name="theme-color"]'); + + currentTheme = signal(this.getInitialTheme()); + + constructor() { + if (this.currentTheme() === 'auto') { + this.applySystemTheme(); + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', this.handleSystemThemeChange); + } + } setLightTheme() { - this.currentTheme.set(AppTheme.LIGHT); - this.setToLocalStorage(AppTheme.LIGHT); - this.removeClassFromHtml('dark'); - document.documentElement.setAttribute('data-color-mode', 'light'); + this.applyTheme(AppTheme.LIGHT); } setDarkTheme() { - this.currentTheme.set(AppTheme.DARK); - this.setToLocalStorage(AppTheme.DARK); - this.addClassToHtml('dark'); - document.documentElement.setAttribute('data-color-mode', 'dark'); + this.applyTheme(AppTheme.DARK); } setSystemTheme() { - this.currentTheme.set(undefined); - this.removeFromLocalStorage(); + this.currentTheme.set('auto'); + localStorage.removeItem(LOCAL_STORAGE_THEME_KEY); + this.applySystemTheme(); + this.updateMetaThemeColor(); + } - const isSystemDark = window?.matchMedia('(prefers-color-scheme: dark)').matches ?? false; - if (isSystemDark) { - this.addClassToHtml('dark'); - } else { - this.removeClassFromHtml('dark'); - } + private applyTheme(theme: AppTheme) { + this.currentTheme.set(theme); + localStorage.setItem(LOCAL_STORAGE_THEME_KEY, theme); + this.htmlElement.classList.toggle(AppTheme.DARK, theme === AppTheme.DARK); + this.htmlElement.setAttribute('data-color-mode', theme); + this.updateMetaThemeColor(); + } - document.documentElement.setAttribute('data-color-mode', 'auto'); + private applySystemTheme() { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + this.htmlElement.classList.toggle(AppTheme.DARK, isDark); + this.htmlElement.setAttribute('data-color-mode', 'auto'); + this.updateMetaThemeColor(); } - private addClassToHtml(className: string) { - if (IS_CLIENT_RENDER) { - this.removeClassFromHtml(className); - document.documentElement.classList.add(className); + private handleSystemThemeChange = (event: MediaQueryListEvent) => { + if (this.currentTheme() === 'auto') { + this.htmlElement.classList.toggle(AppTheme.DARK, event.matches); + this.updateMetaThemeColor(); } - } + }; - private removeClassFromHtml(className: string) { - if (IS_CLIENT_RENDER) { - document.documentElement.classList.remove(className); + private updateMetaThemeColor() { + if (this.metaThemeColor) { + const backgroundColor = getComputedStyle(this.htmlElement).getPropertyValue('--background').trim(); + this.metaThemeColor.setAttribute('content', `hsl(${backgroundColor})`); } } - private setToLocalStorage(theme: AppTheme) { - if (IS_CLIENT_RENDER) { - localStorage.setItem(LOCAL_STORAGE_THEME_KEY, theme); + private getInitialTheme(): AppTheme | 'auto' | undefined { + if (typeof localStorage === 'undefined') { + return 'auto'; } - } - private removeFromLocalStorage() { - if (IS_CLIENT_RENDER) { - localStorage.removeItem(LOCAL_STORAGE_THEME_KEY); + const storedTheme = localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as AppTheme | null; + if (storedTheme === AppTheme.LIGHT || storedTheme === AppTheme.DARK) { + return storedTheme; } + + return 'auto'; } } diff --git a/webapp/src/index.html b/webapp/src/index.html index 7ee67582..7bfba920 100644 --- a/webapp/src/index.html +++ b/webapp/src/index.html @@ -2,6 +2,8 @@ + + Hephaestus @@ -30,6 +32,11 @@ document.documentElement.setAttribute('data-color-mode', browserDark ? 'dark' : 'light'); localStorage.setItem('theme', browserDark ? 'dark' : 'light'); } + + const metaThemeColor = document.querySelector('meta[name="theme-color"]'); + if (metaThemeColor) { + metaThemeColor.setAttribute('content', `hsl(${getComputedStyle(document.documentElement).getPropertyValue('--background')})`); + }