Skip to content

Commit 42820f3

Browse files
fix: Fix HyperDX primary button styling and SSR hydration (#1706)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6241c38 commit 42820f3

File tree

4 files changed

+38
-12
lines changed

4 files changed

+38
-12
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@hyperdx/app': patch
3+
---
4+
5+
fix: Apply theme CSS class during SSR to prevent button styling mismatch
6+
7+
Adds the theme class (e.g., `theme-hyperdx`) to the HTML element during server-side rendering in `_document.tsx`. This ensures CSS variables for button styling are correctly applied from the first render, preventing a hydration mismatch that caused primary buttons to display with Mantine's default styling instead of the custom theme styling when `NEXT_PUBLIC_THEME` was explicitly set.

packages/app/pages/_document.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import { Head, Html, Main, NextScript } from 'next/document';
22

33
import { ibmPlexMono, inter, roboto, robotoMono } from '@/fonts';
44

5+
// Get theme class for SSR - must match ThemeProvider's resolution
6+
// This ensures CSS variables are applied during server-side rendering
7+
// to prevent hydration mismatch with button styling
8+
function getThemeClass(): string {
9+
const envTheme = process.env.NEXT_PUBLIC_THEME;
10+
// Default to hyperdx if not set or invalid
11+
const themeName =
12+
envTheme === 'hyperdx' || envTheme === 'clickstack' ? envTheme : 'hyperdx';
13+
return `theme-${themeName}`;
14+
}
15+
516
export default function Document() {
617
const fontClasses = [
718
ibmPlexMono.variable,
@@ -10,8 +21,10 @@ export default function Document() {
1021
roboto.variable,
1122
].join(' ');
1223

24+
const themeClass = getThemeClass();
25+
1326
return (
14-
<Html lang="en" className={fontClasses}>
27+
<Html lang="en" className={`${fontClasses} ${themeClass}`}>
1528
<Head>
1629
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
1730
<script src="/__ENV.js" />

packages/app/src/theme/themes/hyperdx/_tokens.scss

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
--color-bg-danger: var(--mantine-color-red-4);
3131
--color-bg-warning: var(--mantine-color-orange-5);
3232

33-
/* Primary Button */
34-
--color-primary-button-bg: var(--palette-brand-300);
35-
--color-primary-button-bg-hover: var(--color-brand-200);
36-
--color-primary-button-text: var(--color-text-inverted);
33+
/* Primary Button - uses brand green colors */
34+
--color-primary-button-bg: var(--mantine-color-green-light);
35+
--color-primary-button-bg-hover: var(--mantine-color-green-light-hover);
36+
--color-primary-button-text: var(--mantine-color-green-light-color);
3737

3838
/* Borders & Dividers */
3939
--color-border: var(--mantine-color-dark-5);
@@ -136,6 +136,11 @@
136136
--color-bg-danger: var(--mantine-color-red-8);
137137
--color-bg-warning: var(--mantine-color-orange-8);
138138

139+
/* Primary Button - uses brand green colors */
140+
--color-primary-button-bg: var(--mantine-color-green-light);
141+
--color-primary-button-bg-hover: var(--mantine-color-green-light-hover);
142+
--color-primary-button-text: var(--mantine-color-green-light-color);
143+
139144
/* Borders & Dividers - inverted */
140145
--color-border: var(--mantine-color-gray-1);
141146
--color-border-muted: rgb(0 0 0 / 5%);

packages/app/src/theme/themes/hyperdx/mantineTheme.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,12 @@ export const makeTheme = ({
243243
baseVars['--button-fz'] = rem(12);
244244
}
245245

246-
// Use Mantine's built-in CSS vars for hover support
246+
// Use semantic CSS vars for primary button styling
247247
if (props.variant === 'primary') {
248-
baseVars['--button-bg'] = 'var(--mantine-color-green-light)';
249-
baseVars['--button-hover'] = 'var(--mantine-color-green-light-hover)';
250-
baseVars['--button-color'] = 'var(--mantine-color-green-light-color)';
248+
baseVars['--button-bg'] = 'var(--color-primary-button-bg)';
249+
baseVars['--button-hover'] = 'var(--color-primary-button-bg-hover)';
250+
baseVars['--button-color'] = 'var(--color-primary-button-text)';
251+
baseVars['--button-color-hover'] = 'var(--color-primary-button-text)';
251252
}
252253

253254
if (props.variant === 'secondary') {
@@ -305,9 +306,9 @@ export const makeTheme = ({
305306
}
306307

307308
if (props.variant === 'primary') {
308-
baseVars['--ai-bg'] = 'var(--mantine-color-green-light)';
309-
baseVars['--ai-hover'] = 'var(--mantine-color-green-light-hover)';
310-
baseVars['--ai-color'] = 'var(--mantine-color-green-light-color)';
309+
baseVars['--ai-bg'] = 'var(--color-primary-button-bg)';
310+
baseVars['--ai-hover'] = 'var(--color-primary-button-bg-hover)';
311+
baseVars['--ai-color'] = 'var(--color-primary-button-text)';
311312
}
312313

313314
if (props.variant === 'secondary') {

0 commit comments

Comments
 (0)