Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
531a4e6
feat: integrate AppThemeProvider for theme management and update colo…
elizabetdev Jan 15, 2026
a25e0c6
refactor: update components to use theme-based Logo and Icon
elizabetdev Jan 15, 2026
74b4011
fix: update AppNav styles to use brand color scheme
elizabetdev Jan 15, 2026
333354a
refactor: update components to use brand color scheme
elizabetdev Jan 15, 2026
076f061
refactor: streamline component styles and theme integration
elizabetdev Jan 15, 2026
d0ee1b5
Merge branch 'main' into add-themes
elizabetdev Jan 16, 2026
fa2efc9
chore: Add fallback comment for base design tokens in SCSS file
elizabetdev Jan 16, 2026
c6e41ca
fix: Update button styles in Clickstack theme
elizabetdev Jan 16, 2026
5a7c7bc
refactor: Update button variable names and styles in Clickstack and H…
elizabetdev Jan 16, 2026
811dba1
refactor: Simplify font selection logic in MyApp component
elizabetdev Jan 16, 2026
15dab75
feat: Extend Tabs component in Clickstack theme with custom variables
elizabetdev Jan 19, 2026
6436ffe
Merge branch 'main' into add-themes
elizabetdev Jan 20, 2026
9cd1ec8
style: Update sidenav link colors for improved hover and active states
elizabetdev Jan 20, 2026
64382d2
refactor(theme): streamline theme management and enhance developer ex…
elizabetdev Jan 20, 2026
0e867fd
style(theme): update color tokens and button styles for consistency
elizabetdev Jan 20, 2026
a566633
refactor(theme): enhance AppThemeProvider for better hydration handli…
elizabetdev Jan 20, 2026
40420f2
refactor(theme): remove mantineTheme.ts and streamline token management
elizabetdev Jan 20, 2026
67694e8
refactor(theme): enhance user preferences and background handling
elizabetdev Jan 20, 2026
76af981
Merge branch 'main' into add-themes
elizabetdev Jan 20, 2026
0dbc457
style(theme): update active sidenav link color for consistency
elizabetdev Jan 20, 2026
3f952b8
Merge branch 'main' into add-themes
elizabetdev Jan 22, 2026
742fe84
fix: update import path for focusClasses in mantineTheme.ts
elizabetdev Jan 22, 2026
2440ade
fix: update success marker color in PlaybarSlider component
elizabetdev Jan 22, 2026
8468472
feat: add hover state for brand color and extend Slider styles in themes
elizabetdev Jan 22, 2026
076a33b
feat: implement dynamic favicon support and update theme configurations
elizabetdev Jan 22, 2026
6157ee4
refactor: remove background-related preferences and components
elizabetdev Jan 22, 2026
2ecaa8a
refactor: enhance DynamicFavicon component for SSR compatibility
elizabetdev Jan 22, 2026
06802ed
refactor: update user preferences and theme handling
elizabetdev Jan 22, 2026
d044e02
refactor: export safe localStorage helpers and optimize theme class m…
elizabetdev Jan 22, 2026
57e5ac0
Merge branch 'main' into add-themes
elizabetdev Jan 23, 2026
a051bae
fix: update Wordmark font weight for improved styling
elizabetdev Jan 23, 2026
d389fed
Merge branch 'main' into add-themes
elizabetdev Jan 23, 2026
d5be941
feat: Implement user preferences migration from legacy theme to color…
elizabetdev Jan 23, 2026
07c767d
refactor: enhance user preferences handling with type guards and comm…
elizabetdev Jan 23, 2026
9a97270
Add changeset
elizabetdev Jan 26, 2026
4d94f04
Merge branch 'main' into add-themes
elizabetdev Jan 26, 2026
ea766eb
feat: Enhance multi-theme support and user preferences migration
elizabetdev Jan 26, 2026
1946515
test: Update user preferences migration tests to verify theme propert…
elizabetdev Jan 26, 2026
8e66f67
feat: Integrate dynamic theme display in page title and export theme …
elizabetdev Jan 26, 2026
85d6444
Merge branch 'main' into add-themes
elizabetdev Jan 26, 2026
176f268
refactor: Modularize app layout and head content for improved theme h…
elizabetdev Jan 26, 2026
85cdd61
Merge branch 'main' into add-themes
elizabetdev Jan 27, 2026
4dc9354
refactor: Update theme handling and user preferences management
elizabetdev Jan 28, 2026
80ee0d2
feat: Implement theme configuration validation using Zod
elizabetdev Jan 28, 2026
800ac09
feat: Enhance user preferences migration and theme validation
elizabetdev Jan 28, 2026
ee27eba
Merge branch 'main' into add-themes
elizabetdev Jan 29, 2026
bf97066
Merge branch 'main' into add-themes
elizabetdev Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .changeset/light-penguins-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
"@hyperdx/app": minor
---

feat: Multi-theme system with HyperDX and ClickStack branding support

