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 8576f161ae)
This commit is contained in:
Kam
2026-09-06 13:09:25 +03:00
committed by Kristiyan Kostadinov
parent 023d172dcc
commit da8dac62a7
2 changed files with 15 additions and 6 deletions
@@ -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<Theme | null>(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());
});
}
}
@@ -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);