From 005fe5cd3831503193e7e8e9ca3ccfe03eca3835 Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Mon, 7 Sep 2026 23:46:18 -0700 Subject: [PATCH] theme: apply branded colors as soon as they are known The branded theme provider dropped every color override while the branding fetch was in flight, so each page load painted the default palette (pink spinner, pink ring) until the fetch returned, even when the injected meta or the cache already carried the node's colors. Overrides now apply whenever a value is known and stay absent only while it isn't. Claude-Session: https://claude.ai/code/session_014aPQ5yqG9QFxKnbwQfCYKa (cherry picked from commit 1f29343e70237779825d2bb87d1a87e2361aba5f) --- .../src/lib/theme/branded-theme-provider.tsx | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/js/components/src/lib/theme/branded-theme-provider.tsx b/js/components/src/lib/theme/branded-theme-provider.tsx index 6142ce09c..8029f7d17 100644 --- a/js/components/src/lib/theme/branded-theme-provider.tsx +++ b/js/components/src/lib/theme/branded-theme-provider.tsx @@ -1,10 +1,5 @@ import { useMemo, type ReactNode } from "react"; -import { - useAccentColor, - useBrandingAsset, - usePrimaryColor, - useStreamplaceStore, -} from "../../streamplace-store"; +import { useBrandingAsset } from "../../streamplace-store"; import { ThemeProvider, type BrandColors, type Theme } from "./theme"; interface BrandedThemeProviderProps { @@ -22,9 +17,13 @@ export function BrandedThemeProvider({ defaultTheme, forcedTheme, }: BrandedThemeProviderProps) { - const primaryColor = usePrimaryColor(); - const accentColor = useAccentColor(); - const brandingLoading = useStreamplaceStore((state) => state.brandingLoading); + // Raw values, undefined until the node's branding is known (injected + // meta, the cache, or the fetch). Overrides apply as soon as a value is + // known rather than waiting for the fetch to finish: gating on the + // fetch flashed the default palette on every load even when the colors + // were already at hand. + const primaryColor = useBrandingAsset("primaryColor")?.data || undefined; + const accentColor = useBrandingAsset("accentColor")?.data || undefined; // Chrome: the node's background/foreground pair per scheme, from which the // theme derives surfaces, text and borders. Empty values leave defaults. @@ -42,11 +41,6 @@ export function BrandedThemeProvider({ // Build color theme overrides from branding const colorTheme = useMemo>(() => { - // don't override until branding is loaded - if (brandingLoading) { - return {}; - } - const overrides: Partial = {}; if (primaryColor) { @@ -59,7 +53,7 @@ export function BrandedThemeProvider({ } return overrides; - }, [primaryColor, accentColor, brandingLoading]); + }, [primaryColor, accentColor]); // Accent doubles as the secondary color (the only accent-ish token the // app actually renders); status and live colors are their own keys. @@ -74,7 +68,6 @@ export function BrandedThemeProvider({ const warningLight = useBrandingAsset("warningColorLight")?.data; const infoLight = useBrandingAsset("infoColorLight")?.data; const brandColors = useMemo(() => { - if (brandingLoading) return undefined; return { secondary: accentColor, danger: dangerColor, @@ -89,7 +82,6 @@ export function BrandedThemeProvider({ infoLight, }; }, [ - brandingLoading, accentColor, dangerColor, successColor, -- 2.51.2