## Major Features

### Multi-Theme System
- Add infrastructure for supporting multiple brand themes (HyperDX & ClickStack)
- Theme switching available in dev/local mode via localStorage
- Production deployments use `NEXT_PUBLIC_THEME` environment variable (deployment-configured)
- Each theme provides its own logos, colors, favicons, and default fonts

### Dynamic Favicons
- Implement theme-aware favicon system with SVG, PNG fallbacks, and Apple Touch Icon
- Add hydration-safe `DynamicFavicon` component
- Include XSS protection for theme-color meta tag validation

### Component Refactoring
- Rename `Icon` → `Logomark` (icon/symbol only)
- Rename `Logo` → `Wordmark` (icon + text branding)
- Each theme provides its own `Logomark` and `Wordmark` components
- Update all component imports across the codebase

### User Preferences Updates
- Rename `theme` property to `colorMode` to clarify light/dark mode vs brand theme
- Remove background overlay feature (backgroundEnabled, backgroundUrl, etc.)
- Add automatic data migration from legacy `theme` → `colorMode` in localStorage
- Ensure existing users don't lose their preferences during migration


### Performance & Type Safety
- Optimize theme CSS class management (single class swap instead of iterating all themes)
- Improve type safety in migration function using destructuring
- Add type guards for runtime validation of localStorage data
127 changes: 81 additions & 46 deletions packages/app/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@ import {
} from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import { DynamicFavicon } from '@/components/DynamicFavicon';
import { IS_LOCAL_MODE } from '@/config';
import {
DEFAULT_FONT_VAR,
DEFAULT_MANTINE_FONT,
FONT_VAR_MAP,
MANTINE_FONT_MAP,
} from '@/config/fonts';
import { ibmPlexMono, inter, roboto, robotoMono } from '@/fonts';
import { AppThemeProvider, useAppTheme } from '@/theme/ThemeProvider';
import { ThemeWrapper } from '@/ThemeWrapper';
import { useConfirmModal } from '@/useConfirm';
import { QueryParamProvider as HDXQueryParamProvider } from '@/useQueryParam';
import { useBackground, useUserPreferences } from '@/useUserPreferences';
import { useUserPreferences } from '@/useUserPreferences';

import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
Expand Down Expand Up @@ -63,13 +64,70 @@ type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// Component that renders Head content requiring user preferences
// Must be rendered inside AppThemeProvider to avoid hydration mismatch
function AppHeadContent() {
const { userPreferences } = useUserPreferences();
const { theme } = useAppTheme();

return (
<Head>
<title>{theme.displayName}</title>
<meta name="viewport" content="width=device-width, initial-scale=0.75" />
<meta name="google" content="notranslate" />
<ColorSchemeScript
forceColorScheme={
userPreferences.colorMode === 'dark' ? 'dark' : 'light'
}
/>
</Head>
);
}

// Component that uses user preferences for theme wrapper
// Must be rendered inside AppThemeProvider to avoid hydration mismatch
function AppContent({
Component,
pageProps,
confirmModal,
}: {
Component: NextPageWithLayout;
pageProps: AppProps['pageProps'];
confirmModal: React.ReactNode;
}) {
const { userPreferences } = useUserPreferences();
const confirmModal = useConfirmModal();
const background = useBackground(userPreferences);

const selectedMantineFont =
MANTINE_FONT_MAP[userPreferences.font] || DEFAULT_MANTINE_FONT;
// Only override font if user has explicitly set a preference.
// Otherwise, return undefined to let the theme use its default font:
// - HyperDX theme: "IBM Plex Sans", monospace
// - ClickStack theme: "Inter", sans-serif
const selectedMantineFont = userPreferences.font
? MANTINE_FONT_MAP[userPreferences.font] || undefined
: undefined;

useEffect(() => {
// Update CSS variable for global font cascading
if (typeof document !== 'undefined') {
const fontVar = FONT_VAR_MAP[userPreferences.font] || DEFAULT_FONT_VAR;
document.documentElement.style.setProperty('--app-font-family', fontVar);
}
}, [userPreferences.font]);

const getLayout = Component.getLayout ?? (page => page);

return (
<ThemeWrapper
fontFamily={selectedMantineFont}
colorScheme={userPreferences.colorMode === 'dark' ? 'dark' : 'light'}
>
{getLayout(<Component {...pageProps} />)}
{confirmModal}
</ThemeWrapper>
);
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const confirmModal = useConfirmModal();

// port to react query ? (needs to wrap with QueryClientProvider)
useEffect(() => {
Expand Down Expand Up @@ -121,47 +179,24 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
}
}, []);

useEffect(() => {
// Update CSS variable for global font cascading
if (typeof document !== 'undefined') {
const fontVar = FONT_VAR_MAP[userPreferences.font] || DEFAULT_FONT_VAR;
document.documentElement.style.setProperty('--app-font-family', fontVar);
}
}, [userPreferences.font]);

