Merge pull request #3773 from bluewave-labs/fix/status-page-brand-color-not-applied

fix(status page): apply brand color via a dedicated brand token
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-07-14 09:04:06 -07:00
commit 2331035da0
4 files changed
+54 -9

No files matched your search

@@ -142,6 +142,7 @@ const StatusPageView = () => {
theme={statusPage.theme}
themeMode={statusPage.themeMode}
timezone={statusPage.timezone}
brandColor={statusPage.color}
paintBody
>
{customCss && <style>{customCss}</style>}
@@ -6,6 +6,7 @@ import {
useState,
type ReactNode,
} from "react";
import { alpha, darken } from "@mui/material/styles";
import {
DEFAULT_STATUS_PAGE_THEME,
resolveStatusPageTheme,
@@ -36,6 +37,15 @@ interface Props {
theme?: StatusPageTheme;
themeMode?: StatusPageThemeMode;
timezone?: string;
/**
* Brand color picked in the status-page appearance settings. When
* provided, it feeds `tokens.brand` / `brandStrong` / `brandSoft`,
* which theme styles use for accent surfaces (hero, active tabs,
* links). It does NOT recolor the semantic `up`/OK status color, so
* a red or orange brand cannot make the "all operational" indicator
* signal downtime.
*/
brandColor?: string;
/**
* When true, paint the document body with the theme background so it
* covers beyond the provider's wrapper (useful on the public route
@@ -54,10 +64,32 @@ interface Props {
children: ReactNode;
}
const HEX_COLOR = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
// Resolve the brand triple that theme styles read from. A customer's
// picked color (when valid) overrides the theme's default; otherwise
// the theme's own `brand*` values win, and finally we fall back to the
// `up*` triple so unmigrated theme styles have something to consume.
const resolveBrandTokens = (
tokens: StatusPageThemeTokens,
brandColor: string | undefined
): StatusPageThemeTokens => {
const validOverride = brandColor && HEX_COLOR.test(brandColor) ? brandColor : undefined;
const brand = validOverride ?? tokens.brand ?? tokens.up;
const brandStrong = validOverride
? darken(validOverride, 0.2)
: (tokens.brandStrong ?? tokens.upStrong);
const brandSoft = validOverride
? alpha(validOverride, 0.15)
: (tokens.brandSoft ?? tokens.upSoft);
return { ...tokens, brand, brandStrong, brandSoft };
};
export const StatusPageThemeProvider = ({
theme,
themeMode,
timezone,
brandColor,
paintBody = false,
transparent = false,
children,
@@ -109,7 +141,11 @@ export const StatusPageThemeProvider = ({
const resolvedMode: ResolvedMode =
resolvedThemeMode === "auto" ? systemMode : resolvedThemeMode;
const tokens = themeTokens[resolvedTheme][resolvedMode];
const baseTokens = themeTokens[resolvedTheme][resolvedMode];
const tokens = useMemo(
() => resolveBrandTokens(baseTokens, brandColor),
[baseTokens, brandColor]
);
const value = useMemo(
() => ({
@@ -119,7 +119,7 @@ export const refinedStyles = (
width: 28,
height: 28,
borderRadius: "8px",
background: tokens.up,
background: tokens.brand,
display: "grid",
placeItems: "center",
color: "#fff",
@@ -191,16 +191,16 @@ export const refinedStyles = (
whiteSpace: "nowrap",
textAlign: "center",
border: 0,
background: active ? tokens.upSoft : "transparent",
background: active ? tokens.brandSoft : "transparent",
fontFamily: "inherit",
fontSize: 11,
padding: { xs: "5px 6px", md: "5px 14px" },
cursor: "pointer",
color: active ? tokens.up : tokens.textMuted,
color: active ? tokens.brand : tokens.textMuted,
borderRadius: "5px",
transition: "background 0.15s ease, color 0.15s ease",
fontWeight: active ? 600 : 500,
"&:hover": { color: active ? tokens.up : tokens.text },
"&:hover": { color: active ? tokens.brand : tokens.text },
}),
monitorList: {
@@ -259,9 +259,9 @@ export const refinedStyles = (
},
pillHardware: {
...pillBase,
color: tokens.up,
color: tokens.brand,
border: `1px solid ${tokens.border}`,
background: tokens.upSoft,
background: tokens.brandSoft,
},
monitorUrl: {
fontSize: 12,
@@ -382,11 +382,11 @@ export const refinedStyles = (
fontSize: 12,
mt: "40px",
"& a": {
color: tokens.up,
color: tokens.brand,
textDecoration: "underline",
textUnderlineOffset: "3px",
fontWeight: 600,
"&:hover": { color: tokens.upStrong || tokens.up },
"&:hover": { color: tokens.brandStrong },
},
},
};
@@ -9,6 +9,14 @@ export interface StatusPageThemeTokens {
up: string;
upStrong: string;
upSoft: string;
// Accent used for brand-tinted surfaces (hero background, active tab
// underline, "view all" links). Distinct from `up` so a red/orange
// brand color cannot recolor the semantic OK/up status indicators.
// Themes leave these undefined by default; the provider falls back
// to `up`/`upStrong`/`upSoft` so styles have a value to consume.
brand?: string;
brandStrong?: string;
brandSoft?: string;
degraded: string;
degradedSoft: string;
down: string;