From b7ba25479cb5518c2cf406baddf8894a772b850e Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Tue, 10 Mar 2026 11:37:32 +0100 Subject: [PATCH 1/9] fix: use CSS-based logo theme switching to prevent white flash on light-mode refresh Replace JS-driven useTheme logo swap with dual-span CSS approach. Render both logos and toggle visibility via the .dark class next-themes sets on before first paint, eliminating the SSR hydration mismatch that caused the white-on-white flash. Co-Authored-By: Claude Sonnet 4.6 --- assets/svg/discord-icon.svg | 2 +- assets/svg/discourse-icon.svg | 2 +- assets/svg/feedback-bad.svg | 8 +- assets/svg/feedback-good.svg | 4 +- assets/svg/github-icon.svg | 16 ++-- assets/svg/x-icon.svg | 2 +- assets/svg/youtube-icon.svg | 2 +- components/ApiReference/ApiReference.tsx | 8 +- components/ApiReference/Path.tsx | 3 +- .../ApiReference/SampleRequestHeader.tsx | 4 +- components/ApiReference/TOC.tsx | 8 +- components/Feedback/index.tsx | 26 +++-- components/Footer/Footer.module.css | 2 +- components/Footer/index.tsx | 15 ++- components/ResourceHub/Resources.tsx | 12 ++- components/SupportedNetworks/Networks.tsx | 12 ++- components/ThemeToggle/ThemeToggle.module.css | 69 +++++++++++++ components/ThemeToggle/index.tsx | 96 +++++++++++++++++++ pages/_app.tsx | 33 ++++++- styles/palette.ts | 70 ++++++++++++++ styles/styles.css | 18 +++- styles/theme.ts | 21 ++-- theme.config.tsx | 27 ++++-- 23 files changed, 380 insertions(+), 80 deletions(-) create mode 100644 components/ThemeToggle/ThemeToggle.module.css create mode 100644 components/ThemeToggle/index.tsx diff --git a/assets/svg/discord-icon.svg b/assets/svg/discord-icon.svg index d78504a81..cc9721596 100644 --- a/assets/svg/discord-icon.svg +++ b/assets/svg/discord-icon.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/svg/discourse-icon.svg b/assets/svg/discourse-icon.svg index d4c0484a2..d9b096107 100644 --- a/assets/svg/discourse-icon.svg +++ b/assets/svg/discourse-icon.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/svg/feedback-bad.svg b/assets/svg/feedback-bad.svg index 078c7ba35..5f68a03bf 100644 --- a/assets/svg/feedback-bad.svg +++ b/assets/svg/feedback-bad.svg @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/assets/svg/feedback-good.svg b/assets/svg/feedback-good.svg index b361046e1..9c4be703e 100644 --- a/assets/svg/feedback-good.svg +++ b/assets/svg/feedback-good.svg @@ -1,7 +1,7 @@ - - + + diff --git a/assets/svg/github-icon.svg b/assets/svg/github-icon.svg index b01d2ca4e..603629ce7 100644 --- a/assets/svg/github-icon.svg +++ b/assets/svg/github-icon.svg @@ -1,10 +1,10 @@ - - - - - - - - + + + + + + + + diff --git a/assets/svg/x-icon.svg b/assets/svg/x-icon.svg index b5700350c..9d2f6d794 100644 --- a/assets/svg/x-icon.svg +++ b/assets/svg/x-icon.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/svg/youtube-icon.svg b/assets/svg/youtube-icon.svg index 3a1dc4bf2..83a110fe4 100644 --- a/assets/svg/youtube-icon.svg +++ b/assets/svg/youtube-icon.svg @@ -1,3 +1,3 @@ - + diff --git a/components/ApiReference/ApiReference.tsx b/components/ApiReference/ApiReference.tsx index fb9da41f7..170bdc3e1 100644 --- a/components/ApiReference/ApiReference.tsx +++ b/components/ApiReference/ApiReference.tsx @@ -68,12 +68,12 @@ const ApiReference: React.FC<{ networkName: string }> = ({ networkName }) => { bottom: 60, right: 17, zIndex: 1000, - backgroundColor: 'background.dark', + backgroundColor: 'background.paper', border: 'none', color: 'text.primary', display: ['block', 'none'], '&:hover': { - backgroundColor: 'background.dark' + backgroundColor: 'background.paper' } }} onClick={() => { @@ -99,10 +99,10 @@ const ApiReference: React.FC<{ networkName: string }> = ({ networkName }) => { opacity: currentIndex !== '' ? 1 : 0, transition: 'opacity 0.3s', border: 'none', - backgroundColor: 'background.dark', + backgroundColor: 'background.paper', color: 'text.primary', '&:hover': { - backgroundColor: 'background.dark' + backgroundColor: 'background.paper' } }} onClick={() => { diff --git a/components/ApiReference/Path.tsx b/components/ApiReference/Path.tsx index a238b48d3..f0e7fe106 100644 --- a/components/ApiReference/Path.tsx +++ b/components/ApiReference/Path.tsx @@ -12,7 +12,8 @@ const Path: React.FC<{ path: string; method: string }> = ({ path, method }) => { `1px solid ${palette.grey[800]}`, + border: '1px solid', + borderColor: 'border.light', borderRadius: '8px', mb: 2, fontSize: '14px', diff --git a/components/ApiReference/SampleRequestHeader.tsx b/components/ApiReference/SampleRequestHeader.tsx index 8fa5c3478..681ef30e9 100644 --- a/components/ApiReference/SampleRequestHeader.tsx +++ b/components/ApiReference/SampleRequestHeader.tsx @@ -24,8 +24,8 @@ const SampleRequestHeader: React.FC<{ sx={{ mt: 1, height: '36px', - color: 'rgba(249,250,251,.7)', - backgroundColor: ({ palette }) => palette.grey[900] + color: 'text.primary', + backgroundColor: 'background.paper' }} > Try it on Swagger diff --git a/components/ApiReference/TOC.tsx b/components/ApiReference/TOC.tsx index 0e80eff9e..d96c77c0b 100644 --- a/components/ApiReference/TOC.tsx +++ b/components/ApiReference/TOC.tsx @@ -58,7 +58,7 @@ const TableOfContents: React.FC<{ backgroundColor: 'transparent', '&:hover': { color: 'text.primary', - backgroundColor: 'rgba(224, 255, 240, 0.05)' + backgroundColor: 'rgba(128, 128, 128, 0.08)' }, transition: '0.2s', display: ['none', 'block'] @@ -176,13 +176,11 @@ const TocMenuItem: React.FC<{ width: '100%', color: currentIndex === child.link ? 'primary.main' : 'grey.500', backgroundColor: - currentIndex === child.link - ? theme.palette.background.light - : 'transparent', + currentIndex === child.link ? 'background.light' : 'transparent', '&:hover': { color: currentIndex === child.link ? 'primary.main' : 'text.primary', - backgroundColor: 'rgba(224, 255, 240, 0.05)' + backgroundColor: 'rgba(128, 128, 128, 0.08)' }, transition: '0.2s' }} diff --git a/components/Feedback/index.tsx b/components/Feedback/index.tsx index 676e62696..ece2170ab 100644 --- a/components/Feedback/index.tsx +++ b/components/Feedback/index.tsx @@ -38,10 +38,8 @@ const ReportIssue: React.FC<{ }} size={small ? 'small' : undefined} sx={{ - color: 'rgba(249,250,251,.7)', - backgroundColor: small - ? ({ palette }) => palette.grey[900] - : 'transparent' + color: 'text.primary', + backgroundColor: small ? 'background.paper' : 'transparent' }} > Report issue @@ -115,7 +113,7 @@ const Feedback: React.FC<{ p: small ? 1 : 3, mt: 3, borderRadius: '8px', - border: label != null ? 'none' : '1px solid rgba(249,250,251,.1)' + border: label != null ? 'none' : '1px solid rgba(128,128,128,.15)' }} > {submitted ? ( @@ -126,7 +124,7 @@ const Feedback: React.FC<{ alignItems='center' > - + Thank you for your feedback! @@ -144,7 +142,7 @@ const Feedback: React.FC<{ multiline rows={4} sx={{ - backgroundColor: 'rgba(249,250,251,.1)', + backgroundColor: 'rgba(128,128,128,.1)', p: 1 }} onChange={e => { @@ -162,7 +160,7 @@ const Feedback: React.FC<{ multiline rows={4} sx={{ - backgroundColor: 'rgba(249,250,251,.1)', + backgroundColor: 'rgba(128,128,128,.1)', p: 1 }} onChange={e => { @@ -259,8 +257,8 @@ const Feedback: React.FC<{ + ) +} + +export default ThemeToggle diff --git a/pages/_app.tsx b/pages/_app.tsx index 70868bc70..61ed06550 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,14 +1,14 @@ import type { EmotionCache } from '@emotion/react' import { CacheProvider } from '@emotion/react' import { CssBaseline } from '@mui/material' -import type { CssVarsThemeOptions } from '@mui/material/styles' import { Experimental_CssVarsProvider as CssVarsProvider, - experimental_extendTheme as extendMuiTheme + useColorScheme } from '@mui/material/styles' import type { AppProps } from 'next/app' import Head from 'next/head' import { GoogleAnalytics } from '@next/third-parties/google' +import { useEffect } from 'react' import MetaTags from '../components/MetaTags' import { CookieBanner } from '../components/CookieBanner' @@ -21,8 +21,30 @@ import { theme } from '../styles/theme' // Client-side cache, shared for the whole session of the user in the browser. const clientSideEmotionCache = createEmotionCache() -// Extended theme for CssVarsProvider -const cssVarsTheme = extendMuiTheme(theme as CssVarsThemeOptions) +// Syncs Nextra's next-themes html.dark class → MUI CssVarsProvider mode +const ThemeSyncer: React.FC = () => { + const { setMode } = useColorScheme() + + useEffect(() => { + const sync = (): void => { + setMode(document.documentElement.classList.contains('dark') ? 'dark' : 'light') + } + + sync() + + const observer = new MutationObserver(sync) + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }) + + return () => { + observer.disconnect() + } + }, [setMode]) + + return null +} const App = ({ Component, @@ -37,7 +59,8 @@ const App = ({ - + + {process.env.NEXT_PUBLIC_IS_PRODUCTION === 'true' && ( diff --git a/styles/palette.ts b/styles/palette.ts index 2fbfa9b9f..b86325e10 100644 --- a/styles/palette.ts +++ b/styles/palette.ts @@ -67,3 +67,73 @@ export const palette = { main: '#121312' } } + +export const lightPalette = { + text: { + primary: '#121312', + secondary: '#636669', + disabled: '#636669', + dark: 'rgba(80, 82, 86, 1)' + }, + primary: { + dark: '#0cb259', + main: '#0cb259', + light: '#636669' + }, + secondary: { + dark: '#303033', + main: '#121312', + light: '#0cb259', + background: '#E0E0E0' + }, + border: { + main: '#C4C4C4', + light: '#E0E0E0', + background: '#FFFFFF' + }, + error: { + darker: 'rgb(200, 120, 130)', + dark: '#AC2C3B', + main: '#FF5F72', + light: '#FFB4BD', + background: '#FDECEA' + }, + success: { + darker: 'rgb(180, 220, 200)', + dark: '#028D4C', + main: '#00B460', + light: '#81C784', + background: '#EDF7F1' + }, + info: { + darker: 'rgb(180, 220, 240)', + dark: '#52BFDC', + main: '#5FDDFF', + light: '#B7F0FF', + background: '#EAF7FB' + }, + warning: { + darker: '#F5EAE0', + dark: '#CD674E', + main: '#FF8061', + light: '#FFB7A6', + background: '#FFF3EF' + }, + background: { + default: '#FFFFFF', + dark: '#E0E0E0', + main: '#FFFFFF', + paper: '#F4F4F4', + light: '#E8F5EE' + }, + backdrop: { + main: '#636669' + }, + logo: { + main: '#121312', + background: '#F4F4F4' + }, + static: { + main: '#FFFFFF' + } +} diff --git a/styles/styles.css b/styles/styles.css index 2d84230b0..0ab2a0103 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -2,8 +2,17 @@ html { scroll-padding-top: 110px !important; } -html.dark.dark body { +/* Logo theme switching — driven by the .dark class next-themes sets on + This avoids the SSR hydration mismatch that caused the white-on-light flash */ +.logo-dark { display: none; } +.dark .logo-light { display: none; } +.dark .logo-dark { display: block; } + +body { font-family: 'DM Sans', sans-serif; +} + +html.dark body { background-color: #121312; } @@ -36,7 +45,7 @@ h2 { top: 2rem; } -#__next > div > footer { +html.dark #__next > div > footer { background-color: #121312; } @@ -74,10 +83,13 @@ h2 { /* CopyButton */ button[title='Copy code']:not(#\9) { margin-top: 2px; - background-color: #212121; border: none; } +html.dark button[title='Copy code']:not(#\9) { + background-color: #212121; +} + /* CodeBlocks */ .ch-codeblock.not-prose { box-shadow: none; diff --git a/styles/theme.ts b/styles/theme.ts index 6a5375843..4fa97dc2e 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -1,7 +1,7 @@ -import { createTheme } from '@mui/material/styles' +import { experimental_extendTheme as extendTheme } from '@mui/material/styles' import type { Shadows } from '@mui/material/styles' -import { palette } from './palette' +import { palette, lightPalette } from './palette' declare module '@mui/material/styles' { // Custom color palettes @@ -21,6 +21,7 @@ declare module '@mui/material/styles' { interface TypeBackground { main: string light: string + dark: string } // Custom color properties @@ -32,13 +33,11 @@ declare module '@mui/material/styles' { } } -// declare module '@mui/material/Button' { -// interface ButtonPropsColorOverrides { -// background: true -// } -// } - -export const theme = createTheme({ +export const theme = extendTheme({ + colorSchemes: { + dark: { palette }, + light: { palette: lightPalette } + }, breakpoints: { values: { xs: 0, @@ -48,10 +47,6 @@ export const theme = createTheme({ xl: 1630 } }, - palette: { - mode: 'dark', - ...palette - }, spacing: 8, shape: { borderRadius: 6 diff --git a/theme.config.tsx b/theme.config.tsx index 7d6452fa5..d12076594 100644 --- a/theme.config.tsx +++ b/theme.config.tsx @@ -1,14 +1,26 @@ import { useRouter } from 'next/router' // import localFont from 'next/font/local' import type { DocsThemeConfig } from 'nextra-theme-docs' - import type { PropsWithChildren } from 'react' -import SafeLogo from './assets/svg/safe-logo-white.svg' +import SafeLogoWhite from './assets/svg/safe-logo-white.svg' +import SafeLogoBlack from './assets/svg/safe-logo.svg' import Feedback from './components/Feedback' import Footer from './components/Footer' +import ThemeToggle from './components/ThemeToggle' // const citerne = localFont({ src: './public/fonts/Citerne-Light.woff' }) +const Logo: React.FC = () => ( + <> + + + + + + + +) + const Main: React.FC = ({ children }) => { const { asPath } = useRouter() return ( @@ -22,11 +34,14 @@ const Main: React.FC = ({ children }) => { const config: DocsThemeConfig = { darkMode: false, - nextThemes: { - forcedTheme: 'dark' - }, + logoLink: false, primaryHue: 150, - logo: , + logo: ( +
+ + +
+ ), project: { link: 'https://github.com/safe-global' }, From 3059a9cf37d480a97e2c087d7a99d8ba65bc38cb Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Tue, 10 Mar 2026 12:18:31 +0100 Subject: [PATCH 2/9] fix: resolve pre-push lint and prettier errors Co-Authored-By: Claude Sonnet 4.6 --- components/ApiReference/TOC.tsx | 5 +++-- components/Feedback/index.tsx | 14 ++++++++++++-- pages/_app.tsx | 4 +++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/components/ApiReference/TOC.tsx b/components/ApiReference/TOC.tsx index d96c77c0b..318b15693 100644 --- a/components/ApiReference/TOC.tsx +++ b/components/ApiReference/TOC.tsx @@ -7,7 +7,6 @@ import Link from '@mui/material/Link' import { useState } from 'react' import { useCurrentTocIndex } from '../../lib/mdx' -import { theme } from '../../styles/theme' import Method from './Method' export interface Heading { @@ -176,7 +175,9 @@ const TocMenuItem: React.FC<{ width: '100%', color: currentIndex === child.link ? 'primary.main' : 'grey.500', backgroundColor: - currentIndex === child.link ? 'background.light' : 'transparent', + currentIndex === child.link + ? 'background.light' + : 'transparent', '&:hover': { color: currentIndex === child.link ? 'primary.main' : 'text.primary', diff --git a/components/Feedback/index.tsx b/components/Feedback/index.tsx index ece2170ab..e75d9bed2 100644 --- a/components/Feedback/index.tsx +++ b/components/Feedback/index.tsx @@ -124,7 +124,13 @@ const Feedback: React.FC<{ alignItems='center' > - + Thank you for your feedback! @@ -305,7 +311,11 @@ const Feedback: React.FC<{ ) : ( <> - + Was this page helpful? diff --git a/pages/_app.tsx b/pages/_app.tsx index 61ed06550..25c0c3b17 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -27,7 +27,9 @@ const ThemeSyncer: React.FC = () => { useEffect(() => { const sync = (): void => { - setMode(document.documentElement.classList.contains('dark') ? 'dark' : 'light') + setMode( + document.documentElement.classList.contains('dark') ? 'dark' : 'light' + ) } sync() From 7cd0c9516846cc2dda239738df783f341300e5c2 Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Tue, 10 Mar 2026 12:22:32 +0100 Subject: [PATCH 3/9] fix: add next-themes as direct dependency for TypeScript resolution Co-Authored-By: Claude Sonnet 4.6 --- package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/package.json b/package.json index b95ed9752..164a8e4ff 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "fuse.js": "^7.0.0", "lodash": "^4.17.21", "next": "^14.2.35", + "next-themes": "0.2.1", "nextra": "2.13.4", "nextra-theme-docs": "2.13.4", "node-fetch": "^3.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e65a647f..2070f667e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: next: specifier: ^14.2.35 version: 14.2.35(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes: + specifier: 0.2.1 + version: 0.2.1(next@14.2.35(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nextra: specifier: 2.13.4 version: 2.13.4(next@14.2.35(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) From abaf9d885798f857c5b824c7bbf8ec2774be809d Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Tue, 10 Mar 2026 12:31:46 +0100 Subject: [PATCH 4/9] fix: resolve ThemeToggle race condition and footer logo FOUC ThemeToggle: track the pending setTimeout in a ref and clearTimeout on each new click, preventing rapid clicks from desynchronising the visual thumb position from the actual next-themes theme value. Footer: replace useColorScheme JS branch in FooterLogo with the same CSS class approach (logo-light / logo-dark) used by the navbar Logo, eliminating the SSR hydration mismatch that showed a white logo on a light-mode page before mount. Co-Authored-By: Claude Sonnet 4.6 --- components/Footer/index.tsx | 19 ++++++++++--------- components/ThemeToggle/index.tsx | 9 +++++++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/components/Footer/index.tsx b/components/Footer/index.tsx index 48823abd8..000404e80 100644 --- a/components/Footer/index.tsx +++ b/components/Footer/index.tsx @@ -1,5 +1,4 @@ import { Badge, ButtonBase, Divider, Grid, Typography } from '@mui/material' -import { useColorScheme } from '@mui/material/styles' import Link from 'next/link' import type { ComponentType, SyntheticEvent } from 'react' import DiscordIcon from '../../assets/svg/discord-icon.svg' @@ -330,14 +329,16 @@ const createFooterButton = ( ) } -const FooterLogo: React.FC = () => { - const { mode } = useColorScheme() - return mode === 'light' ? ( - - ) : ( - - ) -} +const FooterLogo: React.FC = () => ( + <> + + + + + + + +) const Footer: React.FC = () => { const openPositions = useOpenPositions() diff --git a/components/ThemeToggle/index.tsx b/components/ThemeToggle/index.tsx index 975557aa8..4253f1c99 100644 --- a/components/ThemeToggle/index.tsx +++ b/components/ThemeToggle/index.tsx @@ -1,5 +1,5 @@ import { useTheme } from 'next-themes' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import css from './ThemeToggle.module.css' const SunIcon = (): JSX.Element => ( @@ -51,6 +51,7 @@ const ThemeToggle: React.FC = () => { // Separate visual state so the thumb animates immediately on click, // before next-themes fires its transition-suppression script. const [visualDark, setVisualDark] = useState(false) + const pendingTimer = useRef | null>(null) useEffect(() => { setMounted(true) @@ -66,9 +67,13 @@ const ThemeToggle: React.FC = () => { const next = !visualDark // Update visual position immediately → CSS transition fires right away setVisualDark(next) + // Cancel any in-flight timer from a previous click before scheduling a new + // one — prevents rapid clicks from desynchronising visual and actual theme. + if (pendingTimer.current !== null) clearTimeout(pendingTimer.current) // Delay the actual theme switch until after the animation completes, // so next-themes' "* { transition: none }" doesn't cancel our animation - setTimeout(() => { + pendingTimer.current = setTimeout(() => { + pendingTimer.current = null setTheme(next ? 'dark' : 'light') }, TRANSITION_MS) } From 85a441eb8ea2ff856bb03671a1a8f519e825d503 Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Tue, 10 Mar 2026 12:33:26 +0100 Subject: [PATCH 5/9] feat: move theme toggle to right side of navbar Use navbar.extraContent instead of the logo slot so the ThemeToggle renders on the far-right of the navbar alongside the project icon. Co-Authored-By: Claude Sonnet 4.6 --- theme.config.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/theme.config.tsx b/theme.config.tsx index d12076594..cf99bdd3a 100644 --- a/theme.config.tsx +++ b/theme.config.tsx @@ -36,12 +36,10 @@ const config: DocsThemeConfig = { darkMode: false, logoLink: false, primaryHue: 150, - logo: ( -
- - -
- ), + logo: , + navbar: { + extraContent: + }, project: { link: 'https://github.com/safe-global' }, From 0844f4e0fb0bb6ad638a9ec1b3101815c3867f23 Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Wed, 11 Mar 2026 11:33:48 +0100 Subject: [PATCH 6/9] fix: replace hardcoded palette refs with MUI CSS variables in extendTheme Shadows, caption color, and MuiOutlinedInput color were baking in static dark-palette hex values at theme-creation time. Switching to CSS variable references ensures they resolve against the active color scheme at runtime. Co-Authored-By: Claude Sonnet 4.6 --- styles/theme.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/styles/theme.ts b/styles/theme.ts index 4fa97dc2e..a0a92fe97 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -53,7 +53,7 @@ export const theme = extendTheme({ }, shadows: [ 'none', - `0 0 2px ${palette.primary.light}`, + '0 0 2px var(--mui-palette-primary-light)', ...Array(23).fill('none') ] as Shadows, typography: { @@ -118,7 +118,7 @@ export const theme = extendTheme({ letterSpacing: '0.1em', fontWeight: 500, textTransform: 'uppercase', - color: palette.primary.light + color: 'var(--mui-palette-primary-light)' }, overline: { fontSize: '11px', @@ -154,7 +154,7 @@ export const theme = extendTheme({ borderColor: 'inherit' }, root: { - color: palette.border.main + color: 'var(--mui-palette-border-main)' } } }, From bf995e050fa317d4765043712efcbf27442bfb3a Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Thu, 12 Mar 2026 14:23:53 +0100 Subject: [PATCH 7/9] fix: replace direct palette imports with theme.vars across components Hr, Resources, Networks, and NetworkModal were importing palette directly and baking in static dark-palette values. Switching to theme.vars.palette.* makes all these references resolve via MUI CSS variables at runtime, so they correctly adapt when the color scheme changes. Also adds TypeText.dark augmentation to theme.ts so text.dark is a typed, CSS-variable-backed palette token. Co-Authored-By: Claude Sonnet 4.6 --- components/Hr.tsx | 4 ++-- components/ResourceHub/Resources.tsx | 4 ++-- components/SupportedNetworks/NetworkModal.tsx | 3 ++- components/SupportedNetworks/Networks.tsx | 3 +-- styles/theme.ts | 22 +++++++++++++------ 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/components/Hr.tsx b/components/Hr.tsx index f76a6662f..0a0a04e2b 100644 --- a/components/Hr.tsx +++ b/components/Hr.tsx @@ -1,11 +1,11 @@ -import { palette } from '../styles/palette' +import { theme } from '../styles/theme' // eslint-disable-next-line @typescript-eslint/no-explicit-any const Hr: React.FC<{ style?: any }> = props => (
{ endIcon={} sx={{ alignItems: 'space-between', - border: ['none', `solid 1px ${palette.text.dark}`], + border: ['none', `solid 1px ${theme.vars.palette.text.dark}`], color: 'white', mb: 2 }} diff --git a/components/SupportedNetworks/NetworkModal.tsx b/components/SupportedNetworks/NetworkModal.tsx index 69c2d7015..db5bdf96d 100644 --- a/components/SupportedNetworks/NetworkModal.tsx +++ b/components/SupportedNetworks/NetworkModal.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { theme } from '../../styles/theme' import Img from 'next/image' import { Typography, @@ -81,7 +82,7 @@ const NetworkModal: React.FC<{ { endIcon={} sx={{ alignItems: 'space-between', - border: ['none', `solid 1px ${palette.text.dark}`], + border: ['none', `solid 1px ${theme.vars.palette.text.dark}`], color: 'white', mb: 2 }} diff --git a/styles/theme.ts b/styles/theme.ts index a0a92fe97..26bb14e68 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -24,6 +24,10 @@ declare module '@mui/material/styles' { dark: string } + interface TypeText { + dark: string + } + // Custom color properties interface PaletteColor { background?: string @@ -33,11 +37,15 @@ declare module '@mui/material/styles' { } } +const colorSchemes = { + dark: { palette }, + light: { palette: lightPalette } +} + +const { vars } = extendTheme({ colorSchemes }) + export const theme = extendTheme({ - colorSchemes: { - dark: { palette }, - light: { palette: lightPalette } - }, + colorSchemes, breakpoints: { values: { xs: 0, @@ -53,7 +61,7 @@ export const theme = extendTheme({ }, shadows: [ 'none', - '0 0 2px var(--mui-palette-primary-light)', + `0 0 2px ${vars.palette.primary.light}`, ...Array(23).fill('none') ] as Shadows, typography: { @@ -118,7 +126,7 @@ export const theme = extendTheme({ letterSpacing: '0.1em', fontWeight: 500, textTransform: 'uppercase', - color: 'var(--mui-palette-primary-light)' + color: vars.palette.primary.light }, overline: { fontSize: '11px', @@ -154,7 +162,7 @@ export const theme = extendTheme({ borderColor: 'inherit' }, root: { - color: 'var(--mui-palette-border-main)' + color: vars.palette.border.main } } }, From f948e06605dc5105c51e14f9d3cd75a80097c6ec Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Thu, 12 Mar 2026 18:53:02 +0100 Subject: [PATCH 8/9] fix: resolve CustomCard arrow color and hover border in light mode Arrow icon had fill="white" hardcoded in the SVG, making it invisible in light mode. Switching to fill="currentColor" with color inherited from --mui-palette-text-primary makes it white in dark and near-black in light mode. The hover border showed a grey inner ring in light mode because the inset box-shadow (border.light = #E0E0E0) was not updated on hover, only the outer CSS border changed color. Replacing the no-op outline properties in .card:hover with a box-shadow override ensures both edges turn the brand green consistently in both color schemes. Co-Authored-By: Claude Sonnet 4.6 --- assets/svg/arrow-outward.svg | 2 +- components/CustomCard/styles.module.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/svg/arrow-outward.svg b/assets/svg/arrow-outward.svg index eac3efb32..69e4408f8 100644 --- a/assets/svg/arrow-outward.svg +++ b/assets/svg/arrow-outward.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/components/CustomCard/styles.module.css b/components/CustomCard/styles.module.css index cfbfa8d2a..27bf4c587 100644 --- a/components/CustomCard/styles.module.css +++ b/components/CustomCard/styles.module.css @@ -10,8 +10,7 @@ } .card:hover { - outline-color: var(--mui-palette-primary-main); - outline-width: 2px; + box-shadow: inset 0 0 0 1px var(--mui-palette-primary-main); } .outline:hover { @@ -32,6 +31,7 @@ top: 32px; right: 32px; transition: transform var(--transition-duration); + color: var(--mui-palette-text-primary); } .hide { From f81ff519e993c3a724c620094384fca005d97bd3 Mon Sep 17 00:00:00 2001 From: rahulrumalla Date: Thu, 12 Mar 2026 18:53:46 +0100 Subject: [PATCH 9/9] fix: use theme.vars.palette in ApiReference components to fix SSR crash Property and Network were accessing palette.grey and palette.error via direct imports or sx callbacks, which are undefined on the extendTheme object and crash during static prerendering. Switching to theme.vars gives type-safe CSS variable references that resolve correctly at runtime. Co-Authored-By: Claude Sonnet 4.6 --- components/ApiReference/Network.tsx | 3 ++- components/ApiReference/Property.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/ApiReference/Network.tsx b/components/ApiReference/Network.tsx index 59a92be8f..fa5b9d589 100644 --- a/components/ApiReference/Network.tsx +++ b/components/ApiReference/Network.tsx @@ -6,6 +6,7 @@ import { type PropsWithChildren, useContext } from 'react' +import { theme } from '../../styles/theme' import Link from 'next/link' import Select from '@mui/material/Select' import MenuItem from '@mui/material/MenuItem' @@ -60,7 +61,7 @@ const NetworkSwitcher: React.FC = () => { mr: 1, width: ['100%', '100%', 'auto'], color: 'white', - border: ({ palette }) => `1px solid ${palette.grey[700]}` + border: `1px solid ${theme.vars.palette.grey[700]}` }} inputProps={{ sx: { diff --git a/components/ApiReference/Property.tsx b/components/ApiReference/Property.tsx index 6349ffc9c..7e27d15f7 100644 --- a/components/ApiReference/Property.tsx +++ b/components/ApiReference/Property.tsx @@ -1,7 +1,7 @@ import { useState } from 'react' +import { theme } from '../../styles/theme' import Grid from '@mui/material/Grid' import Typography from '@mui/material/Typography' -import { theme } from '../../styles/theme' import AccordionDetails from '@mui/material/AccordionDetails' import Accordion from '@mui/material/Accordion' import { AccordionSummary } from '@mui/material' @@ -36,7 +36,7 @@ const Property: React.FC<{ property: any; required?: boolean }> = ({ sx={{ border: 'none', background: 'transparent', - color: theme.palette.grey[400], + color: theme.vars.palette.grey[400], boxShadow: 'none', '&.Mui-expanded': { border: 'none' @@ -54,7 +54,7 @@ const Property: React.FC<{ property: any; required?: boolean }> = ({ container pl={2} my={1} - sx={{ color: ({ palette }) => palette.grey[400] }} + sx={{ color: theme.vars.palette.grey[400] }} > = ({ {' - '}