const getLayout = Component.getLayout ?? (page => page);

return (
<React.Fragment>
<Head>
<title>HyperDX</title>
<link rel="icon" type="image/png" sizes="32x32" href="/Icon32.png" />
<meta
name="viewport"
content="width=device-width, initial-scale=0.75"
/>
<meta name="theme-color" content="#25292e"></meta>
<meta name="google" content="notranslate" />
<ColorSchemeScript
forceColorScheme={userPreferences.theme === 'dark' ? 'dark' : 'light'}
/>
</Head>

<HDXQueryParamProvider>
<QueryParamProvider adapter={NextAdapter}>
<QueryClientProvider client={queryClient}>
<ThemeWrapper
fontFamily={selectedMantineFont}
colorScheme={userPreferences.theme === 'dark' ? 'dark' : 'light'}
>
{getLayout(<Component {...pageProps} />)}
{confirmModal}
</ThemeWrapper>
<ReactQueryDevtools initialIsOpen={true} />
{background}
</QueryClientProvider>
</QueryParamProvider>
</HDXQueryParamProvider>
<AppThemeProvider>
<AppHeadContent />
<DynamicFavicon />
<HDXQueryParamProvider>
<QueryParamProvider adapter={NextAdapter}>
<QueryClientProvider client={queryClient}>
<AppContent
Component={Component}
pageProps={pageProps}
confirmModal={confirmModal}
/>
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
</QueryParamProvider>
</HDXQueryParamProvider>
</AppThemeProvider>
</React.Fragment>
);
}
Binary file removed packages/app/public/Icon32.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
15 changes: 15 additions & 0 deletions packages/app/public/favicons/clickstack/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions packages/app/public/favicons/hyperdx/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion packages/app/src/DBServiceMapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ function DBServiceMapPage() {
<div style={{ minWidth: '200px' }}>
<Slider
label={null}
color="green"
min={0}
max={SAMPLING_FACTORS.length - 1}
value={SAMPLING_FACTORS.findIndex(
Expand Down
5 changes: 3 additions & 2 deletions packages/app/src/LandingHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Link from 'next/link';
import { Anchor, Burger, Button, Container, Group } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

import { useWordmark } from './theme/ThemeProvider';
import api from './api';
import Logo from './Logo';

export default function LandingHeader({
activeKey,
Expand All @@ -12,6 +12,7 @@ export default function LandingHeader({
activeKey: string;
fixed?: boolean;
}) {
const Wordmark = useWordmark();
const { data: me } = api.useMe();
const isLoggedIn = Boolean(me);

Expand All @@ -35,7 +36,7 @@ export default function LandingHeader({
<Container fluid px="xl" py="md">
<Group justify="space-between" align="center">
<Link href="/" style={{ textDecoration: 'none' }}>
<Logo />
<Wordmark />
</Link>

<Burger
Expand Down
55 changes: 0 additions & 55 deletions packages/app/src/Logo.tsx

This file was deleted.

3 changes: 2 additions & 1 deletion packages/app/src/OnboardingChecklist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import {
} from '@tabler/icons-react';

import { useQueriedChartConfig } from './hooks/useChartConfig';
import { useLogomark } from './theme/ThemeProvider';
import api from './api';
import { useConnections } from './connection';
import Icon from './Icon';
import { useSources } from './source';
import { useLocalStorage } from './utils';

Expand All @@ -41,6 +41,7 @@ const OnboardingChecklist = ({
}: {
onAddDataClick?: () => void;
}) => {
const Logomark = useLogomark();
const [isCollapsed, setIsCollapsed] = useLocalStorage(
'onboardingChecklistCollapsed',
false,
Expand Down
7 changes: 4 additions & 3 deletions packages/app/src/Spotlights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import {
IconSettings,
} from '@tabler/icons-react';

import { useLogomark } from './theme/ThemeProvider';
import api from './api';
import Logo from './Icon';
import { useSavedSearches } from './savedSearch';

import '@mantine/spotlight/styles.css';

export const useSpotlightActions = () => {
const router = useRouter();
const Logomark = useLogomark();

const { data: logViewsData } = useSavedSearches();
const { data: dashboardsData } = api.useDashboards();
Expand Down Expand Up @@ -150,7 +151,7 @@ export const useSpotlightActions = () => {
{
id: 'cloud',
group: 'Menu',
leftSection: <Logo />,
leftSection: <Logomark size={16} />,
label: 'HyperDX Cloud',
description: 'Ready to use HyperDX Cloud? Get started for free.',
keywords: ['account', 'profile'],
Expand All @@ -161,7 +162,7 @@ export const useSpotlightActions = () => {
);

return logViewActions;
}, [logViewsData, dashboardsData, router]);
}, [Logomark, logViewsData, dashboardsData, router]);

return { actions };
};
Expand Down
Loading
Loading