diff --git a/apps/web/lib/__tests__/getThemeProviderProps.test.ts b/apps/web/lib/__tests__/getThemeProviderProps.test.ts index 9b9dedca38..0f7432421a 100644 --- a/apps/web/lib/__tests__/getThemeProviderProps.test.ts +++ b/apps/web/lib/__tests__/getThemeProviderProps.test.ts @@ -52,8 +52,8 @@ describe("getThemeProviderProps", () => { expect(result).toEqual({ ...appThemeExpectedProps, - storageKey: undefined, - key: undefined, + storageKey: "forcedThemeKey", + key: "forcedThemeKey", forcedTheme: "light", enableSystem: false, }); diff --git a/apps/web/lib/getThemeProviderProps.ts b/apps/web/lib/getThemeProviderProps.ts index 1d032a576c..61d4a4ef81 100644 --- a/apps/web/lib/getThemeProviderProps.ts +++ b/apps/web/lib/getThemeProviderProps.ts @@ -1,4 +1,5 @@ import type { ReadonlyURLSearchParams } from "next/navigation"; +import { z } from "zod"; import { EmbedTheme } from "@calcom/features/embed/lib/constants"; @@ -92,13 +93,28 @@ export function getThemeProviderProps({ searchParams: ReadonlyURLSearchParams | null; }) { const isBookingPage = props.isBookingPage; - const themeSupport = isBookingPage ? ThemeSupport.Booking : props.isThemeSupported === false ? ThemeSupport.None : ThemeSupport.App; + const themeQueryParam = searchParams?.get("theme") ?? ""; + const themeParsed = z.enum(["light", "dark", "system", "auto"]).safeParse(themeQueryParam); + const isWrongThemeValue = themeQueryParam.length > 0 && !themeParsed.success; + const forcedTheme = themeSupport === ThemeSupport.None || isWrongThemeValue ? "light" : undefined; + if (forcedTheme) { + return { + key: "forcedThemeKey", + storageKey: "forcedThemeKey", + forcedTheme, + attribute: "class", + nonce: props.nonce, + enableColorScheme: false, + enableSystem: themeSupport !== ThemeSupport.None, + }; + } + const isBookingPageThemeSupportRequired = themeSupport === ThemeSupport.Booking; const themeBasis = pathname ? getUniqueIdentifierForBookingPage({ pathname }) : null; const isThemeBasisRequired = isBookingPageThemeSupportRequired || isEmbedMode; @@ -109,8 +125,6 @@ export function getThemeProviderProps({ } const appearanceIdSuffix = themeBasis ? `:${themeBasis}` : ""; - const forcedTheme = themeSupport === ThemeSupport.None ? "light" : undefined; - const themeQueryParam = searchParams?.get("theme"); const embedExplicitlySetThemeSuffix = isEmbedMode && themeQueryParam && themeQueryParam !== EmbedTheme.auto ? `:${themeQueryParam}` : ""; @@ -126,8 +140,6 @@ export function getThemeProviderProps({ const themeProviderProps = { storageKey, - // Can force a theme through this, no matter what system preference is or storage value is - forcedTheme, nonce: props.nonce, enableColorScheme: false, // Enables theme switching based on system preference if true