From 2f15e336d157be8d65ffd84ba4a470ab311338ec Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:26:47 +0200 Subject: [PATCH] fix: use branding colors in charts --- frontend/src/context/BrandingContext.tsx | 4 ++-- frontend/src/context/branding-context.ts | 2 ++ frontend/src/hooks/useChartColors.ts | 24 ++++++++++++++++++++++++ frontend/src/pages/StatusPage.tsx | 17 +++++++---------- frontend/src/pages/TokenDetailPage.tsx | 11 ++++------- 5 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 frontend/src/hooks/useChartColors.ts diff --git a/frontend/src/context/BrandingContext.tsx b/frontend/src/context/BrandingContext.tsx index e677a72..209a2a9 100644 --- a/frontend/src/context/BrandingContext.tsx +++ b/frontend/src/context/BrandingContext.tsx @@ -17,7 +17,7 @@ function readCache(): BrandingConfig | null { function applyConfigState(cfg: BrandingConfig, setBranding: (v: typeof brandingDefaults) => void, setConfig: (v: BrandingConfig) => void) { setConfig(cfg); - setBranding({ chainName: cfg.chain_name, logoUrl: cfg.logo_url || null, loaded: true }); + setBranding({ chainName: cfg.chain_name, logoUrl: cfg.logo_url || null, accentHex: cfg.accent_color || null, loaded: true }); document.title = `${cfg.chain_name} - Block Explorer`; if (cfg.logo_url) { const link = document.querySelector("link[rel='icon']"); @@ -29,7 +29,7 @@ export function BrandingProvider({ children }: { children: ReactNode }) { const [branding, setBranding] = useState(() => { const cached = readCache(); return cached - ? { chainName: cached.chain_name, logoUrl: cached.logo_url || null, loaded: true } + ? { chainName: cached.chain_name, logoUrl: cached.logo_url || null, accentHex: cached.accent_color || null, loaded: true } : brandingDefaults; }); const [config, setConfig] = useState(readCache); diff --git a/frontend/src/context/branding-context.ts b/frontend/src/context/branding-context.ts index d772081..d35aa7d 100644 --- a/frontend/src/context/branding-context.ts +++ b/frontend/src/context/branding-context.ts @@ -3,12 +3,14 @@ import { createContext } from 'react'; export interface BrandingContextValue { chainName: string; logoUrl: string | null; + accentHex: string | null; loaded: boolean; } export const brandingDefaults: BrandingContextValue = { chainName: 'Unknown', logoUrl: null, + accentHex: null, loaded: false, }; diff --git a/frontend/src/hooks/useChartColors.ts b/frontend/src/hooks/useChartColors.ts new file mode 100644 index 0000000..d7fb2f6 --- /dev/null +++ b/frontend/src/hooks/useChartColors.ts @@ -0,0 +1,24 @@ +import { useContext, useMemo } from 'react'; +import { BrandingContext } from '../context/branding-context'; +import { ThemeContext } from '../context/theme-context'; + +const DEFAULT_ACCENT = '#dc2626'; + +function cssVar(name: string): string { + return `rgb(${getComputedStyle(document.documentElement).getPropertyValue(name).trim()})`; +} + +export function useChartColors() { + const { accentHex } = useContext(BrandingContext); + const themeCtx = useContext(ThemeContext); + const theme = themeCtx?.theme ?? 'dark'; + + return useMemo(() => ({ + accent: accentHex ?? DEFAULT_ACCENT, + grid: cssVar('--color-surface-600'), + axisText: cssVar('--color-text-subtle'), + tooltipBg: cssVar('--color-surface-800'), + tooltipText: cssVar('--color-text-primary'), + // eslint-disable-next-line react-hooks/exhaustive-deps + }), [accentHex, theme]); +} diff --git a/frontend/src/pages/StatusPage.tsx b/frontend/src/pages/StatusPage.tsx index 7b6009e..cae482e 100644 --- a/frontend/src/pages/StatusPage.tsx +++ b/frontend/src/pages/StatusPage.tsx @@ -17,12 +17,7 @@ import { formatNumber } from '../utils'; import Loading from '../components/Loading'; import { BlockStatsContext } from '../context/BlockStatsContext'; import { useChartData } from '../hooks/useChartData'; - -// ─── theme constants for recharts (matches CSS vars) ────────────────────────── -const CHART_ACCENT = '#dc2626'; -const CHART_GRID = '#22222e'; -const CHART_AXIS_TEXT = '#94a3b8'; -const CHART_TOOLTIP_BG = '#0c0c10'; +import { useChartColors } from '../hooks/useChartColors'; const WINDOWS: { label: string; value: ChartWindow }[] = [ { label: '1H', value: '1h' }, @@ -45,6 +40,8 @@ export default function StatusPage() { dailyTxsError, blocksChartError, gasPriceError, } = useChartData(window); + const { accent: CHART_ACCENT, grid: CHART_GRID, axisText: CHART_AXIS_TEXT, tooltipBg: CHART_TOOLTIP_BG, tooltipText: CHART_TOOLTIP_TEXT } = useChartColors(); + useEffect(() => { let mounted = true; const fetchStatus = async () => { @@ -137,7 +134,7 @@ export default function StatusPage() { [formatCompact(v as number), 'Transactions']} /> @@ -170,7 +167,7 @@ export default function StatusPage() { [formatCompact(v as number), 'Avg Gas Used']} labelFormatter={(v) => formatBucketTooltip(v, window)} /> @@ -206,7 +203,7 @@ export default function StatusPage() { [formatCompact(v as number), 'Transactions']} labelFormatter={(v) => formatBucketTooltip(v, window)} /> @@ -234,7 +231,7 @@ export default function StatusPage() { [formatGwei(v as number), 'Avg Gas Price']} labelFormatter={(v) => formatBucketTooltip(v, window)} /> diff --git a/frontend/src/pages/TokenDetailPage.tsx b/frontend/src/pages/TokenDetailPage.tsx index f8a4fc1..1fff76d 100644 --- a/frontend/src/pages/TokenDetailPage.tsx +++ b/frontend/src/pages/TokenDetailPage.tsx @@ -6,16 +6,12 @@ import { } from 'recharts'; import { useToken, useTokenHolders, useTokenTransfers } from '../hooks'; import { useTokenChart } from '../hooks/useTokenChart'; +import { useChartColors } from '../hooks/useChartColors'; import { Pagination, AddressLink, TxHashLink, CopyButton } from '../components'; import Loading from '../components/Loading'; import { formatNumber, formatTokenAmount, formatPercentage, formatTimeAgo, truncateHash } from '../utils'; import { type ChartWindow } from '../api/chartData'; -const CHART_ACCENT = '#dc2626'; -const CHART_GRID = '#22222e'; -const CHART_AXIS_TEXT = '#94a3b8'; -const CHART_TOOLTIP_BG = '#0c0c10'; - const WINDOWS: { label: string; value: ChartWindow }[] = [ { label: '1H', value: '1h' }, { label: '6H', value: '6h' }, @@ -101,6 +97,7 @@ export default function TokenDetailPage() { const { holders, pagination: holdersPagination } = useTokenHolders(address, { page: holdersPage, limit: 20 }); const { transfers, pagination: transfersPagination } = useTokenTransfers(address, { page: transfersPage, limit: 20 }); const { data: chartData, loading: chartLoading } = useTokenChart(address, chartWindow); + const { accent: CHART_ACCENT, grid: CHART_GRID, axisText: CHART_AXIS_TEXT, tooltipBg: CHART_TOOLTIP_BG, tooltipText: CHART_TOOLTIP_TEXT } = useChartColors(); const tabs: { id: TabType; label: string; count?: number }[] = [ { id: 'holders', label: 'Holders', count: holdersPagination?.total }, @@ -195,7 +192,7 @@ export default function TokenDetailPage() { [formatCompact(v as number), 'Transfers']} labelFormatter={(v) => formatBucketTooltip(v, chartWindow)} /> @@ -224,7 +221,7 @@ export default function TokenDetailPage() { [formatCompact(v as number), `Volume (${token?.symbol || 'tokens'})`]} labelFormatter={(v) => formatBucketTooltip(v, chartWindow)} />