From da8dac62a79025fa42ae3ee5c64e3e3f1979ce54 Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 6 Sep 2026 13:09:25 +0300 Subject: [PATCH] fix(docs-infra): highlight home page code for the resolved theme `CodeBlock` picked `github-light` only when the theme was exactly `light`, but `Theme` also has `auto`, the value for anyone who has not opened the theme menu, and `theme()` is `null` during prerendering. Both fell to `github-dark`, so the samples in the Signals, Control Flow and Deferrable Views tabs on https://angular.dev rendered on a dark slab inside a light page. Resolve the theme in `ThemeManager`, which already owns the `auto` translation. `resolvedTheme` returns `light` when `theme()` is `null`, which happens only during prerendering, so `window` is never read. The device scheme moves into a signal so `auto` also reacts to OS scheme changes. (cherry picked from commit 8576f161ae45f810a49be6b2c4c666ccf5f5d3c4) --- .../core/services/theme-manager.service.ts | 19 ++++++++++++++----- .../home/components/code-block/code-block.ts | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/adev/src/app/core/services/theme-manager.service.ts b/adev/src/app/core/services/theme-manager.service.ts index 5458086fb87..3e6d1ead0d6 100644 --- a/adev/src/app/core/services/theme-manager.service.ts +++ b/adev/src/app/core/services/theme-manager.service.ts @@ -7,7 +7,7 @@ */ import {DOCUMENT, isPlatformBrowser} from '@angular/common'; -import {PLATFORM_ID, Service, inject, signal} from '@angular/core'; +import {PLATFORM_ID, Service, computed, inject, signal} from '@angular/core'; import {LOCAL_STORAGE} from '@angular/docs'; // Keep these constants in sync with the code in index.html @@ -26,12 +26,21 @@ export class ThemeManager { private readonly platformId = inject(PLATFORM_ID); readonly theme = signal(this.getThemeFromLocalStorageValue()); + private readonly osScheme = signal<'dark' | 'light'>('light'); + readonly resolvedTheme = computed<'dark' | 'light'>(() => { + const theme = this.theme(); + if (theme === null) { + return 'light'; + } + return theme === 'auto' ? this.osScheme() : theme; + }); constructor() { if (!isPlatformBrowser(this.platformId)) { return; } + this.osScheme.set(preferredScheme()); this.loadThemePreference(); this.watchPreferredColorScheme(); } @@ -39,7 +48,7 @@ export class ThemeManager { setTheme(theme: Theme): void { this.theme.set(theme); this.setThemeInLocalStorage(); - this.setThemeBodyClasses(theme === 'auto' ? preferredScheme() : theme); + this.setThemeBodyClasses(this.resolvedTheme()); } // 1. Read theme preferences stored in localStorage @@ -49,7 +58,7 @@ export class ThemeManager { const useTheme = savedUserPreference ?? 'auto'; this.theme.set(useTheme); - this.setThemeBodyClasses(useTheme === 'auto' ? preferredScheme() : useTheme); + this.setThemeBodyClasses(this.resolvedTheme()); } // Set theme classes on the body element @@ -77,11 +86,11 @@ export class ThemeManager { private watchPreferredColorScheme() { window.matchMedia(PREFERS_COLOR_SCHEME_DARK).addEventListener('change', (event) => { + this.osScheme.set(event.matches ? 'dark' : 'light'); if (this.theme() !== 'auto') { return; } - const preferredScheme = event.matches ? 'dark' : 'light'; - this.setThemeBodyClasses(preferredScheme); + this.setThemeBodyClasses(this.resolvedTheme()); }); } } diff --git a/adev/src/app/features/home/components/code-block/code-block.ts b/adev/src/app/features/home/components/code-block/code-block.ts index d9fd263eb06..d79e13a5cfc 100644 --- a/adev/src/app/features/home/components/code-block/code-block.ts +++ b/adev/src/app/features/home/components/code-block/code-block.ts @@ -34,7 +34,7 @@ export class CodeBlock { .codeToHtml(this.code(), { cssVariablePrefix: '--shiki-', lang: this.language(), - theme: this.theme.theme() === 'light' ? 'github-light' : 'github-dark', + theme: this.theme.resolvedTheme() === 'dark' ? 'github-dark' : 'github-light', }) .then((hightlightedHtml) => { return this.sanitizer.bypassSecurityTrustHtml(hightlightedHtml);