From bd8d35bf3f0b24bb13288016d074774f49ef3334 Mon Sep 17 00:00:00 2001 From: "Jens L." Date: Thu, 1 Aug 2024 15:08:09 +0200 Subject: [PATCH] web: fix theme not applying to document correctly (#10721) Signed-off-by: Jens Langhammer --- web/src/elements/Base.ts | 29 +++++++++++++++++-------- web/src/elements/Interface/Interface.ts | 10 ++++++--- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/web/src/elements/Base.ts b/web/src/elements/Base.ts index 3f6fd2b899..fa8d87a5f7 100644 --- a/web/src/elements/Base.ts +++ b/web/src/elements/Base.ts @@ -126,7 +126,7 @@ export class AKElement extends LitElement { ev?.matches || this._mediaMatcher?.matches ? UiThemeEnum.Light : UiThemeEnum.Dark; - this._activateTheme(root, theme); + this._activateTheme(theme, root); }; this._mediaMatcherHandler(undefined); this._mediaMatcher.addEventListener("change", this._mediaMatcherHandler); @@ -138,7 +138,7 @@ export class AKElement extends LitElement { this._mediaMatcher.removeEventListener("change", this._mediaMatcherHandler); this._mediaMatcher = undefined; } - this._activateTheme(root, theme); + this._activateTheme(theme, root); } static themeToStylesheet(theme?: UiThemeEnum): CSSStyleSheet | undefined { @@ -148,7 +148,11 @@ export class AKElement extends LitElement { return undefined; } - _activateTheme(root: DocumentOrShadowRoot, theme: UiThemeEnum) { + /** + * Directly activate a given theme, accepts multiple document/ShadowDOMs to apply the stylesheet + * to. The stylesheets are applied to each DOM in order. Does nothing if the given theme is already active. + */ + _activateTheme(theme: UiThemeEnum, ...roots: DocumentOrShadowRoot[]) { if (theme === this._activeTheme) { return; } @@ -163,12 +167,19 @@ export class AKElement extends LitElement { this.setAttribute("theme", theme); const stylesheet = AKElement.themeToStylesheet(theme); const oldStylesheet = AKElement.themeToStylesheet(this._activeTheme); - if (stylesheet) { - root.adoptedStyleSheets = [...root.adoptedStyleSheets, ensureCSSStyleSheet(stylesheet)]; - } - if (oldStylesheet) { - root.adoptedStyleSheets = root.adoptedStyleSheets.filter((v) => v !== oldStylesheet); - } + roots.forEach((root) => { + if (stylesheet) { + root.adoptedStyleSheets = [ + ...root.adoptedStyleSheets, + ensureCSSStyleSheet(stylesheet), + ]; + } + if (oldStylesheet) { + root.adoptedStyleSheets = root.adoptedStyleSheets.filter( + (v) => v !== oldStylesheet, + ); + } + }); this._activeTheme = theme; this.requestUpdate(); } diff --git a/web/src/elements/Interface/Interface.ts b/web/src/elements/Interface/Interface.ts index d7fc811f7e..29aa053d8f 100644 --- a/web/src/elements/Interface/Interface.ts +++ b/web/src/elements/Interface/Interface.ts @@ -50,15 +50,19 @@ export class Interface extends AKElement implements AkInterface { this.dataset.akInterfaceRoot = "true"; } - _activateTheme(root: DocumentOrShadowRoot, theme: UiThemeEnum): void { + _activateTheme(theme: UiThemeEnum, ...roots: DocumentOrShadowRoot[]): void { if (theme === this._activeTheme) { return; } console.debug( `authentik/interface[${rootInterface()?.tagName.toLowerCase()}]: Enabling theme ${theme}`, ); - super._activateTheme(document as unknown as DocumentOrShadowRoot, theme); - super._activateTheme(root, theme); + // Special case for root interfaces, as they need to modify the global document CSS too + // Instead of calling ._activateTheme() twice, we insert the root document in the call + // since multiple calls to ._activateTheme() would not do anything after the first call + // as the theme is already enabled. + roots.unshift(document as unknown as DocumentOrShadowRoot); + super._activateTheme(theme, ...roots); } async getTheme(): Promise {